diff options
111 files changed, 4142 insertions, 2467 deletions
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index e7a1f8b2ee..62d83b2e04 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -2344,10 +2344,10 @@ Error _Directory::change_dir(String p_dir) { ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); return d->change_dir(p_dir); } -String _Directory::get_current_dir(bool p_include_drive) { +String _Directory::get_current_dir() { ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use."); - return d->get_current_dir(p_include_drive); + return d->get_current_dir(); } Error _Directory::make_dir(String p_dir) { @@ -2444,7 +2444,7 @@ void _Directory::_bind_methods() { ClassDB::bind_method(D_METHOD("get_drive", "idx"), &_Directory::get_drive); ClassDB::bind_method(D_METHOD("get_current_drive"), &_Directory::get_current_drive); ClassDB::bind_method(D_METHOD("change_dir", "todir"), &_Directory::change_dir); - ClassDB::bind_method(D_METHOD("get_current_dir", "include_drive"), &_Directory::get_current_dir, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_current_dir"), &_Directory::get_current_dir); ClassDB::bind_method(D_METHOD("make_dir", "path"), &_Directory::make_dir); ClassDB::bind_method(D_METHOD("make_dir_recursive", "path"), &_Directory::make_dir_recursive); ClassDB::bind_method(D_METHOD("file_exists", "path"), &_Directory::file_exists); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index fc6419b7d8..3a5bd28ce8 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -572,7 +572,7 @@ public: int get_current_drive(); Error change_dir(String p_dir); // Can be relative or absolute, return false on success. - String get_current_dir(bool p_include_drive = true); // Return current dir location. + String get_current_dir(); // Return current dir location. Error make_dir(String p_dir); Error make_dir_recursive(String p_dir); diff --git a/core/callable.cpp b/core/callable.cpp index 4a5ae3a248..2bb9ab167b 100644 --- a/core/callable.cpp +++ b/core/callable.cpp @@ -78,6 +78,12 @@ StringName Callable::get_method() const { return method; } +CallableCustom *Callable::get_custom() const { + ERR_FAIL_COND_V_MSG(!is_custom(), NULL, + vformat("Can't get custom on non-CallableCustom \"%s\".", operator String())); + return custom; +} + uint32_t Callable::hash() const { if (is_custom()) { return custom->hash(); diff --git a/core/callable.h b/core/callable.h index cecf2264a3..7fa024dccd 100644 --- a/core/callable.h +++ b/core/callable.h @@ -84,6 +84,7 @@ public: Object *get_object() const; ObjectID get_object_id() const; StringName get_method() const; + CallableCustom *get_custom() const; uint32_t hash() const; diff --git a/core/type_info.h b/core/type_info.h index 618419a323..3b08ff3cae 100644 --- a/core/type_info.h +++ b/core/type_info.h @@ -174,7 +174,7 @@ MAKE_TYPE_INFO(IP_Address, Variant::STRING) template <> struct GetTypeInfo<ObjectID> { static const Variant::Type VARIANT_TYPE = Variant::INT; - static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_INT_IS_UINT64; static inline PropertyInfo get_class_info() { return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_INT_IS_OBJECTID); } diff --git a/doc/classes/AnimationNodeStateMachine.xml b/doc/classes/AnimationNodeStateMachine.xml index 4ff39b7500..3b351a3345 100644 --- a/doc/classes/AnimationNodeStateMachine.xml +++ b/doc/classes/AnimationNodeStateMachine.xml @@ -28,17 +28,6 @@ Adds a new node to the graph. The [code]position[/code] is used for display in the editor. </description> </method> - <method name="replace_node"> - <return type="void"> - </return> - <argument index="0" name="name" type="String"> - </argument> - <argument index="1" name="node" type="AnimationNode"> - </argument> - <description> - Replaces the node and keeps its transitions unchanged. - </description> - </method> <method name="add_transition"> <return type="void"> </return> @@ -194,6 +183,16 @@ Renames the given node. </description> </method> + <method name="replace_node"> + <return type="void"> + </return> + <argument index="0" name="name" type="StringName"> + </argument> + <argument index="1" name="node" type="AnimationNode"> + </argument> + <description> + </description> + </method> <method name="set_end_node"> <return type="void"> </return> diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index 6401feb95c..20296bbf45 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -116,7 +116,7 @@ </description> </method> <method name="back"> - <return type="void"> + <return type="Variant"> </return> <description> Returns the last element of the array, or [code]null[/code] if the array is empty. @@ -213,7 +213,7 @@ </description> </method> <method name="front"> - <return type="void"> + <return type="Variant"> </return> <description> Returns the first element of the array, or [code]null[/code] if the array is empty. @@ -260,28 +260,28 @@ </description> </method> <method name="max"> - <return type="void"> + <return type="Variant"> </return> <description> Returns the maximum value contained in the array if all elements are of comparable types. If the elements can't be compared, [code]null[/code] is returned. </description> </method> <method name="min"> - <return type="void"> + <return type="Variant"> </return> <description> Returns the minimum value contained in the array if all elements are of comparable types. If the elements can't be compared, [code]null[/code] is returned. </description> </method> <method name="pop_back"> - <return type="void"> + <return type="Variant"> </return> <description> Removes and returns the last element of the array. Returns [code]null[/code] if the array is empty. </description> </method> <method name="pop_front"> - <return type="void"> + <return type="Variant"> </return> <description> Removes and returns the first element of the array. Returns [code]null[/code] if the array is empty. diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 062e72388f..540ebf3931 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -98,7 +98,7 @@ </description> </method> <method name="get"> - <return type="void"> + <return type="Variant"> </return> <argument index="0" name="key" type="Variant"> </argument> diff --git a/doc/classes/Directory.xml b/doc/classes/Directory.xml index cb59a69876..ed4257a809 100644 --- a/doc/classes/Directory.xml +++ b/doc/classes/Directory.xml @@ -77,11 +77,8 @@ <method name="get_current_dir"> <return type="String"> </return> - <argument index="0" name="include_drive" type="bool" default="true"> - </argument> <description> Returns the absolute path to the currently opened directory (e.g. [code]res://folder[/code] or [code]C:\tmp\folder[/code]). - On Windows, if [code]include_drive[/code] is [code]false[/code], the leading drive specificator is omitted from the returned value (e.g. [code]\tmp\folder[/code]). </description> </method> <method name="get_current_drive"> diff --git a/doc/classes/Signal.xml b/doc/classes/Signal.xml index 51490caf6f..1a05c02b60 100644 --- a/doc/classes/Signal.xml +++ b/doc/classes/Signal.xml @@ -33,7 +33,7 @@ </description> </method> <method name="disconnect"> - <return type="void"> + <return type="Variant"> </return> <argument index="0" name="callable" type="Callable"> </argument> diff --git a/doc/classes/Transform.xml b/doc/classes/Transform.xml index 72f9c5493a..e4d367c344 100644 --- a/doc/classes/Transform.xml +++ b/doc/classes/Transform.xml @@ -149,7 +149,7 @@ </description> </method> <method name="xform"> - <return type="void"> + <return type="Variant"> </return> <argument index="0" name="v" type="Variant"> </argument> @@ -158,7 +158,7 @@ </description> </method> <method name="xform_inv"> - <return type="void"> + <return type="Variant"> </return> <argument index="0" name="v" type="Variant"> </argument> diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index 164efd4e5e..af93d4c654 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -151,7 +151,7 @@ </description> </method> <method name="xform"> - <return type="void"> + <return type="Variant"> </return> <argument index="0" name="v" type="Variant"> </argument> @@ -160,7 +160,7 @@ </description> </method> <method name="xform_inv"> - <return type="void"> + <return type="Variant"> </return> <argument index="0" name="v" type="Variant"> </argument> diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 16f6883b31..61480c3c20 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3612,6 +3612,7 @@ void EditorNode::register_editor_types() { ClassDB::register_class<ScriptCreateDialog>(); ClassDB::register_class<EditorFeatureProfile>(); ClassDB::register_class<EditorSpinSlider>(); + ClassDB::register_virtual_class<FileSystemDock>(); // FIXME: Is this stuff obsolete, or should it be ported to new APIs? ClassDB::register_class<EditorScenePostImport>(); @@ -6704,6 +6705,9 @@ EditorNode::EditorNode() { screenshot_timer->connect("timeout", callable_mp(this, &EditorNode::_request_screenshot)); add_child(screenshot_timer); screenshot_timer->set_owner(get_owner()); + + String exec = OS::get_singleton()->get_executable_path(); + EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "executable_path", exec); // Save editor executable path for third-party tools } EditorNode::~EditorNode() { diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 5204a4d579..07a63c39ba 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -226,6 +226,10 @@ EditorFileSystem *EditorInterface::get_resource_file_system() { return EditorFileSystem::get_singleton(); } +FileSystemDock *EditorInterface::get_file_system_dock() { + return EditorNode::get_singleton()->get_filesystem_dock(); +} + EditorSelection *EditorInterface::get_selection() { return EditorNode::get_singleton()->get_editor_selection(); } @@ -295,6 +299,7 @@ void EditorInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("select_file", "file"), &EditorInterface::select_file); ClassDB::bind_method(D_METHOD("get_selected_path"), &EditorInterface::get_selected_path); ClassDB::bind_method(D_METHOD("get_current_path"), &EditorInterface::get_current_path); + ClassDB::bind_method(D_METHOD("get_file_system_dock"), &EditorInterface::get_file_system_dock); ClassDB::bind_method(D_METHOD("set_plugin_enabled", "plugin", "enabled"), &EditorInterface::set_plugin_enabled); ClassDB::bind_method(D_METHOD("is_plugin_enabled", "plugin"), &EditorInterface::is_plugin_enabled); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 0313ef2b26..cd3f4d0638 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -53,6 +53,7 @@ class EditorSpatialGizmoPlugin; class EditorResourcePreview; class EditorFileSystem; class EditorToolAddons; +class FileSystemDock; class ScriptEditor; class EditorInterface : public Node { @@ -88,6 +89,8 @@ public: EditorResourcePreview *get_resource_previewer(); EditorFileSystem *get_resource_file_system(); + FileSystemDock *get_file_system_dock(); + Control *get_base_control(); void set_plugin_enabled(const String &p_plugin, bool p_enabled); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index e9e1b3be43..962d95736f 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1287,12 +1287,12 @@ void FileSystemDock::_make_scene_confirm() { editor->get_editor_data().set_scene_path(idx, scene_name); } -void FileSystemDock::_file_deleted(String p_file) { - emit_signal("file_deleted", p_file); +void FileSystemDock::_file_removed(String p_file) { + emit_signal("file_removed", p_file); } -void FileSystemDock::_folder_deleted(String p_folder) { - emit_signal("folder_deleted", p_folder); +void FileSystemDock::_folder_removed(String p_folder) { + emit_signal("folder_removed", p_folder); } void FileSystemDock::_rename_operation_confirm() { @@ -2613,8 +2613,8 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { add_child(owners_editor); remove_dialog = memnew(DependencyRemoveDialog); - remove_dialog->connect("file_removed", callable_mp(this, &FileSystemDock::_file_deleted)); - remove_dialog->connect("folder_removed", callable_mp(this, &FileSystemDock::_folder_deleted)); + remove_dialog->connect("file_removed", callable_mp(this, &FileSystemDock::_file_removed)); + remove_dialog->connect("folder_removed", callable_mp(this, &FileSystemDock::_folder_removed)); add_child(remove_dialog); move_dialog = memnew(EditorDirDialog); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 00f8cd9d50..6d2d8510d1 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -210,8 +210,8 @@ private: void _update_favorites_list_after_move(const Map<String, String> &p_files_renames, const Map<String, String> &p_folders_renames) const; void _update_project_settings_after_move(const Map<String, String> &p_renames) const; - void _file_deleted(String p_file); - void _folder_deleted(String p_folder); + void _file_removed(String p_file); + void _folder_removed(String p_folder); void _files_moved(String p_old_file, String p_new_file); void _folder_moved(String p_old_folder, String p_new_folder); diff --git a/methods.py b/methods.py index 28c6d0c097..56c495468d 100644 --- a/methods.py +++ b/methods.py @@ -193,25 +193,6 @@ void unregister_module_types() { return module_list -def win32_spawn(sh, escape, cmd, args, env): - import subprocess - newargs = ' '.join(args[1:]) - cmdline = cmd + " " + newargs - startupinfo = subprocess.STARTUPINFO() - for e in env: - if type(env[e]) != type(""): - env[e] = str(env[e]) - proc = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, startupinfo=startupinfo, shell=False, env=env) - _, err = proc.communicate() - rv = proc.wait() - if rv: - print("=====") - print(err) - print("=====") - return rv - - def disable_module(self): self.disabled_modules.append(self.current_module) diff --git a/modules/mono/SCsub b/modules/mono/SCsub index 41be367f2f..5f03fafdcf 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -1,6 +1,5 @@ #!/usr/bin/env python -import build_scripts.tls_configure as tls_configure import build_scripts.mono_configure as mono_configure Import('env') @@ -24,12 +23,6 @@ if env_mono['mono_glue']: if env_mono['tools'] or env_mono['target'] != 'release': env_mono.Append(CPPDEFINES=['GD_MONO_HOT_RELOAD']) -# Configure Thread Local Storage - -conf = Configure(env_mono) -tls_configure.configure(conf) -env_mono = conf.Finish() - # Configure Mono mono_configure.configure(env, env_mono) diff --git a/modules/mono/build_scripts/tls_configure.py b/modules/mono/build_scripts/tls_configure.py deleted file mode 100644 index 622280b00b..0000000000 --- a/modules/mono/build_scripts/tls_configure.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import print_function - -def supported(result): - return 'supported' if result else 'not supported' - - -def check_cxx11_thread_local(conf): - print('Checking for `thread_local` support...', end=" ") - result = conf.TryCompile('thread_local int foo = 0; int main() { return foo; }', '.cpp') - print(supported(result)) - return bool(result) - - -def check_declspec_thread(conf): - print('Checking for `__declspec(thread)` support...', end=" ") - result = conf.TryCompile('__declspec(thread) int foo = 0; int main() { return foo; }', '.cpp') - print(supported(result)) - return bool(result) - - -def check_gcc___thread(conf): - print('Checking for `__thread` support...', end=" ") - result = conf.TryCompile('__thread int foo = 0; int main() { return foo; }', '.cpp') - print(supported(result)) - return bool(result) - - -def configure(conf): - if check_cxx11_thread_local(conf): - conf.env.Append(CPPDEFINES=['HAVE_CXX11_THREAD_LOCAL']) - else: - if conf.env.msvc: - if check_declspec_thread(conf): - conf.env.Append(CPPDEFINES=['HAVE_DECLSPEC_THREAD']) - elif check_gcc___thread(conf): - conf.env.Append(CPPDEFINES=['HAVE_GCC___THREAD']) diff --git a/modules/mono/config.py b/modules/mono/config.py index 70cb464c7a..a5d3059ecc 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -9,7 +9,7 @@ def configure(env): env.use_ptrcall = True env.add_module_version_string('mono') - from SCons.Script import BoolVariable, PathVariable, Variables + from SCons.Script import BoolVariable, PathVariable, Variables, Help envvars = Variables() envvars.Add(PathVariable('mono_prefix', 'Path to the mono installation directory for the target platform and architecture', '', PathVariable.PathAccept)) @@ -18,6 +18,7 @@ def configure(env): envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False)) envvars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False)) envvars.Update(env) + Help(envvars.GenerateHelpText(env)) if env['platform'] == 'javascript': # Mono wasm already has zlib builtin, so we need this workaround to avoid symbol collisions diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 1f56b77d93..28bacbd0f0 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -62,7 +62,6 @@ #include "signal_awaiter_utils.h" #include "utils/macros.h" #include "utils/string_utils.h" -#include "utils/thread_local.h" #define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var) @@ -75,7 +74,7 @@ static bool _create_project_solution_if_needed() { if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) { // A solution does not yet exist, create a new one - CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == NULL); + CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == nullptr); return CSharpLanguage::get_singleton()->get_godotsharp_editor()->call("CreateProjectSolution"); } @@ -83,7 +82,7 @@ static bool _create_project_solution_if_needed() { } #endif -CSharpLanguage *CSharpLanguage::singleton = NULL; +CSharpLanguage *CSharpLanguage::singleton = nullptr; String CSharpLanguage::get_name() const { @@ -142,14 +141,17 @@ void CSharpLanguage::init() { void CSharpLanguage::finish() { + if (finalized) + return; + finalizing = true; // Make sure all script binding gchandles are released before finalizing GDMono for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) { CSharpScriptBinding &script_binding = E->value(); - if (script_binding.gchandle.is_valid()) { - script_binding.gchandle->release(); + if (!script_binding.gchandle.is_released()) { + script_binding.gchandle.release(); script_binding.inited = false; } } @@ -175,7 +177,10 @@ void CSharpLanguage::finish() { } #endif + memdelete(managed_callable_middleman); + finalizing = false; + finalized = true; } void CSharpLanguage::get_reserved_words(List<String> *p_words) const { @@ -434,13 +439,12 @@ static String variant_type_to_managed_name(const String &p_var_type_name) { return "byte[]"; if (p_var_type_name == Variant::get_type_name(Variant::PACKED_INT32_ARRAY)) return "int[]"; - if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT32_ARRAY)) { -#ifdef REAL_T_IS_DOUBLE - return "double[]"; -#else + if (p_var_type_name == Variant::get_type_name(Variant::PACKED_INT64_ARRAY)) + return "long[]"; + if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT32_ARRAY)) return "float[]"; -#endif - } + if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT64_ARRAY)) + return "double[]"; if (p_var_type_name == Variant::get_type_name(Variant::PACKED_STRING_ARRAY)) return "string[]"; if (p_var_type_name == Variant::get_type_name(Variant::PACKED_VECTOR2_ARRAY)) @@ -450,12 +454,18 @@ static String variant_type_to_managed_name(const String &p_var_type_name) { if (p_var_type_name == Variant::get_type_name(Variant::PACKED_COLOR_ARRAY)) return "Color[]"; + if (p_var_type_name == Variant::get_type_name(Variant::SIGNAL)) + return "SignalInfo"; + Variant::Type var_types[] = { Variant::BOOL, Variant::INT, Variant::VECTOR2, + Variant::VECTOR2I, Variant::RECT2, + Variant::RECT2I, Variant::VECTOR3, + Variant::VECTOR3I, Variant::TRANSFORM2D, Variant::PLANE, Variant::QUAT, @@ -463,8 +473,10 @@ static String variant_type_to_managed_name(const String &p_var_type_name) { Variant::BASIS, Variant::TRANSFORM, Variant::COLOR, + Variant::STRING_NAME, Variant::NODE_PATH, - Variant::_RID + Variant::_RID, + Variant::CALLABLE }; for (unsigned int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) { @@ -561,7 +573,13 @@ String CSharpLanguage::debug_get_stack_level_source(int p_level) const { Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() { #ifdef DEBUG_ENABLED - _TLS_RECURSION_GUARD_V_(Vector<StackInfo>()); + // Printing an error here will result in endless recursion, so we must be careful + static thread_local bool _recursion_flag_ = false; + if (_recursion_flag_) + return Vector<StackInfo>(); + _recursion_flag_ = true; + SCOPE_EXIT { _recursion_flag_ = false; }; + GD_MONO_SCOPE_THREAD_ATTACH; if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoCache::cached_data.corlib_cache_updated) @@ -586,7 +604,13 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() #ifdef DEBUG_ENABLED Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) { - _TLS_RECURSION_GUARD_V_(Vector<StackInfo>()); + // Printing an error here will result in endless recursion, so we must be careful + static thread_local bool _recursion_flag_ = false; + if (_recursion_flag_) + return Vector<StackInfo>(); + _recursion_flag_ = true; + SCOPE_EXIT { _recursion_flag_ = false; }; + GD_MONO_SCOPE_THREAD_ATTACH; MonoException *exc = NULL; @@ -655,7 +679,7 @@ void CSharpLanguage::pre_unsafe_unreference(Object *p_obj) { void CSharpLanguage::frame() { if (gdmono && gdmono->is_runtime_initialized() && gdmono->get_core_api_assembly() != NULL) { - const Ref<MonoGCHandle> &task_scheduler_handle = GDMonoCache::cached_data.task_scheduler_handle; + const Ref<MonoGCHandleRef> &task_scheduler_handle = GDMonoCache::cached_data.task_scheduler_handle; if (task_scheduler_handle.is_valid()) { MonoObject *task_scheduler = task_scheduler_handle->get_target(); @@ -774,6 +798,36 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { } } + scripts.sort_custom<CSharpScriptDepSort>(); // Update in inheritance dependency order + + // Serialize managed callables + { + MutexLock lock(ManagedCallable::instances_mutex); + + for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) { + ManagedCallable *managed_callable = elem->self(); + + MonoDelegate *delegate = (MonoDelegate *)managed_callable->delegate_handle.get_target(); + + Array serialized_data; + MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); + + MonoException *exc = NULL; + bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate).invoke(delegate, managed_serialized_data, &exc); + + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + continue; + } + + if (success) { + ManagedCallable::instances_pending_reload.insert(managed_callable, serialized_data); + } else if (OS::get_singleton()->is_stdout_verbose()) { + OS::get_singleton()->print("Failed to serialize delegate\n"); + } + } + } + List<Ref<CSharpScript>> to_reload; // We need to keep reference instances alive during reloading @@ -789,8 +843,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { // As scripts are going to be reloaded, must proceed without locking here - scripts.sort_custom<CSharpScriptDepSort>(); // Update in inheritance dependency order - for (List<Ref<CSharpScript>>::Element *E = scripts.front(); E; E = E->next()) { Ref<CSharpScript> &script = E->get(); @@ -845,6 +897,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { // TODO: Proper state backup (Not only variants, serialize managed state of scripts) csi->get_properties_state_for_reloading(state.properties); + csi->get_event_signals_state_for_reloading(state.event_signals); owners_map[obj->get_instance_id()] = state; } @@ -957,7 +1010,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { CSharpScript::initialize_for_managed_type(script, script_class, native); } - String native_name = NATIVE_GDMONOCLASS_NAME(script->native); + StringName native_name = NATIVE_GDMONOCLASS_NAME(script->native); { for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) { @@ -1034,15 +1087,80 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { obj->get_script_instance()->set(G->get().first, G->get().second); } - // Call OnAfterDeserialization CSharpInstance *csi = CAST_CSHARP_INSTANCE(obj->get_script_instance()); - if (csi && csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) - obj->get_script_instance()->call_multilevel(string_names.on_after_deserialize); + + if (csi) { + for (List<Pair<StringName, Array>>::Element *G = state_backup.event_signals.front(); G; G = G->next()) { + const StringName &name = G->get().first; + const Array &serialized_data = G->get().second; + + Map<StringName, CSharpScript::EventSignal>::Element *match = script->event_signals.find(name); + + if (!match) { + // The event or its signal attribute were removed + continue; + } + + const CSharpScript::EventSignal &event_signal = match->value(); + + MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); + MonoDelegate *delegate = NULL; + + MonoException *exc = NULL; + bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegate).invoke(managed_serialized_data, &delegate, &exc); + + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + continue; + } + + if (success) { + ERR_CONTINUE(delegate == NULL); + event_signal.field->set_value(csi->get_mono_object(), (MonoObject *)delegate); + } else if (OS::get_singleton()->is_stdout_verbose()) { + OS::get_singleton()->print("Failed to deserialize event signal delegate\n"); + } + } + + // Call OnAfterDeserialization + if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) + obj->get_script_instance()->call_multilevel(string_names.on_after_deserialize); + } } script->pending_reload_instances.clear(); } + // Deserialize managed callables + { + MutexLock lock(ManagedCallable::instances_mutex); + + for (Map<ManagedCallable *, Array>::Element *elem = ManagedCallable::instances_pending_reload.front(); elem; elem = elem->next()) { + ManagedCallable *managed_callable = elem->key(); + const Array &serialized_data = elem->value(); + + MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); + MonoDelegate *delegate = NULL; + + MonoException *exc = NULL; + bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegate).invoke(managed_serialized_data, &delegate, &exc); + + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + continue; + } + + if (success) { + ERR_CONTINUE(delegate == NULL); + managed_callable->set_delegate(delegate); + } else if (OS::get_singleton()->is_stdout_verbose()) { + OS::get_singleton()->print("Failed to deserialize delegate\n"); + } + } + + ManagedCallable::instances_pending_reload.clear(); + } + #ifdef TOOLS_ENABLED // FIXME: Hack to refresh editor in order to display new properties and signals. See if there is a better alternative. if (Engine::get_singleton()->is_editor_hint()) { @@ -1163,9 +1281,20 @@ bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) { void CSharpLanguage::_on_scripts_domain_unloaded() { for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) { CSharpScriptBinding &script_binding = E->value(); + script_binding.gchandle.release(); script_binding.inited = false; } + { + MutexLock lock(ManagedCallable::instances_mutex); + + for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) { + ManagedCallable *managed_callable = elem->self(); + managed_callable->delegate_handle.release(); + managed_callable->delegate_invoke = NULL; + } + } + scripts_metadata_invalidated = true; } @@ -1203,57 +1332,45 @@ void CSharpLanguage::set_language_index(int p_idx) { lang_idx = p_idx; } -void CSharpLanguage::release_script_gchandle(Ref<MonoGCHandle> &p_gchandle) { +void CSharpLanguage::release_script_gchandle(MonoGCHandleData &p_gchandle) { - if (!p_gchandle->is_released()) { // Do not lock unnecessarily + if (!p_gchandle.is_released()) { // Do not lock unnecessarily MutexLock lock(get_singleton()->script_gchandle_release_mutex); - p_gchandle->release(); + p_gchandle.release(); } } -void CSharpLanguage::release_script_gchandle(MonoObject *p_expected_obj, Ref<MonoGCHandle> &p_gchandle) { +void CSharpLanguage::release_script_gchandle(MonoObject *p_expected_obj, MonoGCHandleData &p_gchandle) { - uint32_t pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(p_expected_obj); // We might lock after this, so pin it + uint32_t pinned_gchandle = GDMonoUtils::new_strong_gchandle_pinned(p_expected_obj); // We might lock after this, so pin it - if (!p_gchandle->is_released()) { // Do not lock unnecessarily + if (!p_gchandle.is_released()) { // Do not lock unnecessarily MutexLock lock(get_singleton()->script_gchandle_release_mutex); - MonoObject *target = p_gchandle->get_target(); + MonoObject *target = p_gchandle.get_target(); // We release the gchandle if it points to the MonoObject* we expect (otherwise it was // already released and could have been replaced) or if we can't get its target MonoObject* // (which doesn't necessarily mean it was released, and we want it released in order to // avoid locking other threads unnecessarily). if (target == p_expected_obj || target == NULL) { - p_gchandle->release(); + p_gchandle.release(); } } - MonoGCHandle::free_handle(pinned_gchandle); + GDMonoUtils::free_gchandle(pinned_gchandle); } CSharpLanguage::CSharpLanguage() { ERR_FAIL_COND_MSG(singleton, "C# singleton already exist."); singleton = this; - - finalizing = false; - - gdmono = NULL; - - lang_idx = -1; - - scripts_metadata_invalidated = true; - -#ifdef TOOLS_ENABLED - godotsharp_editor = NULL; -#endif } CSharpLanguage::~CSharpLanguage() { finish(); - singleton = NULL; + singleton = nullptr; } bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object) { @@ -1286,7 +1403,7 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b r_script_binding.inited = true; r_script_binding.type_name = type_name; r_script_binding.wrapper_class = type_class; // cache - r_script_binding.gchandle = MonoGCHandle::create_strong(mono_object); + r_script_binding.gchandle = MonoGCHandleData::new_strong_handle(mono_object); r_script_binding.owner = p_object; // Tie managed to unmanaged @@ -1351,10 +1468,11 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) { if (script_binding.inited) { // Set the native instance field to IntPtr.Zero, if not yet garbage collected. // This is done to avoid trying to dispose the native instance from Dispose(bool). - MonoObject *mono_object = script_binding.gchandle->get_target(); + MonoObject *mono_object = script_binding.gchandle.get_target(); if (mono_object) { CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, NULL); } + script_binding.gchandle.release(); } script_bindings.erase(data); @@ -1374,26 +1492,26 @@ void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) { CRASH_COND(!data); CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); - Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + MonoGCHandleData &gchandle = script_binding.gchandle; if (!script_binding.inited) return; - if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 + if (ref_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 GD_MONO_SCOPE_THREAD_ATTACH; // The reference count was increased after the managed side was the only one referencing our owner. // This means the owner is being referenced again by the unmanaged side, // so the owner must hold the managed side alive again to avoid it from being GCed. - MonoObject *target = gchandle->get_target(); + MonoObject *target = gchandle.get_target(); if (!target) return; // Called after the managed side was collected, so nothing to do here // Release the current weak handle and replace it with a strong handle. - uint32_t strong_gchandle = MonoGCHandle::new_strong_handle(target); - gchandle->release(); - gchandle->set_handle(strong_gchandle, MonoGCHandle::STRONG_HANDLE); + MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(target); + gchandle.release(); + gchandle = strong_gchandle; } } @@ -1410,27 +1528,27 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) { CRASH_COND(!data); CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); - Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + MonoGCHandleData &gchandle = script_binding.gchandle; int refcount = ref_owner->reference_get_count(); if (!script_binding.inited) return refcount == 0; - if (refcount == 1 && gchandle.is_valid() && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 + if (refcount == 1 && !gchandle.is_released() && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 GD_MONO_SCOPE_THREAD_ATTACH; // If owner owner is no longer referenced by the unmanaged side, // the managed instance takes responsibility of deleting the owner when GCed. - MonoObject *target = gchandle->get_target(); + MonoObject *target = gchandle.get_target(); if (!target) return refcount == 0; // Called after the managed side was collected, so nothing to do here // Release the current strong handle and replace it with a weak handle. - uint32_t weak_gchandle = MonoGCHandle::new_weak_handle(target); - gchandle->release(); - gchandle->set_handle(weak_gchandle, MonoGCHandle::WEAK_HANDLE); + MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(target); + gchandle.release(); + gchandle = weak_gchandle; return false; } @@ -1438,14 +1556,13 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) { return refcount == 0; } -CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle) { +CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) { - CSharpInstance *instance = memnew(CSharpInstance); + CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(p_script))); Reference *ref = Object::cast_to<Reference>(p_owner); instance->base_ref = ref != NULL; - instance->script = Ref<CSharpScript>(p_script); instance->owner = p_owner; instance->gchandle = p_gchandle; @@ -1459,8 +1576,8 @@ CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpS MonoObject *CSharpInstance::get_mono_object() const { - ERR_FAIL_COND_V(gchandle.is_null(), NULL); - return gchandle->get_target(); + ERR_FAIL_COND_V(gchandle.is_released(), NULL); + return gchandle.get_target(); } Object *CSharpInstance::get_owner() { @@ -1610,6 +1727,37 @@ void CSharpInstance::get_properties_state_for_reloading(List<Pair<StringName, Va } } +void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state) { + + MonoObject *owner_managed = get_mono_object(); + ERR_FAIL_NULL(owner_managed); + + for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) { + const CSharpScript::EventSignal &event_signal = E->value(); + + MonoDelegate *delegate_field_value = (MonoDelegate *)event_signal.field->get_value(owner_managed); + if (!delegate_field_value) + continue; // Empty + + Array serialized_data; + MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data); + + MonoException *exc = NULL; + bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate).invoke(delegate_field_value, managed_serialized_data, &exc); + + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + continue; + } + + if (success) { + r_state.push_back(Pair<StringName, Array>(event_signal.field->get_name(), serialized_data)); + } else if (OS::get_singleton()->is_stdout_verbose()) { + OS::get_singleton()->print("Failed to serialize event signal delegate\n"); + } + } +} + void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const { for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) { @@ -1802,10 +1950,6 @@ bool CSharpInstance::_unreference_owner_unsafe() { } MonoObject *CSharpInstance::_internal_new_managed() { -#ifdef DEBUG_ENABLED - CRASH_COND(!gchandle.is_valid()); -#endif - // Search the constructor first, to fail with an error if it's not found before allocating anything else. GDMonoMethod *ctor = script->script_class->get_method(CACHED_STRING_NAME(dotctor), 0); ERR_FAIL_NULL_V_MSG(ctor, NULL, @@ -1832,7 +1976,7 @@ MonoObject *CSharpInstance::_internal_new_managed() { } // Tie managed to unmanaged - gchandle = MonoGCHandle::create_strong(mono_object); + gchandle = MonoGCHandleData::new_strong_handle(mono_object); if (base_ref) _reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) @@ -1847,9 +1991,11 @@ MonoObject *CSharpInstance::_internal_new_managed() { void CSharpInstance::mono_object_disposed(MonoObject *p_obj) { + disconnect_event_signals(); + #ifdef DEBUG_ENABLED CRASH_COND(base_ref); - CRASH_COND(gchandle.is_null()); + CRASH_COND(gchandle.is_released()); #endif CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle); } @@ -1858,7 +2004,7 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f #ifdef DEBUG_ENABLED CRASH_COND(!base_ref); - CRASH_COND(gchandle.is_null()); + CRASH_COND(gchandle.is_released()); #endif r_remove_script_instance = false; @@ -1888,6 +2034,33 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f } } +void CSharpInstance::connect_event_signals() { + for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) { + const CSharpScript::EventSignal &event_signal = E->value(); + + StringName signal_name = event_signal.field->get_name(); + + // TODO: Use pooling for ManagedCallable instances. + auto event_signal_callable = memnew(EventSignalCallable(owner, &event_signal)); + + owner->connect(signal_name, Callable(event_signal_callable)); + } +} + +void CSharpInstance::disconnect_event_signals() { + for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) { + const CSharpScript::EventSignal &event_signal = E->value(); + + StringName signal_name = event_signal.field->get_name(); + + // TODO: It would be great if we could store this EventSignalCallable on the stack. + // The problem is that Callable memdeletes it when it's destructed... + auto event_signal_callable = memnew(EventSignalCallable(owner, &event_signal)); + + owner->disconnect(signal_name, Callable(event_signal_callable)); + } +} + void CSharpInstance::refcount_incremented() { #ifdef DEBUG_ENABLED @@ -1897,7 +2070,7 @@ void CSharpInstance::refcount_incremented() { Reference *ref_owner = Object::cast_to<Reference>(owner); - if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 + if (ref_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 GD_MONO_SCOPE_THREAD_ATTACH; // The reference count was increased after the managed side was the only one referencing our owner. @@ -1905,9 +2078,9 @@ void CSharpInstance::refcount_incremented() { // so the owner must hold the managed side alive again to avoid it from being GCed. // Release the current weak handle and replace it with a strong handle. - uint32_t strong_gchandle = MonoGCHandle::new_strong_handle(gchandle->get_target()); - gchandle->release(); - gchandle->set_handle(strong_gchandle, MonoGCHandle::STRONG_HANDLE); + MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(gchandle.get_target()); + gchandle.release(); + gchandle = strong_gchandle; } } @@ -1922,16 +2095,16 @@ bool CSharpInstance::refcount_decremented() { int refcount = ref_owner->reference_get_count(); - if (refcount == 1 && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 + if (refcount == 1 && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 GD_MONO_SCOPE_THREAD_ATTACH; // If owner owner is no longer referenced by the unmanaged side, // the managed instance takes responsibility of deleting the owner when GCed. // Release the current strong handle and replace it with a weak handle. - uint32_t weak_gchandle = MonoGCHandle::new_weak_handle(gchandle->get_target()); - gchandle->release(); - gchandle->set_handle(weak_gchandle, MonoGCHandle::WEAK_HANDLE); + MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(gchandle.get_target()); + gchandle.release(); + gchandle = weak_gchandle; return false; } @@ -2087,13 +2260,8 @@ ScriptLanguage *CSharpInstance::get_language() { return CSharpLanguage::get_singleton(); } -CSharpInstance::CSharpInstance() : - owner(NULL), - base_ref(false), - ref_dying(false), - unsafe_referenced(false), - predelete_notified(false), - destructing_script_instance(false) { +CSharpInstance::CSharpInstance(const Ref<CSharpScript> &p_script) : + script(p_script) { } CSharpInstance::~CSharpInstance() { @@ -2102,7 +2270,7 @@ CSharpInstance::~CSharpInstance() { destructing_script_instance = true; - if (gchandle.is_valid()) { + if (!gchandle.is_released()) { if (!predelete_notified && !ref_dying) { // This destructor is not called from the owners destructor. // This could be being called from the owner's set_script_instance method, @@ -2110,7 +2278,7 @@ CSharpInstance::~CSharpInstance() { // we must call Dispose here, because Dispose calls owner->set_script_instance(NULL) // and that would mess up with the new script instance if called later. - MonoObject *mono_object = gchandle->get_target(); + MonoObject *mono_object = gchandle.get_target(); if (mono_object) { MonoException *exc = NULL; @@ -2122,7 +2290,7 @@ CSharpInstance::~CSharpInstance() { } } - gchandle->release(); // Make sure the gchandle is released + gchandle.release(); // Make sure the gchandle is released } // If not being called from the owner's destructor, and we still hold a reference to the owner @@ -2282,7 +2450,7 @@ bool CSharpScript::_update_exports() { return false; } - uint32_t tmp_pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(tmp_object); // pin it (not sure if needed) + uint32_t tmp_pinned_gchandle = GDMonoUtils::new_strong_gchandle_pinned(tmp_object); // pin it (not sure if needed) GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0); @@ -2297,7 +2465,7 @@ bool CSharpScript::_update_exports() { if (ctor_exc) { // TODO: Should we free 'tmp_native' if the exception was thrown after its creation? - MonoGCHandle::free_handle(tmp_pinned_gchandle); + GDMonoUtils::free_gchandle(tmp_pinned_gchandle); tmp_object = NULL; ERR_PRINT("Exception thrown from constructor of temporary MonoObject:"); @@ -2376,7 +2544,7 @@ bool CSharpScript::_update_exports() { GDMonoUtils::debug_print_unhandled_exception(exc); } - MonoGCHandle::free_handle(tmp_pinned_gchandle); + GDMonoUtils::free_gchandle(tmp_pinned_gchandle); tmp_object = NULL; if (tmp_native && !base_ref) { @@ -2416,6 +2584,7 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati // make sure this classes signals are empty when loading for the first time _signals.clear(); + event_signals.clear(); GD_MONO_SCOPE_THREAD_ATTACH; @@ -2423,56 +2592,90 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati while (top && top != p_native_class) { const Vector<GDMonoClass *> &delegates = top->get_all_delegates(); for (int i = delegates.size() - 1; i >= 0; --i) { - Vector<Argument> parameters; - GDMonoClass *delegate = delegates[i]; - if (_get_signal(top, delegate, parameters)) { + if (!delegate->has_attribute(CACHED_CLASS(SignalAttribute))) + continue; + + // Arguments are accessibles as arguments of .Invoke method + GDMonoMethod *invoke_method = delegate->get_method(mono_get_delegate_invoke(delegate->get_mono_ptr())); + + Vector<SignalParameter> parameters; + if (_get_signal(top, invoke_method, parameters)) { _signals[delegate->get_name()] = parameters; } } + List<StringName> found_event_signals; + + void *iter = NULL; + MonoEvent *raw_event = NULL; + while ((raw_event = mono_class_get_events(top->get_mono_ptr(), &iter)) != NULL) { + MonoCustomAttrInfo *event_attrs = mono_custom_attrs_from_event(top->get_mono_ptr(), raw_event); + if (event_attrs) { + if (mono_custom_attrs_has_attr(event_attrs, CACHED_CLASS(SignalAttribute)->get_mono_ptr())) { + const char *event_name = mono_event_get_name(raw_event); + found_event_signals.push_back(StringName(event_name)); + } + + mono_custom_attrs_free(event_attrs); + } + } + + const Vector<GDMonoField *> &fields = top->get_all_fields(); + for (int i = 0; i < fields.size(); i++) { + GDMonoField *field = fields[i]; + + GDMonoClass *field_class = field->get_type().type_class; + + if (!mono_class_is_delegate(field_class->get_mono_ptr())) + continue; + + if (!found_event_signals.find(field->get_name())) + continue; + + GDMonoMethod *invoke_method = field_class->get_method(mono_get_delegate_invoke(field_class->get_mono_ptr())); + + Vector<SignalParameter> parameters; + if (_get_signal(top, invoke_method, parameters)) { + event_signals[field->get_name()] = { field, invoke_method, parameters }; + } + } + top = top->get_parent_class(); } signals_invalidated = false; } -bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> ¶ms) { +bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> ¶ms) { GD_MONO_ASSERT_THREAD_ATTACHED; - if (p_delegate->has_attribute(CACHED_CLASS(SignalAttribute))) { - MonoType *raw_type = p_delegate->get_mono_type(); + Vector<StringName> names; + Vector<ManagedType> types; + p_delegate_invoke->get_parameter_names(names); + p_delegate_invoke->get_parameter_types(types); - if (mono_type_get_type(raw_type) == MONO_TYPE_CLASS) { - // Arguments are accessibles as arguments of .Invoke method - GDMonoMethod *invoke = p_delegate->get_method("Invoke", -1); - - Vector<StringName> names; - Vector<ManagedType> types; - invoke->get_parameter_names(names); - invoke->get_parameter_types(types); - - if (names.size() == types.size()) { - for (int i = 0; i < names.size(); ++i) { - Argument arg; - arg.name = names[i]; - arg.type = GDMonoMarshal::managed_to_variant_type(types[i]); - - if (arg.type == Variant::NIL) { - ERR_PRINT("Unknown type of signal parameter: '" + arg.name + "' in '" + p_class->get_full_name() + "'."); - return false; - } + for (int i = 0; i < names.size(); ++i) { + SignalParameter arg; + arg.name = names[i]; - params.push_back(arg); - } + bool nil_is_variant = false; + arg.type = GDMonoMarshal::managed_to_variant_type(types[i], &nil_is_variant); - return true; + if (arg.type == Variant::NIL) { + if (nil_is_variant) { + arg.nil_is_variant = true; + } else { + ERR_PRINT("Unknown type of signal parameter: '" + arg.name + "' in '" + p_class->get_full_name() + "'."); + return false; } } + + params.push_back(arg); } - return false; + return true; } #ifdef TOOLS_ENABLED @@ -2523,7 +2726,8 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect } } - Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type); + bool nil_is_variant = false; + Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type, &nil_is_variant); if (!p_inspect_export || !exported) { r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE); @@ -2536,7 +2740,7 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect PropertyHint hint = PROPERTY_HINT_NONE; String hint_string; - if (variant_type == Variant::NIL) { + if (variant_type == Variant::NIL && !nil_is_variant) { ERR_PRINT("Unknown exported member type: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'."); return false; } @@ -2552,7 +2756,14 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr); } - r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE); + uint32_t prop_usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE; + + if (variant_type == Variant::NIL) { + // System.Object (Variant) + prop_usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + } + + r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, prop_usage); r_exported = true; return true; @@ -2562,6 +2773,11 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) { + if (p_variant_type == Variant::NIL) { + // System.Object (Variant) + return 1; + } + GD_MONO_ASSERT_THREAD_ATTACHED; if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) { @@ -2621,7 +2837,7 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage CRASH_COND(field_native_class == NULL); r_hint = PROPERTY_HINT_RESOURCE_TYPE; - r_hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class); + r_hint_string = String(NATIVE_GDMONOCLASS_NAME(field_native_class)); } else if (p_allow_generics && p_variant_type == Variant::ARRAY) { // Nested arrays are not supported in the inspector @@ -2820,22 +3036,6 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon bool CSharpScript::can_instance() const { #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - - // Hack to lower the risk of attached scripts not being added to the C# project - if (!get_path().empty() && get_path().find("::") == -1) { // Ignore if built-in script. Can happen if the file is deleted... - if (_create_project_solution_if_needed()) { - CSharpProject::add_item(GodotSharpDirs::get_project_csproj_path(), - "Compile", - ProjectSettings::get_singleton()->globalize_path(get_path())); - } else { - ERR_PRINT("C# project could not be created; cannot add file: '" + get_path() + "'."); - } - } - } -#endif - -#ifdef TOOLS_ENABLED bool extra_cond = tool || ScriptServer::is_scripting_enabled(); #else bool extra_cond = true; @@ -2894,8 +3094,8 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg CRASH_COND(data == NULL); CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); - if (script_binding.inited && script_binding.gchandle.is_valid()) { - MonoObject *mono_object = script_binding.gchandle->get_target(); + if (script_binding.inited && !script_binding.gchandle.is_released()) { + MonoObject *mono_object = script_binding.gchandle.get_target(); if (mono_object) { MonoException *exc = NULL; GDMonoUtils::dispose(mono_object, &exc); @@ -2905,13 +3105,13 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg } } + script_binding.gchandle.release(); // Just in case script_binding.inited = false; } } - CSharpInstance *instance = memnew(CSharpInstance); + CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(this))); instance->base_ref = p_isref; - instance->script = Ref<CSharpScript>(this); instance->owner = p_owner; instance->owner->set_script_instance(instance); @@ -2934,7 +3134,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg } // Tie managed to unmanaged - instance->gchandle = MonoGCHandle::create_strong(mono_object); + instance->gchandle = MonoGCHandleData::new_strong_handle(mono_object); if (instance->base_ref) instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) @@ -2998,12 +3198,14 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) { #endif if (native) { - String native_name = NATIVE_GDMONOCLASS_NAME(native); + StringName native_name = NATIVE_GDMONOCLASS_NAME(native); if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) { if (EngineDebugger::is_active()) { - CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0, "Script inherits from native type '" + native_name + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'"); + CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0, + "Script inherits from native type '" + String(native_name) + + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'"); } - ERR_FAIL_V_MSG(NULL, "Script inherits from native type '" + native_name + + ERR_FAIL_V_MSG(NULL, "Script inherits from native type '" + String(native_name) + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'."); } } @@ -3290,19 +3492,45 @@ void CSharpScript::update_exports() { } bool CSharpScript::has_script_signal(const StringName &p_signal) const { - return _signals.has(p_signal); + return event_signals.has(p_signal) || _signals.has(p_signal); } void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const { - for (const Map<StringName, Vector<Argument>>::Element *E = _signals.front(); E; E = E->next()) { + + for (const Map<StringName, Vector<SignalParameter>>::Element *E = _signals.front(); E; E = E->next()) { MethodInfo mi; + mi.name = E->key(); + + const Vector<SignalParameter> ¶ms = E->value(); + for (int i = 0; i < params.size(); i++) { + const SignalParameter ¶m = params[i]; + + PropertyInfo arg_info = PropertyInfo(param.type, param.name); + if (param.type == Variant::NIL && param.nil_is_variant) + arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + + mi.arguments.push_back(arg_info); + } + + r_signals->push_back(mi); + } + for (const Map<StringName, EventSignal>::Element *E = event_signals.front(); E; E = E->next()) { + MethodInfo mi; mi.name = E->key(); - for (int i = 0; i < E->get().size(); i++) { - PropertyInfo arg; - arg.name = E->get()[i].name; - mi.arguments.push_back(arg); + + const EventSignal &event_signal = E->value(); + const Vector<SignalParameter> ¶ms = event_signal.parameters; + for (int i = 0; i < params.size(); i++) { + const SignalParameter ¶m = params[i]; + + PropertyInfo arg_info = PropertyInfo(param.type, param.name); + if (param.type == Variant::NIL && param.nil_is_variant) + arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + + mi.arguments.push_back(arg_info); } + r_signals->push_back(mi); } } @@ -3420,19 +3648,10 @@ StringName CSharpScript::get_script_name() const { return name; } -CSharpScript::CSharpScript() : - script_list(this) { +CSharpScript::CSharpScript() { _clear(); -#ifdef TOOLS_ENABLED - source_changed_cache = false; - placeholder_fallback_enabled = false; - exports_invalidated = true; -#endif - - signals_invalidated = true; - _resource_path_changed(); #ifdef DEBUG_ENABLED @@ -3561,4 +3780,5 @@ CSharpLanguage::StringNameCache::StringNameCache() { on_before_serialize = StaticCString::create("OnBeforeSerialize"); on_after_deserialize = StaticCString::create("OnAfterDeserialize"); dotctor = StaticCString::create(".ctor"); + delegate_invoke_method_name = StaticCString::create("Invoke"); } diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index fa12aad1e1..53b4aa6c20 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -53,8 +53,8 @@ class CSharpLanguage; template <typename TScriptInstance, typename TScriptLanguage> TScriptInstance *cast_script_instance(ScriptInstance *p_inst) { if (!p_inst) - return NULL; - return p_inst->get_language() == TScriptLanguage::get_singleton() ? static_cast<TScriptInstance *>(p_inst) : NULL; + return nullptr; + return p_inst->get_language() == TScriptLanguage::get_singleton() ? static_cast<TScriptInstance *>(p_inst) : nullptr; } #else template <typename TScriptInstance, typename TScriptLanguage> @@ -69,18 +69,32 @@ class CSharpScript : public Script { GDCLASS(CSharpScript, Script); +public: + struct SignalParameter { + String name; + Variant::Type type; + bool nil_is_variant = false; + }; + + struct EventSignal { + GDMonoField *field = NULL; + GDMonoMethod *invoke_method = NULL; + Vector<SignalParameter> parameters; + }; + +private: friend class CSharpInstance; friend class CSharpLanguage; friend struct CSharpScriptDepSort; - bool tool; - bool valid; + bool tool = false; + bool valid = false; bool builtin; - GDMonoClass *base; - GDMonoClass *native; - GDMonoClass *script_class; + GDMonoClass *base = nullptr; + GDMonoClass *native = nullptr; + GDMonoClass *script_class = nullptr; Ref<CSharpScript> base_cache; // TODO what's this for? @@ -92,6 +106,7 @@ class CSharpScript : public Script { // Replace with buffer containing the serialized state of managed scripts. // Keep variant state backup to use only with script instance placeholders. List<Pair<StringName, Variant>> properties; + List<Pair<StringName, Array>> event_signals; }; Set<ObjectID> pending_reload_instances; @@ -103,15 +118,11 @@ class CSharpScript : public Script { String source; StringName name; - SelfList<CSharpScript> script_list; - - struct Argument { - String name; - Variant::Type type; - }; + SelfList<CSharpScript> script_list = this; - Map<StringName, Vector<Argument>> _signals; - bool signals_invalidated; + Map<StringName, Vector<SignalParameter>> _signals; + Map<StringName, EventSignal> event_signals; + bool signals_invalidated = true; Vector<ScriptNetData> rpc_functions; Vector<ScriptNetData> rpc_variables; @@ -120,9 +131,9 @@ class CSharpScript : public Script { List<PropertyInfo> exported_members_cache; // members_cache Map<StringName, Variant> exported_members_defval_cache; // member_default_values_cache Set<PlaceHolderScriptInstance *> placeholders; - bool source_changed_cache; - bool placeholder_fallback_enabled; - bool exports_invalidated; + bool source_changed_cache = false; + bool placeholder_fallback_enabled = false; + bool exports_invalidated = true; void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames); void _update_member_info_no_exports(); virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder); @@ -133,7 +144,7 @@ class CSharpScript : public Script { void _clear(); void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class); - bool _get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> ¶ms); + bool _get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> ¶ms); bool _update_exports(); #ifdef TOOLS_ENABLED @@ -221,15 +232,17 @@ class CSharpInstance : public ScriptInstance { friend class CSharpScript; friend class CSharpLanguage; - Object *owner; - bool base_ref; - bool ref_dying; - bool unsafe_referenced; - bool predelete_notified; - bool destructing_script_instance; + Object *owner = nullptr; + bool base_ref = false; + bool ref_dying = false; + bool unsafe_referenced = false; + bool predelete_notified = false; + bool destructing_script_instance = false; Ref<CSharpScript> script; - Ref<MonoGCHandle> gchandle; + MonoGCHandleData gchandle; + + Vector<Callable> event_signal_callables; bool _reference_owner_unsafe(); @@ -239,17 +252,18 @@ class CSharpInstance : public ScriptInstance { bool _unreference_owner_unsafe(); /* - * If NULL is returned, the caller must destroy the script instance by removing it from its owner. + * If nullptr is returned, the caller must destroy the script instance by removing it from its owner. */ MonoObject *_internal_new_managed(); // Do not use unless you know what you are doing friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *); - static CSharpInstance *create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle); + static CSharpInstance *create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle); void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount); void get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state); + void get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state); public: MonoObject *get_mono_object() const; @@ -272,11 +286,14 @@ public: void mono_object_disposed(MonoObject *p_obj); /* - * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if + * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, ifevent_signal * 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner. */ void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance); + void connect_event_signals(); + void disconnect_event_signals(); + virtual void refcount_incremented(); virtual bool refcount_decremented(); @@ -301,7 +318,7 @@ public: virtual ScriptLanguage *get_language(); - CSharpInstance(); + CSharpInstance(const Ref<CSharpScript> &p_script); ~CSharpInstance(); }; @@ -309,8 +326,18 @@ struct CSharpScriptBinding { bool inited; StringName type_name; GDMonoClass *wrapper_class; - Ref<MonoGCHandle> gchandle; + MonoGCHandleData gchandle; Object *owner; + + CSharpScriptBinding() : + inited(false), + wrapper_class(NULL), + owner(NULL) { + } +}; + +class ManagedCallableMiddleman : public Object { + GDCLASS(ManagedCallableMiddleman, Object); }; class CSharpLanguage : public ScriptLanguage { @@ -320,9 +347,10 @@ class CSharpLanguage : public ScriptLanguage { static CSharpLanguage *singleton; - bool finalizing; + bool finalizing = false; + bool finalized = false; - GDMono *gdmono; + GDMono *gdmono = nullptr; SelfList<CSharpScript>::List script_list; Mutex script_instances_mutex; @@ -337,6 +365,8 @@ class CSharpLanguage : public ScriptLanguage { Mutex unsafe_object_references_lock; #endif + ManagedCallableMiddleman *managed_callable_middleman = memnew(ManagedCallableMiddleman); + struct StringNameCache { StringName _signal_callback; @@ -348,17 +378,18 @@ class CSharpLanguage : public ScriptLanguage { StringName dotctor; // .ctor StringName on_before_serialize; // OnBeforeSerialize StringName on_after_deserialize; // OnAfterDeserialize + StringName delegate_invoke_method_name; StringNameCache(); }; - int lang_idx; + int lang_idx = -1; Dictionary scripts_metadata; - bool scripts_metadata_invalidated; + bool scripts_metadata_invalidated = true; // For debug_break and debug_break_parse - int _debug_parse_err_line; + int _debug_parse_err_line = -1; String _debug_parse_err_file; String _debug_error; @@ -368,7 +399,7 @@ class CSharpLanguage : public ScriptLanguage { void _on_scripts_domain_unloaded(); #ifdef TOOLS_ENABLED - EditorPlugin *godotsharp_editor; + EditorPlugin *godotsharp_editor = nullptr; static void _editor_init_callback(); #endif @@ -389,8 +420,8 @@ public: _FORCE_INLINE_ EditorPlugin *get_godotsharp_editor() const { return godotsharp_editor; } #endif - static void release_script_gchandle(Ref<MonoGCHandle> &p_gchandle); - static void release_script_gchandle(MonoObject *p_expected_obj, Ref<MonoGCHandle> &p_gchandle); + static void release_script_gchandle(MonoGCHandleData &p_gchandle); + static void release_script_gchandle(MonoObject *p_expected_obj, MonoGCHandleData &p_gchandle); bool debug_break(const String &p_error, bool p_allow_continue = true); bool debug_break_parse(const String &p_file, int p_line, const String &p_error); @@ -410,6 +441,8 @@ public: return scripts_metadata; } + _FORCE_INLINE_ ManagedCallableMiddleman *get_managed_callable_middleman() const { return managed_callable_middleman; } + virtual String get_name() const; /* LANGUAGE FUNCTIONS */ @@ -426,7 +459,7 @@ public: virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; virtual bool is_using_templates(); virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); - /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const { return true; } + /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const { return true; } virtual String validate_path(const String &p_path) const; virtual Script *create_script() const; virtual bool has_named_classes() const; @@ -497,7 +530,7 @@ public: class ResourceFormatLoaderCSharpScript : public ResourceFormatLoader { public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr); + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs index b531b6aeee..326c49f096 100644 --- a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs +++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs @@ -30,7 +30,7 @@ namespace GodotTools.Core path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim(); - return rooted ? Path.DirectorySeparatorChar.ToString() + path : path; + return rooted ? Path.DirectorySeparatorChar + path : path; } private static readonly string driveRoot = Path.GetPathRoot(Environment.CurrentDirectory); diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs index 36961eb45e..f0e0d1b33d 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs @@ -1,5 +1,7 @@ using GodotTools.Core; using System; +using System.Collections.Generic; +using System.IO; using DotNet.Globbing; using Microsoft.Build.Construction; @@ -7,16 +9,15 @@ namespace GodotTools.ProjectEditor { public static class ProjectExtensions { - public static bool HasItem(this ProjectRootElement root, string itemType, string include) + public static ProjectItemElement FindItemOrNull(this ProjectRootElement root, string itemType, string include, bool noCondition = false) { - GlobOptions globOptions = new GlobOptions(); - globOptions.Evaluation.CaseInsensitive = false; + GlobOptions globOptions = new GlobOptions {Evaluation = {CaseInsensitive = false}}; string normalizedInclude = include.NormalizePath(); foreach (var itemGroup in root.ItemGroups) { - if (itemGroup.Condition.Length != 0) + if (noCondition && itemGroup.Condition.Length != 0) continue; foreach (var item in itemGroup.Items) @@ -27,18 +28,65 @@ namespace GodotTools.ProjectEditor var glob = Glob.Parse(item.Include.NormalizePath(), globOptions); if (glob.IsMatch(normalizedInclude)) - { - return true; - } + return item; } } - return false; + return null; + } + public static ProjectItemElement FindItemOrNullAbs(this ProjectRootElement root, string itemType, string include, bool noCondition = false) + { + GlobOptions globOptions = new GlobOptions {Evaluation = {CaseInsensitive = false}}; + + string normalizedInclude = Path.GetFullPath(include).NormalizePath(); + + foreach (var itemGroup in root.ItemGroups) + { + if (noCondition && itemGroup.Condition.Length != 0) + continue; + + foreach (var item in itemGroup.Items) + { + if (item.ItemType != itemType) + continue; + + var glob = Glob.Parse(Path.GetFullPath(item.Include).NormalizePath(), globOptions); + + if (glob.IsMatch(normalizedInclude)) + return item; + } + } + + return null; + } + + public static IEnumerable<ProjectItemElement> FindAllItemsInFolder(this ProjectRootElement root, string itemType, string folder) + { + string absFolderNormalizedWithSep = Path.GetFullPath(folder).NormalizePath() + Path.DirectorySeparatorChar; + + foreach (var itemGroup in root.ItemGroups) + { + foreach (var item in itemGroup.Items) + { + if (item.ItemType != itemType) + continue; + + string absPathNormalized = Path.GetFullPath(item.Include).NormalizePath(); + + if (absPathNormalized.StartsWith(absFolderNormalizedWithSep)) + yield return item; + } + } + } + + public static bool HasItem(this ProjectRootElement root, string itemType, string include, bool noCondition = false) + { + return root.FindItemOrNull(itemType, include, noCondition) != null; } public static bool AddItemChecked(this ProjectRootElement root, string itemType, string include) { - if (!root.HasItem(itemType, include)) + if (!root.HasItem(itemType, include, noCondition: true)) { root.AddItem(itemType, include); return true; @@ -47,6 +95,18 @@ namespace GodotTools.ProjectEditor return false; } + public static bool RemoveItemChecked(this ProjectRootElement root, string itemType, string include) + { + var item = root.FindItemOrNullAbs(itemType, include); + if (item != null) + { + item.Parent.RemoveChild(item); + return true; + } + + return false; + } + public static Guid GetGuid(this ProjectRootElement root) { foreach (var property in root.Properties) diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs index af36f125f5..1776b46e6a 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs @@ -23,6 +23,79 @@ namespace GodotTools.ProjectEditor root.Save(); } + public static void RenameItemInProjectChecked(string projectPath, string itemType, string oldInclude, string newInclude) + { + var dir = Directory.GetParent(projectPath).FullName; + var root = ProjectRootElement.Open(projectPath); + Debug.Assert(root != null); + + var normalizedOldInclude = oldInclude.NormalizePath(); + var normalizedNewInclude = newInclude.NormalizePath(); + + var item = root.FindItemOrNullAbs(itemType, normalizedOldInclude); + + if (item == null) + return; + + item.Include = normalizedNewInclude.RelativeToPath(dir).Replace("/", "\\"); + root.Save(); + } + + public static void RemoveItemFromProjectChecked(string projectPath, string itemType, string include) + { + var dir = Directory.GetParent(projectPath).FullName; + var root = ProjectRootElement.Open(projectPath); + Debug.Assert(root != null); + + var normalizedInclude = include.NormalizePath(); + + if (root.RemoveItemChecked(itemType, normalizedInclude)) + root.Save(); + } + + public static void RenameItemsToNewFolderInProjectChecked(string projectPath, string itemType, string oldFolder, string newFolder) + { + var dir = Directory.GetParent(projectPath).FullName; + var root = ProjectRootElement.Open(projectPath); + Debug.Assert(root != null); + + bool dirty = false; + + var oldFolderNormalized = oldFolder.NormalizePath(); + var newFolderNormalized = newFolder.NormalizePath(); + string absOldFolderNormalized = Path.GetFullPath(oldFolderNormalized).NormalizePath(); + string absNewFolderNormalized = Path.GetFullPath(newFolderNormalized).NormalizePath(); + + foreach (var item in root.FindAllItemsInFolder(itemType, oldFolderNormalized)) + { + string absPathNormalized = Path.GetFullPath(item.Include).NormalizePath(); + string absNewIncludeNormalized = absNewFolderNormalized + absPathNormalized.Substring(absOldFolderNormalized.Length); + item.Include = absNewIncludeNormalized.RelativeToPath(dir).Replace("/", "\\"); + dirty = true; + } + + if (dirty) + root.Save(); + } + + public static void RemoveItemsInFolderFromProjectChecked(string projectPath, string itemType, string folder) + { + var root = ProjectRootElement.Open(projectPath); + Debug.Assert(root != null); + + var folderNormalized = folder.NormalizePath(); + + var itemsToRemove = root.FindAllItemsInFolder(itemType, folderNormalized).ToList(); + + if (itemsToRemove.Count > 0) + { + foreach (var item in itemsToRemove) + item.Parent.RemoveChild(item); + + root.Save(); + } + } + private static string[] GetAllFilesRecursive(string rootDirectory, string mask) { string[] files = Directory.GetFiles(rootDirectory, mask, SearchOption.AllDirectories); diff --git a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs index 2971236482..e4c8759802 100644 --- a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs +++ b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs @@ -280,7 +280,7 @@ namespace GodotTools Text = "Build Project".TTR(), FocusMode = FocusModeEnum.None }; - buildProjectBtn.Connect("pressed", this, nameof(BuildProjectPressed)); + buildProjectBtn.PressedSignal += BuildProjectPressed; toolBarHBox.AddChild(buildProjectBtn); toolBarHBox.AddSpacer(begin: false); @@ -293,7 +293,7 @@ namespace GodotTools Visible = false, FocusMode = FocusModeEnum.None }; - warningsBtn.Connect("toggled", this, nameof(_WarningsToggled)); + warningsBtn.Toggled += _WarningsToggled; toolBarHBox.AddChild(warningsBtn); errorsBtn = new ToolButton @@ -304,7 +304,7 @@ namespace GodotTools Visible = false, FocusMode = FocusModeEnum.None }; - errorsBtn.Connect("toggled", this, nameof(_ErrorsToggled)); + errorsBtn.Toggled += _ErrorsToggled; toolBarHBox.AddChild(errorsBtn); toolBarHBox.AddSpacer(begin: false); @@ -315,7 +315,7 @@ namespace GodotTools FocusMode = FocusModeEnum.None, Visible = false }; - viewLogBtn.Connect("pressed", this, nameof(_ViewLogPressed)); + viewLogBtn.PressedSignal += _ViewLogPressed; toolBarHBox.AddChild(viewLogBtn); var hsc = new HSplitContainer @@ -326,8 +326,8 @@ namespace GodotTools panelBuildsTab.AddChild(hsc); buildTabsList = new ItemList { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill }; - buildTabsList.Connect("item_selected", this, nameof(_BuildTabsItemSelected)); - buildTabsList.Connect("nothing_selected", this, nameof(_BuildTabsNothingSelected)); + buildTabsList.ItemSelected += _BuildTabsItemSelected; + buildTabsList.NothingSelected += _BuildTabsNothingSelected; hsc.AddChild(buildTabsList); buildTabs = new TabContainer diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs b/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs index f75fe239e3..b2459b69ad 100644 --- a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs +++ b/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs @@ -251,7 +251,7 @@ namespace GodotTools base._Ready(); issuesList = new ItemList { SizeFlagsVertical = (int)SizeFlags.ExpandFill }; - issuesList.Connect("item_activated", this, nameof(_IssueActivated)); + issuesList.ItemActivated += _IssueActivated; AddChild(issuesList); } diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 05f84f547b..022005ad0b 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -96,7 +96,7 @@ namespace GodotTools.Export if (type != Internal.CSharpLanguageType) return; - if (Path.GetExtension(path) != $".{Internal.CSharpLanguageExtension}") + if (Path.GetExtension(path) != Internal.CSharpLanguageExtension) throw new ArgumentException($"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", nameof(path)); // TODO What if the source file is not part of the game's C# project diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 796522b2f2..e6d5dd9895 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -13,6 +13,7 @@ using JetBrains.Annotations; using static GodotTools.Internals.Globals; using File = GodotTools.Utils.File; using OS = GodotTools.Utils.OS; +using Path = System.IO.Path; namespace GodotTools { @@ -61,7 +62,7 @@ namespace GodotTools { Guid = guid, PathRelativeToSolution = name + ".csproj", - Configs = new List<string> { "Debug", "ExportDebug", "ExportRelease" } + Configs = new List<string> {"Debug", "ExportDebug", "ExportRelease"} }; solution.AddNewProject(name, projectInfo); @@ -121,16 +122,9 @@ namespace GodotTools aboutDialog.PopupCenteredMinsize(); } - private void _ToggleAboutDialogOnStart(bool enabled) + private void _MenuOptionPressed(int id) { - bool showOnStart = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start"); - if (showOnStart != enabled) - editorSettings.SetSetting("mono/editor/show_info_on_start", enabled); - } - - private void _MenuOptionPressed(MenuOptions id) - { - switch (id) + switch ((MenuOptions)id) { case MenuOptions.CreateSln: CreateProjectSolution(); @@ -168,6 +162,36 @@ namespace GodotTools // Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on. aboutDialog.PopupExclusive = false; } + + var fileSystemDock = GetEditorInterface().GetFileSystemDock(); + + fileSystemDock.FilesMoved += (file, newFile) => + { + if (Path.GetExtension(file) == Internal.CSharpLanguageExtension) + { + ProjectUtils.RenameItemInProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile", + ProjectSettings.GlobalizePath(file), ProjectSettings.GlobalizePath(newFile)); + } + }; + + fileSystemDock.FileRemoved += file => + { + if (Path.GetExtension(file) == Internal.CSharpLanguageExtension) + ProjectUtils.RemoveItemFromProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile", + ProjectSettings.GlobalizePath(file)); + }; + + fileSystemDock.FolderMoved += (oldFolder, newFolder) => + { + ProjectUtils.RenameItemsToNewFolderInProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile", + ProjectSettings.GlobalizePath(oldFolder), ProjectSettings.GlobalizePath(newFolder)); + }; + + fileSystemDock.FolderRemoved += oldFolder => + { + ProjectUtils.RemoveItemsInFolderFromProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile", + ProjectSettings.GlobalizePath(oldFolder)); + }; } } @@ -210,7 +234,7 @@ namespace GodotTools string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath); RiderPathManager.OpenFile(GodotSharpDirs.ProjectSlnPath, scriptPath, line); return Error.Ok; - } + } case ExternalEditorId.MonoDevelop: { string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath); @@ -346,7 +370,7 @@ namespace GodotTools bottomPanelBtn = AddControlToBottomPanel(BottomPanel, "Mono".TTR()); - AddChild(new HotReloadAssemblyWatcher { Name = "HotReloadAssemblyWatcher" }); + AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"}); menuPopup = new PopupMenu(); menuPopup.Hide(); @@ -394,8 +418,13 @@ namespace GodotTools EditorDef("mono/editor/show_info_on_start", true); // CheckBox in main container - aboutDialogCheckBox = new CheckBox { Text = "Show this warning when starting the editor" }; - aboutDialogCheckBox.Connect("toggled", this, nameof(_ToggleAboutDialogOnStart)); + aboutDialogCheckBox = new CheckBox {Text = "Show this warning when starting the editor"}; + aboutDialogCheckBox.Toggled += enabled => + { + bool showOnStart = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start"); + if (showOnStart != enabled) + editorSettings.SetSetting("mono/editor/show_info_on_start", enabled); + }; aboutVBox.AddChild(aboutDialogCheckBox); } @@ -424,7 +453,7 @@ namespace GodotTools menuPopup.AddItem("Create C# solution".TTR(), (int)MenuOptions.CreateSln); } - menuPopup.Connect("id_pressed", this, nameof(_MenuOptionPressed)); + menuPopup.IdPressed += _MenuOptionPressed; var buildButton = new ToolButton { @@ -432,7 +461,7 @@ namespace GodotTools HintTooltip = "Build solution", FocusMode = Control.FocusModeEnum.None }; - buildButton.Connect("pressed", this, nameof(_BuildSolutionPressed)); + buildButton.PressedSignal += _BuildSolutionPressed; AddControlToContainer(CustomControlContainer.Toolbar, buildButton); // External editor settings diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs index 0ed567afd1..1d19fab706 100644 --- a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs +++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs @@ -40,7 +40,7 @@ namespace GodotTools OneShot = false, WaitTime = (float)EditorDef("mono/assembly_watch_interval_sec", 0.5) }; - watchTimer.Connect("timeout", this, nameof(TimerTimeout)); + watchTimer.Timeout += TimerTimeout; AddChild(watchTimer); watchTimer.Start(); } diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs index 77740f0e53..e3a4fa7b45 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs @@ -162,7 +162,7 @@ namespace GodotTools.Ides.Rider private static string GetToolboxRiderRootPath(string localAppData) { - var toolboxPath = Path.Combine(localAppData, @"JetBrains\Toolbox"); + var toolboxPath = Path.Combine(localAppData, @"JetBrains/Toolbox"); var settingsJson = Path.Combine(toolboxPath, ".settings.json"); if (File.Exists(settingsJson)) @@ -172,7 +172,7 @@ namespace GodotTools.Ides.Rider toolboxPath = path; } - var toolboxRiderRootPath = Path.Combine(toolboxPath, @"apps\Rider"); + var toolboxRiderRootPath = Path.Combine(toolboxPath, @"apps/Rider"); return toolboxRiderRootPath; } diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs index 2e121ba879..026a7db89c 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs @@ -8,7 +8,7 @@ namespace GodotTools.Internals public static class Internal { public const string CSharpLanguageType = "CSharpScript"; - public const string CSharpLanguageExtension = "cs"; + public const string CSharpLanguageExtension = ".cs"; public static string UpdateApiAssembliesFromPrebuilt(string config) => internal_UpdateApiAssembliesFromPrebuilt(config); diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 5355cb4e45..9a5de6db16 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -76,7 +76,7 @@ #define GLUE_HEADER_FILE "glue_header.h" #define ICALL_PREFIX "godot_icall_" #define SINGLETON_ICALL_SUFFIX "_get_singleton" -#define ICALL_GET_METHODBIND ICALL_PREFIX "Object_ClassDB_get_method" +#define ICALL_GET_METHODBIND "__ClassDB_get_method" #define C_LOCAL_RET "ret" #define C_LOCAL_VARARG_RET "vararg_ret" @@ -95,6 +95,10 @@ #define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot" #define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type #define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array" +#define C_METHOD_MANAGED_TO_CALLABLE C_NS_MONOMARSHAL "::managed_to_callable" +#define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL "::callable_to_managed" +#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable" +#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info" #define BINDINGS_GENERATOR_VERSION UINT32_C(11) @@ -504,23 +508,23 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf xml_output.append(tag); xml_output.append("</c>"); } else if (tag == "PackedByteArray") { - xml_output.append("<see cref=\"byte\"/>"); + xml_output.append("<see cref=\"T:byte[]\"/>"); } else if (tag == "PackedInt32Array") { - xml_output.append("<see cref=\"int\"/>"); + xml_output.append("<see cref=\"T:int[]\"/>"); + } else if (tag == "PackedInt64Array") { + xml_output.append("<see cref=\"T:long[]\"/>"); } else if (tag == "PackedFloat32Array") { -#ifdef REAL_T_IS_DOUBLE - xml_output.append("<see cref=\"double\"/>"); -#else - xml_output.append("<see cref=\"float\"/>"); -#endif + xml_output.append("<see cref=\"T:float[]\"/>"); + } else if (tag == "PackedFloat64Array") { + xml_output.append("<see cref=\"T:double[]\"/>"); } else if (tag == "PackedStringArray") { - xml_output.append("<see cref=\"string\"/>"); + xml_output.append("<see cref=\"T:string[]\"/>"); } else if (tag == "PackedVector2Array") { - xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector2\"/>"); + xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Vector2[]\"/>"); } else if (tag == "PackedVector3Array") { - xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector3\"/>"); + xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Vector3[]\"/>"); } else if (tag == "PackedColorArray") { - xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Color\"/>"); + xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Color[]\"/>"); } else { const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag)); @@ -932,7 +936,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) { "using System.Runtime.CompilerServices;\n" "\n"); cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); - cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK); + cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 "{"); cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = "); cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n"); @@ -944,7 +948,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) { #define ADD_INTERNAL_CALL(m_icall) \ if (!m_icall.editor_only) { \ cs_icalls_content.append(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ - cs_icalls_content.append(INDENT2 "internal extern static "); \ + cs_icalls_content.append(INDENT2 "internal static extern "); \ cs_icalls_content.append(m_icall.im_type_out + " "); \ cs_icalls_content.append(m_icall.name + "("); \ cs_icalls_content.append(m_icall.im_sig + ");\n"); \ @@ -1046,7 +1050,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) { #define ADD_INTERNAL_CALL(m_icall) \ if (m_icall.editor_only) { \ cs_icalls_content.append(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ - cs_icalls_content.append(INDENT2 "internal extern static "); \ + cs_icalls_content.append(INDENT2 "internal static extern "); \ cs_icalls_content.append(m_icall.im_type_out + " "); \ cs_icalls_content.append(m_icall.name + "("); \ cs_icalls_content.append(m_icall.im_sig + ");\n"); \ @@ -1312,7 +1316,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.append(itype.proxy_name); output.append(").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n"); - output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \""); + output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); output.append(itype.name); output.append("\";\n"); @@ -1324,7 +1328,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str } else if (is_derived_type) { // Add member fields - output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \""); + output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); output.append(itype.name); output.append("\";\n"); @@ -1363,6 +1367,13 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str "Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'."); } + for (const List<SignalInterface>::Element *E = itype.signals_.front(); E; E = E->next()) { + const SignalInterface &isignal = E->get(); + Error method_err = _generate_cs_signal(itype, isignal, output); + ERR_FAIL_COND_V_MSG(method_err != OK, method_err, + "Failed to generate signal '" + isignal.name + "' for class '" + itype.name + "'."); + } + if (itype.is_singleton) { InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr"); @@ -1424,7 +1435,16 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte } if (getter && setter) { - ERR_FAIL_COND_V(getter->return_type.cname != setter->arguments.back()->get().type.cname, ERR_BUG); + const ArgumentInterface &setter_first_arg = setter->arguments.back()->get(); + if (getter->return_type.cname != setter_first_arg.type.cname) { + // Special case for Node::set_name + bool whitelisted = getter->return_type.cname == name_cache.type_StringName && + setter_first_arg.type.cname == name_cache.type_String; + + ERR_FAIL_COND_V_MSG(!whitelisted, ERR_BUG, + "Return type from getter doesn't match first argument of setter for property: '" + + p_itype.name + "." + String(p_iprop.cname) + "'."); + } } const TypeReference &proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type; @@ -1525,7 +1545,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type); - String method_bind_field = "method_bind_" + itos(p_method_bind_count); + String method_bind_field = "__method_bind_" + itos(p_method_bind_count); String arguments_sig; String cs_in_statements; @@ -1612,7 +1632,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf { if (!p_imethod.is_virtual && !p_imethod.requires_object_call) { p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static IntPtr "); - p_output.append(method_bind_field + " = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); + p_output.append(method_bind_field); + p_output.append(" = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); p_output.append(p_imethod.name); p_output.append("\");\n"); } @@ -1726,6 +1747,106 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf return OK; } +Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output) { + String arguments_sig; + + // Retrieve information from the arguments + for (const List<ArgumentInterface>::Element *F = p_isignal.arguments.front(); F; F = F->next()) { + const ArgumentInterface &iarg = F->get(); + const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); + + // Add the current arguments to the signature + + if (F != p_isignal.arguments.front()) + arguments_sig += ", "; + + arguments_sig += arg_type->cs_type; + arguments_sig += " "; + arguments_sig += iarg.name; + } + + // Generate signal + { + if (p_isignal.method_doc && p_isignal.method_doc->description.size()) { + String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); + + if (summary_lines.size()) { + p_output.append(MEMBER_BEGIN "/// <summary>\n"); + + for (int i = 0; i < summary_lines.size(); i++) { + p_output.append(INDENT2 "/// "); + p_output.append(summary_lines[i]); + p_output.append("\n"); + } + + p_output.append(INDENT2 "/// </summary>"); + } + } + + if (p_isignal.is_deprecated) { + if (p_isignal.deprecation_message.empty()) + WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'."); + + p_output.append(MEMBER_BEGIN "[Obsolete(\""); + p_output.append(p_isignal.deprecation_message); + p_output.append("\")]"); + } + + String delegate_name = p_isignal.proxy_name; + delegate_name += "Handler"; // Delegate name is [SignalName]Handler + + // Generate delegate + p_output.append(MEMBER_BEGIN "public delegate void "); + p_output.append(delegate_name); + p_output.append("("); + p_output.append(arguments_sig); + p_output.append(");\n"); + + // TODO: + // Could we assume the StringName instance of signal name will never be freed (it's stored in ClassDB) before the managed world is unloaded? + // If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here. + + // Cached signal name (StringName) + p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static StringName __signal_name_"); + p_output.append(p_isignal.name); + p_output.append(" = \""); + p_output.append(p_isignal.name); + p_output.append("\";\n"); + + // Generate event + p_output.append(MEMBER_BEGIN "[Signal]" MEMBER_BEGIN "public "); + + if (p_itype.is_singleton) + p_output.append("static "); + + p_output.append("event "); + p_output.append(delegate_name); + p_output.append(" "); + p_output.append(p_isignal.proxy_name); + p_output.append("\n" OPEN_BLOCK_L2); + + if (p_itype.is_singleton) + p_output.append("add => Singleton.Connect(__signal_name_"); + else + p_output.append("add => Connect(__signal_name_"); + + p_output.append(p_isignal.name); + p_output.append(", new Callable(value));\n"); + + if (p_itype.is_singleton) + p_output.append(INDENT3 "remove => Singleton.Disconnect(__signal_name_"); + else + p_output.append(INDENT3 "remove => Disconnect(__signal_name_"); + + p_output.append(p_isignal.name); + p_output.append(", new Callable(value));\n"); + p_output.append(CLOSE_BLOCK_L2); + } + + return OK; +} + Error BindingsGenerator::generate_glue(const String &p_output_dir) { ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED); @@ -2479,13 +2600,92 @@ bool BindingsGenerator::_populate_object_type_interfaces() { } } + // Populate signals + + const HashMap<StringName, MethodInfo> &signal_map = class_info->signal_map; + const StringName *k = NULL; + + while ((k = signal_map.next(k))) { + SignalInterface isignal; + + const MethodInfo &method_info = signal_map.get(*k); + + isignal.name = method_info.name; + isignal.cname = method_info.name; + + int argc = method_info.arguments.size(); + + for (int i = 0; i < argc; i++) { + PropertyInfo arginfo = method_info.arguments[i]; + + String orig_arg_name = arginfo.name; + + ArgumentInterface iarg; + iarg.name = orig_arg_name; + + if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { + iarg.type.cname = arginfo.class_name; + iarg.type.is_enum = true; + } else if (arginfo.class_name != StringName()) { + iarg.type.cname = arginfo.class_name; + } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { + iarg.type.cname = arginfo.hint_string; + } else if (arginfo.type == Variant::NIL) { + iarg.type.cname = name_cache.type_Variant; + } else { + if (arginfo.type == Variant::INT) { + iarg.type.cname = _get_int_type_name_from_meta(GodotTypeInfo::METADATA_NONE); + } else if (arginfo.type == Variant::FLOAT) { + iarg.type.cname = _get_float_type_name_from_meta(GodotTypeInfo::METADATA_NONE); + } else { + iarg.type.cname = Variant::get_type_name(arginfo.type); + } + } + + iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name)); + + isignal.add_argument(iarg); + } + + isignal.proxy_name = escape_csharp_keyword(snake_to_pascal_case(isignal.name)); + + // Prevent the signal and its enclosing type from sharing the same name + if (isignal.proxy_name == itype.proxy_name) { + _log("Name of signal '%s' is ambiguous with the name of its enclosing class '%s'. Renaming signal to '%s_'\n", + isignal.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), isignal.proxy_name.utf8().get_data()); + + isignal.proxy_name += "_"; + } + + if (itype.find_property_by_proxy_name(isignal.proxy_name) || itype.find_method_by_proxy_name(isignal.proxy_name)) { + // ClassDB allows signal names that conflict with method or property names. + // While registering a signal with a conflicting name is considered wrong, + // it may still happen and it may take some time until someone fixes the name. + // We can't allow the bindings to be in a broken state while we wait for a fix; + // that's why we must handle this possibility by renaming the signal. + isignal.proxy_name += "Signal"; + } + + if (itype.class_doc) { + for (int i = 0; i < itype.class_doc->signals.size(); i++) { + const DocData::MethodDoc &signal_doc = itype.class_doc->signals[i]; + if (signal_doc.name == isignal.name) { + isignal.method_doc = &signal_doc; + break; + } + } + } + + itype.signals_.push_back(isignal); + } + // Populate enums and constants List<String> constants; ClassDB::get_integer_constant_list(type_cname, &constants, true); const HashMap<StringName, List<StringName>> &enum_map = class_info->enum_map; - const StringName *k = NULL; + k = NULL; while ((k = enum_map.next(k))) { StringName enum_proxy_cname = *k; @@ -2587,8 +2787,15 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar #endif break; case Variant::STRING: + case Variant::STRING_NAME: case Variant::NODE_PATH: - r_iarg.default_argument = "\"" + r_iarg.default_argument + "\""; + if (r_iarg.type.cname == name_cache.type_StringName || r_iarg.type.cname == name_cache.type_NodePath) { + r_iarg.default_argument = "(%s)\"" + r_iarg.default_argument + "\""; + r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF; + } else { + CRASH_COND(r_iarg.type.cname != name_cache.type_String); + r_iarg.default_argument = "\"" + r_iarg.default_argument + "\""; + } break; case Variant::TRANSFORM: if (p_val.operator Transform() == Transform()) @@ -2603,8 +2810,11 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; break; case Variant::VECTOR2: + case Variant::VECTOR2I: case Variant::RECT2: + case Variant::RECT2I: case Variant::VECTOR3: + case Variant::VECTOR3I: r_iarg.default_argument = "new %s" + r_iarg.default_argument; r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; break; @@ -2630,8 +2840,8 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar case Variant::ARRAY: case Variant::PACKED_BYTE_ARRAY: case Variant::PACKED_INT32_ARRAY: - case Variant::PACKED_FLOAT32_ARRAY: case Variant::PACKED_INT64_ARRAY: + case Variant::PACKED_FLOAT32_ARRAY: case Variant::PACKED_FLOAT64_ARRAY: case Variant::PACKED_STRING_ARRAY: case Variant::PACKED_VECTOR2_ARRAY: @@ -2646,8 +2856,13 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar r_iarg.default_argument = Variant::get_type_name(p_val.get_type()) + ".Identity"; r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; break; - default: { - } + case Variant::CALLABLE: + case Variant::SIGNAL: + CRASH_NOW_MSG("Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value."); + break; + default: + CRASH_NOW_MSG("Unexpected Variant type: " + itos(p_val.get_type())); + break; } if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null") @@ -2672,16 +2887,19 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = "GDMonoMarshal::M_" #m_type; \ itype.cs_in = "ref %s"; \ /* in cs_out, im_type_out (%3) includes the 'out ' part */ \ - itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;"; \ + itype.cs_out = "%0(%1, %3 argRet); return argRet;"; \ itype.im_type_out = "out " + itype.cs_type; \ itype.ret_as_byref_arg = true; \ builtin_types.insert(itype.cname, itype); \ } INSERT_STRUCT_TYPE(Vector2) + INSERT_STRUCT_TYPE(Vector2i) INSERT_STRUCT_TYPE(Rect2) + INSERT_STRUCT_TYPE(Rect2i) INSERT_STRUCT_TYPE(Transform2D) INSERT_STRUCT_TYPE(Vector3) + INSERT_STRUCT_TYPE(Vector3i) INSERT_STRUCT_TYPE(Basis) INSERT_STRUCT_TYPE(Quat) INSERT_STRUCT_TYPE(Transform) @@ -2749,7 +2967,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.im_type_out = "out " + itype.name; itype.cs_in = "ref %0"; /* in cs_out, im_type_out (%3) includes the 'out ' part */ - itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;"; + itype.cs_out = "%0(%1, %3 argRet); return argRet;"; itype.ret_as_byref_arg = true; builtin_types.insert(itype.cname, itype); @@ -2766,7 +2984,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.im_type_out = "out " + itype.name; itype.cs_in = "ref %0"; /* in cs_out, im_type_out (%3) includes the 'out ' part */ - itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;"; + itype.cs_out = "%0(%1, %3 argRet); return argRet;"; itype.ret_as_byref_arg = true; builtin_types.insert(itype.cname, itype); } @@ -2792,7 +3010,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.im_type_out = "out " + itype.proxy_name; itype.cs_in = "ref %0"; /* in cs_out, im_type_out (%3) includes the 'out ' part */ - itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;"; + itype.cs_out = "%0(%1, %3 argRet); return argRet;"; itype.ret_as_byref_arg = true; builtin_types.insert(itype.cname, itype); @@ -2814,7 +3032,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.im_type_out = "out " + itype.proxy_name; itype.cs_in = "ref %0"; /* in cs_out, im_type_out (%3) includes the 'out ' part */ - itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;"; + itype.cs_out = "%0(%1, %3 argRet); return argRet;"; itype.ret_as_byref_arg = true; builtin_types.insert(itype.cname, itype); } @@ -2835,6 +3053,24 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.im_type_out = itype.proxy_name; builtin_types.insert(itype.cname, itype); + // StringName + itype = TypeInterface(); + itype.name = "StringName"; + itype.cname = itype.name; + itype.proxy_name = "StringName"; + itype.c_in = "\t%0 %1_in = %1 ? *%1 : StringName();\n"; + itype.c_out = "\treturn memnew(StringName(%1));\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = itype.name; + itype.c_type_in = itype.c_type + "*"; + itype.c_type_out = itype.c_type + "*"; + itype.cs_type = itype.proxy_name; + itype.cs_in = "StringName." CS_SMETHOD_GETINSTANCE "(%0)"; + itype.cs_out = "return new %2(%0(%1));"; + itype.im_type_in = "IntPtr"; + itype.im_type_out = "IntPtr"; + builtin_types.insert(itype.cname, itype); + // NodePath itype = TypeInterface(); itype.name = "NodePath"; @@ -2883,6 +3119,40 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.im_type_out = itype.proxy_name; builtin_types.insert(itype.cname, itype); + // Callable + itype = TypeInterface::create_value_type(String("Callable")); + itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(*%1);\n"; + itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_CALLABLE "(%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type_in = "GDMonoMarshal::M_Callable*"; + itype.c_type_out = "GDMonoMarshal::M_Callable"; + itype.cs_in = "ref %s"; + /* in cs_out, im_type_out (%3) includes the 'out ' part */ + itype.cs_out = "%0(%1, %3 argRet); return argRet;"; + itype.im_type_out = "out " + itype.cs_type; + itype.ret_as_byref_arg = true; + builtin_types.insert(itype.cname, itype); + + // Signal + itype = TypeInterface(); + itype.name = "Signal"; + itype.cname = itype.name; + itype.proxy_name = "SignalInfo"; + itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(*%1);\n"; + itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_SIGNAL "(%1);\n"; + itype.c_arg_in = "&%s_in"; + itype.c_type = itype.name; + itype.c_type_in = "GDMonoMarshal::M_SignalInfo*"; + itype.c_type_out = "GDMonoMarshal::M_SignalInfo"; + itype.cs_in = "ref %s"; + /* in cs_out, im_type_out (%3) includes the 'out ' part */ + itype.cs_out = "%0(%1, %3 argRet); return argRet;"; + itype.cs_type = itype.proxy_name; + itype.im_type_in = "ref " + itype.cs_type; + itype.im_type_out = "out " + itype.cs_type; + itype.ret_as_byref_arg = true; + builtin_types.insert(itype.cname, itype); + // VarArg (fictitious type to represent variable arguments) itype = TypeInterface(); itype.name = "VarArg"; @@ -2917,13 +3187,11 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { #define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t) INSERT_ARRAY(PackedInt32Array, int); + INSERT_ARRAY(PackedInt64Array, long); INSERT_ARRAY_FULL(PackedByteArray, PackedByteArray, byte); -#ifdef REAL_T_IS_DOUBLE - INSERT_ARRAY(PackedFloat32Array, double); -#else INSERT_ARRAY(PackedFloat32Array, float); -#endif + INSERT_ARRAY(PackedFloat64Array, double); INSERT_ARRAY(PackedStringArray, string); @@ -3052,7 +3320,10 @@ void BindingsGenerator::_populate_global_constants() { // HARDCODED List<StringName> hardcoded_enums; + hardcoded_enums.push_back("Vector2.Axis"); + hardcoded_enums.push_back("Vector2i.Axis"); hardcoded_enums.push_back("Vector3.Axis"); + hardcoded_enums.push_back("Vector3i.Axis"); for (List<StringName>::Element *E = hardcoded_enums.front(); E; E = E->next()) { // These enums are not generated and must be written manually (e.g.: Vector3.Axis) // Here, we assume core types do not begin with underscore diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 0998d9f76a..b133923c25 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -107,9 +107,15 @@ class BindingsGenerator { TypeReference type; String name; - String default_argument; DefaultParamMode def_param_mode; + /** + * Determines the expression for the parameter default value. + * Formatting elements: + * %0 or %s: [cs_type] of the argument type + */ + String default_argument; + ArgumentInterface() { def_param_mode = CONSTANT; } @@ -175,6 +181,32 @@ class BindingsGenerator { } }; + struct SignalInterface { + String name; + StringName cname; + + /** + * Name of the C# method + */ + String proxy_name; + + List<ArgumentInterface> arguments; + + const DocData::MethodDoc *method_doc; + + bool is_deprecated; + String deprecation_message; + + void add_argument(const ArgumentInterface &argument) { + arguments.push_back(argument); + } + + SignalInterface() { + method_doc = NULL; + is_deprecated = false; + } + }; + struct TypeInterface { /** * Identifier name for this type. @@ -336,6 +368,7 @@ class BindingsGenerator { List<EnumInterface> enums; List<PropertyInterface> properties; List<MethodInterface> methods; + List<SignalInterface> signals_; const MethodInterface *find_method_by_name(const StringName &p_cname) const { for (const List<MethodInterface>::Element *E = methods.front(); E; E = E->next()) { @@ -364,6 +397,15 @@ class BindingsGenerator { return NULL; } + const MethodInterface *find_method_by_proxy_name(const String &p_proxy_name) const { + for (const List<MethodInterface>::Element *E = methods.front(); E; E = E->next()) { + if (E->get().proxy_name == p_proxy_name) + return &E->get(); + } + + return NULL; + } + private: static void _init_value_type(TypeInterface &itype) { itype.proxy_name = itype.name; @@ -524,6 +566,8 @@ class BindingsGenerator { StringName type_Reference; StringName type_RID; StringName type_String; + StringName type_StringName; + StringName type_NodePath; StringName type_at_GlobalScope; StringName enum_Error; @@ -548,6 +592,8 @@ class BindingsGenerator { type_Reference = StaticCString::create("Reference"); type_RID = StaticCString::create("RID"); type_String = StaticCString::create("String"); + type_StringName = StaticCString::create("StringName"); + type_NodePath = StaticCString::create("NodePath"); type_at_GlobalScope = StaticCString::create("@GlobalScope"); enum_Error = StaticCString::create("Error"); @@ -623,6 +669,7 @@ class BindingsGenerator { Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_iprop, StringBuilder &p_output); Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output); + Error _generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output); void _generate_global_constants(StringBuilder &p_output); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs index 3957387be9..39d5782db8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs @@ -2,7 +2,7 @@ using System; namespace Godot { - [AttributeUsage(AttributeTargets.Delegate)] + [AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event)] public class SignalAttribute : Attribute { } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs new file mode 100644 index 0000000000..c85cc1884c --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs @@ -0,0 +1,31 @@ +using System; + +namespace Godot +{ + public struct Callable + { + private readonly Object _target; + private readonly StringName _method; + private readonly Delegate _delegate; + + public Object Target => _target; + public StringName Method => _method; + public Delegate Delegate => _delegate; + + public static implicit operator Callable(Delegate @delegate) => new Callable(@delegate); + + public Callable(Object target, StringName method) + { + _target = target; + _method = method; + _delegate = null; + } + + public Callable(Delegate @delegate) + { + _target = null; + _method = null; + _delegate = @delegate; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs new file mode 100644 index 0000000000..785e87a043 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -0,0 +1,395 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace Godot +{ + internal static class DelegateUtils + { + private enum TargetKind : uint + { + Static, + GodotObject, + CompilerGenerated + } + + internal static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData) + { + if (@delegate is MulticastDelegate multicastDelegate) + { + bool someDelegatesSerialized = false; + + Delegate[] invocationList = multicastDelegate.GetInvocationList(); + + if (invocationList.Length > 1) + { + var multiCastData = new Collections.Array(); + + foreach (Delegate oneDelegate in invocationList) + someDelegatesSerialized |= TrySerializeDelegate(oneDelegate, multiCastData); + + if (!someDelegatesSerialized) + return false; + + serializedData.Add(multiCastData); + return true; + } + } + + if (TrySerializeSingleDelegate(@delegate, out byte[] buffer)) + { + serializedData.Add(buffer); + return true; + } + + return false; + } + + private static bool TrySerializeSingleDelegate(Delegate @delegate, out byte[] buffer) + { + buffer = null; + + object target = @delegate.Target; + + switch (target) + { + case null: + { + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + writer.Write((ulong) TargetKind.Static); + + SerializeType(writer, @delegate.GetType()); + + if (!TrySerializeMethodInfo(writer, @delegate.Method)) + return false; + + buffer = stream.ToArray(); + return true; + } + } + case Godot.Object godotObject: + { + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + writer.Write((ulong) TargetKind.GodotObject); + writer.Write((ulong) godotObject.GetInstanceId()); + + SerializeType(writer, @delegate.GetType()); + + if (!TrySerializeMethodInfo(writer, @delegate.Method)) + return false; + + buffer = stream.ToArray(); + return true; + } + } + default: + { + Type targetType = target.GetType(); + + if (targetType.GetCustomAttribute(typeof(CompilerGeneratedAttribute), true) != null) + { + // Compiler generated. Probably a closure. Try to serialize it. + + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + writer.Write((ulong) TargetKind.CompilerGenerated); + SerializeType(writer, targetType); + + SerializeType(writer, @delegate.GetType()); + + if (!TrySerializeMethodInfo(writer, @delegate.Method)) + return false; + + FieldInfo[] fields = targetType.GetFields(BindingFlags.Instance | BindingFlags.Public); + + writer.Write(fields.Length); + + foreach (FieldInfo field in fields) + { + Type fieldType = field.GetType(); + + Variant.Type variantType = GD.TypeToVariantType(fieldType); + + if (variantType == Variant.Type.Nil) + return false; + + writer.Write(field.Name); + byte[] valueBuffer = GD.Var2Bytes(field.GetValue(target)); + writer.Write(valueBuffer.Length); + writer.Write(valueBuffer); + } + + buffer = stream.ToArray(); + return true; + } + } + + return false; + } + } + } + + private static bool TrySerializeMethodInfo(BinaryWriter writer, MethodInfo methodInfo) + { + if (methodInfo == null) + return false; + + SerializeType(writer, methodInfo.DeclaringType); + + writer.Write(methodInfo.Name); + + int flags = 0; + + if (methodInfo.IsPublic) + flags |= (int) BindingFlags.Public; + else + flags |= (int) BindingFlags.NonPublic; + + if (methodInfo.IsStatic) + flags |= (int) BindingFlags.Static; + else + flags |= (int) BindingFlags.Instance; + + writer.Write(flags); + + Type returnType = methodInfo.ReturnType; + bool hasReturn = methodInfo.ReturnType != typeof(void); + + writer.Write(hasReturn); + if (hasReturn) + SerializeType(writer, returnType); + + ParameterInfo[] parameters = methodInfo.GetParameters(); + + writer.Write(parameters.Length); + + if (parameters.Length > 0) + { + for (int i = 0; i < parameters.Length; i++) + SerializeType(writer, parameters[i].ParameterType); + } + + return true; + } + + private static void SerializeType(BinaryWriter writer, Type type) + { + if (type == null) + { + int genericArgumentsCount = -1; + writer.Write(genericArgumentsCount); + } + else if (type.IsGenericType) + { + Type genericTypeDef = type.GetGenericTypeDefinition(); + Type[] genericArgs = type.GetGenericArguments(); + + int genericArgumentsCount = genericArgs.Length; + writer.Write(genericArgumentsCount); + + string assemblyQualifiedName = genericTypeDef.AssemblyQualifiedName; + Debug.Assert(assemblyQualifiedName != null); + writer.Write(assemblyQualifiedName); + + for (int i = 0; i < genericArgs.Length; i++) + SerializeType(writer, genericArgs[i]); + } + else + { + int genericArgumentsCount = 0; + writer.Write(genericArgumentsCount); + + string assemblyQualifiedName = type.AssemblyQualifiedName; + Debug.Assert(assemblyQualifiedName != null); + writer.Write(assemblyQualifiedName); + } + } + + private static bool TryDeserializeDelegate(Collections.Array serializedData, out Delegate @delegate) + { + if (serializedData.Count == 1) + { + object elem = serializedData[0]; + + if (elem is Collections.Array multiCastData) + return TryDeserializeDelegate(multiCastData, out @delegate); + + return TryDeserializeSingleDelegate((byte[])elem, out @delegate); + } + + @delegate = null; + + var delegates = new List<Delegate>(serializedData.Count); + + foreach (object elem in serializedData) + { + if (elem is Collections.Array multiCastData) + { + if (TryDeserializeDelegate(multiCastData, out Delegate oneDelegate)) + delegates.Add(oneDelegate); + } + else + { + if (TryDeserializeSingleDelegate((byte[]) elem, out Delegate oneDelegate)) + delegates.Add(oneDelegate); + } + } + + if (delegates.Count <= 0) + return false; + + @delegate = delegates.Count == 1 ? delegates[0] : Delegate.Combine(delegates.ToArray()); + return true; + } + + private static bool TryDeserializeSingleDelegate(byte[] buffer, out Delegate @delegate) + { + @delegate = null; + + using (var stream = new MemoryStream(buffer, writable: false)) + using (var reader = new BinaryReader(stream)) + { + var targetKind = (TargetKind) reader.ReadUInt64(); + + switch (targetKind) + { + case TargetKind.Static: + { + Type delegateType = DeserializeType(reader); + if (delegateType == null) + return false; + + if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo)) + return false; + + @delegate = Delegate.CreateDelegate(delegateType, null, methodInfo); + return true; + } + case TargetKind.GodotObject: + { + ulong objectId = reader.ReadUInt64(); + Godot.Object godotObject = GD.InstanceFromId(objectId); + if (godotObject == null) + return false; + + Type delegateType = DeserializeType(reader); + if (delegateType == null) + return false; + + if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo)) + return false; + + @delegate = Delegate.CreateDelegate(delegateType, godotObject, methodInfo); + return true; + } + case TargetKind.CompilerGenerated: + { + Type targetType = DeserializeType(reader); + if (targetType == null) + return false; + + Type delegateType = DeserializeType(reader); + if (delegateType == null) + return false; + + if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo)) + return false; + + int fieldCount = reader.ReadInt32(); + + object recreatedTarget = Activator.CreateInstance(targetType); + + for (int i = 0; i < fieldCount; i++) + { + string name = reader.ReadString(); + int valueBufferLength = reader.ReadInt32(); + byte[] valueBuffer = reader.ReadBytes(valueBufferLength); + + FieldInfo fieldInfo = targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public); + fieldInfo?.SetValue(recreatedTarget, GD.Bytes2Var(valueBuffer)); + } + + @delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo); + return true; + } + default: + return false; + } + } + } + + private static bool TryDeserializeMethodInfo(BinaryReader reader, out MethodInfo methodInfo) + { + methodInfo = null; + + Type declaringType = DeserializeType(reader); + + string methodName = reader.ReadString(); + + int flags = reader.ReadInt32(); + + bool hasReturn = reader.ReadBoolean(); + Type returnType = hasReturn ? DeserializeType(reader) : typeof(void); + + int parametersCount = reader.ReadInt32(); + + if (parametersCount > 0) + { + var parameterTypes = new Type[parametersCount]; + + for (int i = 0; i < parametersCount; i++) + { + Type parameterType = DeserializeType(reader); + if (parameterType == null) + return false; + parameterTypes[i] = parameterType; + } + + methodInfo = declaringType.GetMethod(methodName, (BindingFlags) flags, null, parameterTypes, null); + return methodInfo != null && methodInfo.ReturnType == returnType; + } + + methodInfo = declaringType.GetMethod(methodName, (BindingFlags) flags); + return methodInfo != null && methodInfo.ReturnType == returnType; + } + + private static Type DeserializeType(BinaryReader reader) + { + int genericArgumentsCount = reader.ReadInt32(); + + if (genericArgumentsCount == -1) + return null; + + string assemblyQualifiedName = reader.ReadString(); + var type = Type.GetType(assemblyQualifiedName); + + if (type == null) + return null; // Type not found + + if (genericArgumentsCount != 0) + { + var genericArgumentTypes = new Type[genericArgumentsCount]; + + for (int i = 0; i < genericArgumentsCount; i++) + { + Type genericArgumentType = DeserializeType(reader); + if (genericArgumentType == null) + return null; + genericArgumentTypes[i] = genericArgumentType; + } + + type = type.MakeGenericType(genericArgumentTypes); + } + + return type; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 2a9c2d73b1..9384da0e48 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using real_t = System.Double; #else using real_t = System.Single; + #endif // TODO: Add comments describing what this class does. It is not obvious. @@ -13,9 +14,9 @@ namespace Godot { public static partial class GD { - public static object Bytes2Var(byte[] bytes, bool allow_objects = false) + public static object Bytes2Var(byte[] bytes, bool allowObjects = false) { - return godot_icall_GD_bytes2var(bytes, allow_objects); + return godot_icall_GD_bytes2var(bytes, allowObjects); } public static object Convert(object what, Variant.Type type) @@ -25,7 +26,7 @@ namespace Godot public static real_t Db2Linear(real_t db) { - return (real_t)Math.Exp(db * 0.11512925464970228420089957273422); + return (real_t) Math.Exp(db * 0.11512925464970228420089957273422); } public static real_t DecTime(real_t value, real_t amount, real_t step) @@ -38,11 +39,11 @@ namespace Godot return val * sgn; } - public static FuncRef FuncRef(Object instance, string funcname) + public static FuncRef FuncRef(Object instance, StringName funcName) { var ret = new FuncRef(); ret.SetInstance(instance); - ret.SetFunction(funcname); + ret.SetFunction(funcName); return ret; } @@ -58,7 +59,7 @@ namespace Godot public static real_t Linear2Db(real_t linear) { - return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321); + return (real_t) (Math.Log(linear) * 8.6858896380650365530225783783321); } public static Resource Load(string path) @@ -181,14 +182,14 @@ namespace Godot return godot_icall_GD_str2var(str); } - public static bool TypeExists(string type) + public static bool TypeExists(StringName type) { - return godot_icall_GD_type_exists(type); + return godot_icall_GD_type_exists(StringName.GetPtr(type)); } - public static byte[] Var2Bytes(object var, bool full_objects = false) + public static byte[] Var2Bytes(object var, bool fullObjects = false) { - return godot_icall_GD_var2bytes(var, full_objects); + return godot_icall_GD_var2bytes(var, fullObjects); } public static string Var2Str(object var) @@ -196,8 +197,13 @@ namespace Godot return godot_icall_GD_var2str(var); } + public static Variant.Type TypeToVariantType(Type type) + { + return godot_icall_TypeToVariantType(type); + } + [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allow_objects); + internal extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allowObjects); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static object godot_icall_GD_convert(object what, Variant.Type type); @@ -206,7 +212,7 @@ namespace Godot internal extern static int godot_icall_GD_hash(object var); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static Object godot_icall_GD_instance_from_id(ulong instance_id); + internal extern static Object godot_icall_GD_instance_from_id(ulong instanceId); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_GD_print(object[] what); @@ -249,10 +255,10 @@ namespace Godot internal extern static object godot_icall_GD_str2var(string str); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_GD_type_exists(string type); + internal extern static bool godot_icall_GD_type_exists(IntPtr type); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static byte[] godot_icall_GD_var2bytes(object what, bool full_objects); + internal extern static byte[] godot_icall_GD_var2bytes(object what, bool fullObjects); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static string godot_icall_GD_var2str(object var); @@ -262,5 +268,8 @@ namespace Godot [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_GD_pushwarning(string type); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern Variant.Type godot_icall_TypeToVariantType(Type type); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index 8c5872ba5a..4ecc55f94e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -7,7 +7,7 @@ namespace Godot { private bool disposed = false; - internal IntPtr ptr; + private IntPtr ptr; internal static IntPtr GetPtr(NodePath instance) { @@ -50,104 +50,93 @@ namespace Godot this.ptr = ptr; } - public IntPtr NativeInstance - { - get { return ptr; } - } - public NodePath() : this(string.Empty) {} public NodePath(string path) { - this.ptr = godot_icall_NodePath_Ctor(path); + ptr = godot_icall_NodePath_Ctor(path); } - public static implicit operator NodePath(string from) - { - return new NodePath(from); - } + public static implicit operator NodePath(string from) => new NodePath(from); - public static implicit operator string(NodePath from) - { - return godot_icall_NodePath_operator_String(NodePath.GetPtr(from)); - } + public static implicit operator string(NodePath from) => from.ToString(); public override string ToString() { - return (string)this; + return godot_icall_NodePath_operator_String(GetPtr(this)); } public NodePath GetAsPropertyPath() { - return new NodePath(godot_icall_NodePath_get_as_property_path(NodePath.GetPtr(this))); + return new NodePath(godot_icall_NodePath_get_as_property_path(GetPtr(this))); } public string GetConcatenatedSubnames() { - return godot_icall_NodePath_get_concatenated_subnames(NodePath.GetPtr(this)); + return godot_icall_NodePath_get_concatenated_subnames(GetPtr(this)); } public string GetName(int idx) { - return godot_icall_NodePath_get_name(NodePath.GetPtr(this), idx); + return godot_icall_NodePath_get_name(GetPtr(this), idx); } public int GetNameCount() { - return godot_icall_NodePath_get_name_count(NodePath.GetPtr(this)); + return godot_icall_NodePath_get_name_count(GetPtr(this)); } public string GetSubname(int idx) { - return godot_icall_NodePath_get_subname(NodePath.GetPtr(this), idx); + return godot_icall_NodePath_get_subname(GetPtr(this), idx); } public int GetSubnameCount() { - return godot_icall_NodePath_get_subname_count(NodePath.GetPtr(this)); + return godot_icall_NodePath_get_subname_count(GetPtr(this)); } public bool IsAbsolute() { - return godot_icall_NodePath_is_absolute(NodePath.GetPtr(this)); + return godot_icall_NodePath_is_absolute(GetPtr(this)); } public bool IsEmpty() { - return godot_icall_NodePath_is_empty(NodePath.GetPtr(this)); + return godot_icall_NodePath_is_empty(GetPtr(this)); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_NodePath_Ctor(string path); + private static extern IntPtr godot_icall_NodePath_Ctor(string path); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_NodePath_Dtor(IntPtr ptr); + private static extern void godot_icall_NodePath_Dtor(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_NodePath_operator_String(IntPtr ptr); + private static extern string godot_icall_NodePath_operator_String(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr); + private static extern IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr); + private static extern string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_NodePath_get_name(IntPtr ptr, int arg1); + private static extern string godot_icall_NodePath_get_name(IntPtr ptr, int arg1); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_NodePath_get_name_count(IntPtr ptr); + private static extern int godot_icall_NodePath_get_name_count(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1); + private static extern string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_NodePath_get_subname_count(IntPtr ptr); + private static extern int godot_icall_NodePath_get_subname_count(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_NodePath_is_absolute(IntPtr ptr); + private static extern bool godot_icall_NodePath_is_absolute(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_NodePath_is_empty(IntPtr ptr); + private static extern bool godot_icall_NodePath_is_empty(IntPtr ptr); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index de80f7fddc..42610c5ef7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -7,7 +7,7 @@ namespace Godot { private bool disposed = false; - private const string nativeName = "Object"; + private static StringName nativeName = "Object"; internal IntPtr ptr; internal bool memoryOwn; @@ -15,7 +15,14 @@ namespace Godot public Object() : this(false) { if (ptr == IntPtr.Zero) + { ptr = godot_icall_Object_Ctor(this); + } + else + { + // This is called inside godot_icall_Object_Ctor, so we must call it as well in this case. + godot_icall_Object_ConnectEventSignals(ptr); + } } internal Object(bool memoryOwn) @@ -101,7 +108,7 @@ namespace Godot /// } /// </code> /// </example> - public SignalAwaiter ToSignal(Object source, string signal) + public SignalAwaiter ToSignal(Object source, StringName signal) { return new SignalAwaiter(source, signal, this); } @@ -111,20 +118,28 @@ namespace Godot /// </summary> public dynamic DynamicObject => new DynamicGodotObject(this); + internal static IntPtr __ClassDB_get_method(StringName type, string method) + { + return godot_icall_Object_ClassDB_get_method(StringName.GetPtr(type), method); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern IntPtr godot_icall_Object_Ctor(Object obj); + [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Object_Ctor(Object obj); + internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Object_Disposed(Object obj, IntPtr ptr); + internal static extern void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer); + internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_Object_ToString(IntPtr ptr); + internal static extern string godot_icall_Object_ToString(IntPtr ptr); // Used by the generated API [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Object_ClassDB_get_method(string type, string method); + internal static extern IntPtr godot_icall_Object_ClassDB_get_method(IntPtr type, string method); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs new file mode 100644 index 0000000000..bc2cad8713 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs @@ -0,0 +1,262 @@ +using System; +using System.Runtime.InteropServices; + +namespace Godot +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Rect2i : IEquatable<Rect2i> + { + private Vector2i _position; + private Vector2i _size; + + public Vector2i Position + { + get { return _position; } + set { _position = value; } + } + + public Vector2i Size + { + get { return _size; } + set { _size = value; } + } + + public Vector2i End + { + get { return _position + _size; } + set { _size = value - _position; } + } + + public int Area + { + get { return GetArea(); } + } + + public Rect2i Abs() + { + Vector2i end = End; + Vector2i topLeft = new Vector2i(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y)); + return new Rect2i(topLeft, _size.Abs()); + } + + public Rect2i Clip(Rect2i b) + { + var newRect = b; + + if (!Intersects(newRect)) + return new Rect2i(); + + newRect._position.x = Mathf.Max(b._position.x, _position.x); + newRect._position.y = Mathf.Max(b._position.y, _position.y); + + Vector2i bEnd = b._position + b._size; + Vector2i end = _position + _size; + + newRect._size.x = Mathf.Min(bEnd.x, end.x) - newRect._position.x; + newRect._size.y = Mathf.Min(bEnd.y, end.y) - newRect._position.y; + + return newRect; + } + + public bool Encloses(Rect2i b) + { + return b._position.x >= _position.x && b._position.y >= _position.y && + b._position.x + b._size.x < _position.x + _size.x && + b._position.y + b._size.y < _position.y + _size.y; + } + + public Rect2i Expand(Vector2i to) + { + var expanded = this; + + Vector2i begin = expanded._position; + Vector2i end = expanded._position + expanded._size; + + if (to.x < begin.x) + begin.x = to.x; + if (to.y < begin.y) + begin.y = to.y; + + if (to.x > end.x) + end.x = to.x; + if (to.y > end.y) + end.y = to.y; + + expanded._position = begin; + expanded._size = end - begin; + + return expanded; + } + + public int GetArea() + { + return _size.x * _size.y; + } + + public Rect2i Grow(int by) + { + var g = this; + + g._position.x -= by; + g._position.y -= by; + g._size.x += by * 2; + g._size.y += by * 2; + + return g; + } + + public Rect2i GrowIndividual(int left, int top, int right, int bottom) + { + var g = this; + + g._position.x -= left; + g._position.y -= top; + g._size.x += left + right; + g._size.y += top + bottom; + + return g; + } + + public Rect2i GrowMargin(Margin margin, int by) + { + var g = this; + + g.GrowIndividual(Margin.Left == margin ? by : 0, + Margin.Top == margin ? by : 0, + Margin.Right == margin ? by : 0, + Margin.Bottom == margin ? by : 0); + + return g; + } + + public bool HasNoArea() + { + return _size.x <= 0 || _size.y <= 0; + } + + public bool HasPoint(Vector2i point) + { + if (point.x < _position.x) + return false; + if (point.y < _position.y) + return false; + + if (point.x >= _position.x + _size.x) + return false; + if (point.y >= _position.y + _size.y) + return false; + + return true; + } + + public bool Intersects(Rect2i b) + { + if (_position.x >= b._position.x + b._size.x) + return false; + if (_position.x + _size.x <= b._position.x) + return false; + if (_position.y >= b._position.y + b._size.y) + return false; + if (_position.y + _size.y <= b._position.y) + return false; + + return true; + } + + public Rect2i Merge(Rect2i b) + { + Rect2i newRect; + + newRect._position.x = Mathf.Min(b._position.x, _position.x); + newRect._position.y = Mathf.Min(b._position.y, _position.y); + + newRect._size.x = Mathf.Max(b._position.x + b._size.x, _position.x + _size.x); + newRect._size.y = Mathf.Max(b._position.y + b._size.y, _position.y + _size.y); + + newRect._size = newRect._size - newRect._position; // Make relative again + + return newRect; + } + + // Constructors + public Rect2i(Vector2i position, Vector2i size) + { + _position = position; + _size = size; + } + public Rect2i(Vector2i position, int width, int height) + { + _position = position; + _size = new Vector2i(width, height); + } + public Rect2i(int x, int y, Vector2i size) + { + _position = new Vector2i(x, y); + _size = size; + } + public Rect2i(int x, int y, int width, int height) + { + _position = new Vector2i(x, y); + _size = new Vector2i(width, height); + } + + public static bool operator ==(Rect2i left, Rect2i right) + { + return left.Equals(right); + } + + public static bool operator !=(Rect2i left, Rect2i right) + { + return !left.Equals(right); + } + + public static implicit operator Rect2(Rect2i value) + { + return new Rect2(value._position, value._size); + } + + public static explicit operator Rect2i(Rect2 value) + { + return new Rect2i((Vector2i)value.Position, (Vector2i)value.Size); + } + + public override bool Equals(object obj) + { + if (obj is Rect2i) + { + return Equals((Rect2i)obj); + } + + return false; + } + + public bool Equals(Rect2i other) + { + return _position.Equals(other._position) && _size.Equals(other._size); + } + + public override int GetHashCode() + { + return _position.GetHashCode() ^ _size.GetHashCode(); + } + + public override string ToString() + { + return String.Format("{0}, {1}", new object[] + { + _position.ToString(), + _size.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("{0}, {1}", new object[] + { + _position.ToString(format), + _size.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs index 9483b6ffb4..4dc630238b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -9,13 +9,13 @@ namespace Godot private object[] result; private Action action; - public SignalAwaiter(Object source, string signal, Object target) + public SignalAwaiter(Object source, StringName signal, Object target) { - godot_icall_SignalAwaiter_connect(Object.GetPtr(source), signal, Object.GetPtr(target), this); + godot_icall_SignalAwaiter_connect(Object.GetPtr(source), StringName.GetPtr(signal), Object.GetPtr(target), this); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static Error godot_icall_SignalAwaiter_connect(IntPtr source, string signal, IntPtr target, SignalAwaiter awaiter); + internal extern static Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter); public bool IsCompleted { @@ -50,11 +50,5 @@ namespace Godot action(); } } - - internal void FailureCallback() - { - action = null; - completed = true; - } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs new file mode 100644 index 0000000000..dc92de7a61 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs @@ -0,0 +1,17 @@ +namespace Godot +{ + public struct SignalInfo + { + private readonly Object _owner; + private readonly StringName _signalName; + + public Object Owner => _owner; + public StringName Name => _signalName; + + public SignalInfo(Object owner, StringName name) + { + _owner = owner; + _signalName = name; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs new file mode 100644 index 0000000000..7700b6d4ed --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -0,0 +1,82 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public sealed partial class StringName : IDisposable + { + private IntPtr ptr; + + internal static IntPtr GetPtr(StringName instance) + { + if (instance == null) + throw new NullReferenceException($"The instance of type {nameof(StringName)} is null."); + + if (instance.ptr == IntPtr.Zero) + throw new ObjectDisposedException(instance.GetType().FullName); + + return instance.ptr; + } + + ~StringName() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (ptr != IntPtr.Zero) + { + godot_icall_StringName_Dtor(ptr); + ptr = IntPtr.Zero; + } + } + + internal StringName(IntPtr ptr) + { + this.ptr = ptr; + } + + public StringName() + { + ptr = IntPtr.Zero; + } + + public StringName(string path) + { + ptr = path == null ? IntPtr.Zero : godot_icall_StringName_Ctor(path); + } + + public static implicit operator StringName(string from) => new StringName(from); + + public static implicit operator string(StringName from) => from.ToString(); + + public override string ToString() + { + return ptr == IntPtr.Zero ? string.Empty : godot_icall_StringName_operator_String(GetPtr(this)); + } + + public bool IsEmpty() + { + return ptr == IntPtr.Zero || godot_icall_StringName_is_empty(GetPtr(this)); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern IntPtr godot_icall_StringName_Ctor(string path); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void godot_icall_StringName_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string godot_icall_StringName_operator_String(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool godot_icall_StringName_is_empty(IntPtr ptr); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index 385bfed122..f7b13198f8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -76,11 +76,6 @@ namespace Godot } } - public real_t Cross(Vector2 b) - { - return x * b.y - y * b.x; - } - public Vector2 Abs() { return new Vector2(Mathf.Abs(x), Mathf.Abs(y)); @@ -130,6 +125,11 @@ namespace Godot return v; } + public real_t Cross(Vector2 b) + { + return x * b.y - y * b.x; + } + public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t t) { var p0 = preA; @@ -234,7 +234,7 @@ namespace Godot public Vector2 Reflect(Vector2 n) { - return 2.0f * n * Dot(n) - this; + return 2 * Dot(n) * n - this; } public Vector2 Rotated(real_t phi) @@ -352,18 +352,18 @@ namespace Godot return left; } - public static Vector2 operator /(Vector2 vec, real_t scale) + public static Vector2 operator /(Vector2 vec, real_t divisor) { - vec.x /= scale; - vec.y /= scale; + vec.x /= divisor; + vec.y /= divisor; return vec; } - public static Vector2 operator /(Vector2 left, Vector2 right) + public static Vector2 operator /(Vector2 vec, Vector2 divisorv) { - left.x /= right.x; - left.y /= right.y; - return left; + vec.x /= divisorv.x; + vec.y /= divisorv.y; + return vec; } public static Vector2 operator %(Vector2 vec, real_t divisor) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs new file mode 100644 index 0000000000..7dc22d7918 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs @@ -0,0 +1,380 @@ +using System; +using System.Runtime.InteropServices; + +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + /// <summary> + /// 2-element structure that can be used to represent 2D grid coordinates or pairs of integers. + /// </summary> + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Vector2i : IEquatable<Vector2i> + { + public enum Axis + { + X = 0, + Y + } + + public int x; + public int y; + + public int this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + public Vector2i Abs() + { + return new Vector2i(Mathf.Abs(x), Mathf.Abs(y)); + } + + public real_t Angle() + { + return Mathf.Atan2(y, x); + } + + public real_t AngleTo(Vector2i to) + { + return Mathf.Atan2(Cross(to), Dot(to)); + } + + public real_t AngleToPoint(Vector2i to) + { + return Mathf.Atan2(y - to.y, x - to.x); + } + + public real_t Aspect() + { + return x / (real_t)y; + } + + public Vector2i Bounce(Vector2i n) + { + return -Reflect(n); + } + + public int Cross(Vector2i b) + { + return x * b.y - y * b.x; + } + + public int DistanceSquaredTo(Vector2i b) + { + return (b - this).LengthSquared(); + } + + public real_t DistanceTo(Vector2i b) + { + return (b - this).Length(); + } + + public int Dot(Vector2i b) + { + return x * b.x + y * b.y; + } + + public real_t Length() + { + int x2 = x * x; + int y2 = y * y; + + return Mathf.Sqrt(x2 + y2); + } + + public int LengthSquared() + { + int x2 = x * x; + int y2 = y * y; + + return x2 + y2; + } + + public Axis MaxAxis() + { + return x < y ? Axis.Y : Axis.X; + } + + public Axis MinAxis() + { + return x > y ? Axis.Y : Axis.X; + } + + public Vector2i PosMod(int mod) + { + Vector2i v = this; + v.x = Mathf.PosMod(v.x, mod); + v.y = Mathf.PosMod(v.y, mod); + return v; + } + + public Vector2i PosMod(Vector2i modv) + { + Vector2i v = this; + v.x = Mathf.PosMod(v.x, modv.x); + v.y = Mathf.PosMod(v.y, modv.y); + return v; + } + + public Vector2i Reflect(Vector2i n) + { + return 2 * Dot(n) * n - this; + } + + public Vector2i Sign() + { + Vector2i v = this; + v.x = Mathf.Sign(v.x); + v.y = Mathf.Sign(v.y); + return v; + } + + public Vector2i Tangent() + { + return new Vector2i(y, -x); + } + + // Constants + private static readonly Vector2i _zero = new Vector2i(0, 0); + private static readonly Vector2i _one = new Vector2i(1, 1); + + private static readonly Vector2i _up = new Vector2i(0, -1); + private static readonly Vector2i _down = new Vector2i(0, 1); + private static readonly Vector2i _right = new Vector2i(1, 0); + private static readonly Vector2i _left = new Vector2i(-1, 0); + + public static Vector2i Zero { get { return _zero; } } + public static Vector2i One { get { return _one; } } + + public static Vector2i Up { get { return _up; } } + public static Vector2i Down { get { return _down; } } + public static Vector2i Right { get { return _right; } } + public static Vector2i Left { get { return _left; } } + + // Constructors + public Vector2i(int x, int y) + { + this.x = x; + this.y = y; + } + public Vector2i(Vector2i vi) + { + this.x = vi.x; + this.y = vi.y; + } + public Vector2i(Vector2 v) + { + this.x = Mathf.RoundToInt(v.x); + this.y = Mathf.RoundToInt(v.y); + } + + public static Vector2i operator +(Vector2i left, Vector2i right) + { + left.x += right.x; + left.y += right.y; + return left; + } + + public static Vector2i operator -(Vector2i left, Vector2i right) + { + left.x -= right.x; + left.y -= right.y; + return left; + } + + public static Vector2i operator -(Vector2i vec) + { + vec.x = -vec.x; + vec.y = -vec.y; + return vec; + } + + public static Vector2i operator *(Vector2i vec, int scale) + { + vec.x *= scale; + vec.y *= scale; + return vec; + } + + public static Vector2i operator *(int scale, Vector2i vec) + { + vec.x *= scale; + vec.y *= scale; + return vec; + } + + public static Vector2i operator *(Vector2i left, Vector2i right) + { + left.x *= right.x; + left.y *= right.y; + return left; + } + + public static Vector2i operator /(Vector2i vec, int divisor) + { + vec.x /= divisor; + vec.y /= divisor; + return vec; + } + + public static Vector2i operator /(Vector2i vec, Vector2i divisorv) + { + vec.x /= divisorv.x; + vec.y /= divisorv.y; + return vec; + } + + public static Vector2i operator %(Vector2i vec, int divisor) + { + vec.x %= divisor; + vec.y %= divisor; + return vec; + } + + public static Vector2i operator %(Vector2i vec, Vector2i divisorv) + { + vec.x %= divisorv.x; + vec.y %= divisorv.y; + return vec; + } + + public static Vector2i operator &(Vector2i vec, int and) + { + vec.x &= and; + vec.y &= and; + return vec; + } + + public static Vector2i operator &(Vector2i vec, Vector2i andv) + { + vec.x &= andv.x; + vec.y &= andv.y; + return vec; + } + + public static bool operator ==(Vector2i left, Vector2i right) + { + return left.Equals(right); + } + + public static bool operator !=(Vector2i left, Vector2i right) + { + return !left.Equals(right); + } + + public static bool operator <(Vector2i left, Vector2i right) + { + if (left.x.Equals(right.x)) + { + return left.y < right.y; + } + return left.x < right.x; + } + + public static bool operator >(Vector2i left, Vector2i right) + { + if (left.x.Equals(right.x)) + { + return left.y > right.y; + } + return left.x > right.x; + } + + public static bool operator <=(Vector2i left, Vector2i right) + { + if (left.x.Equals(right.x)) + { + return left.y <= right.y; + } + return left.x <= right.x; + } + + public static bool operator >=(Vector2i left, Vector2i right) + { + if (left.x.Equals(right.x)) + { + return left.y >= right.y; + } + return left.x >= right.x; + } + + public static implicit operator Vector2(Vector2i value) + { + return new Vector2(value.x, value.y); + } + + public static explicit operator Vector2i(Vector2 value) + { + return new Vector2i(value); + } + + public override bool Equals(object obj) + { + if (obj is Vector2i) + { + return Equals((Vector2i)obj); + } + + return false; + } + + public bool Equals(Vector2i other) + { + return x == other.x && y == other.y; + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ x.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1})", new object[] + { + this.x.ToString(), + this.y.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1})", new object[] + { + this.x.ToString(format), + this.y.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index 390036c654..a43836e985 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -400,20 +400,20 @@ namespace Godot return left; } - public static Vector3 operator /(Vector3 vec, real_t scale) + public static Vector3 operator /(Vector3 vec, real_t divisor) { - vec.x /= scale; - vec.y /= scale; - vec.z /= scale; + vec.x /= divisor; + vec.y /= divisor; + vec.z /= divisor; return vec; } - public static Vector3 operator /(Vector3 left, Vector3 right) + public static Vector3 operator /(Vector3 vec, Vector3 divisorv) { - left.x /= right.x; - left.y /= right.y; - left.z /= right.z; - return left; + vec.x /= divisorv.x; + vec.y /= divisorv.y; + vec.z /= divisorv.z; + return vec; } public static Vector3 operator %(Vector3 vec, real_t divisor) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs new file mode 100644 index 0000000000..c17f900131 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs @@ -0,0 +1,402 @@ +using System; +using System.Runtime.InteropServices; + +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + /// <summary> + /// 3-element structure that can be used to represent 3D grid coordinates or sets of integers. + /// </summary> + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Vector3i : IEquatable<Vector3i> + { + public enum Axis + { + X = 0, + Y, + Z + } + + public int x; + public int y; + public int z; + + public int this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + case 2: + z = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + public Vector3i Abs() + { + Vector3i v = this; + if (v.x < 0) + { + v.x = -v.x; + } + if (v.y < 0) + { + v.y = -v.y; + } + if (v.z < 0) + { + v.z = -v.z; + } + return v; + } + + public int DistanceSquaredTo(Vector3i b) + { + return (b - this).LengthSquared(); + } + + public real_t DistanceTo(Vector3i b) + { + return (b - this).Length(); + } + + public int Dot(Vector3i b) + { + return x * b.x + y * b.y + z * b.z; + } + + public real_t Length() + { + int x2 = x * x; + int y2 = y * y; + int z2 = z * z; + + return Mathf.Sqrt(x2 + y2 + z2); + } + + public int LengthSquared() + { + int x2 = x * x; + int y2 = y * y; + int z2 = z * z; + + return x2 + y2 + z2; + } + + public Axis MaxAxis() + { + return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X); + } + + public Axis MinAxis() + { + return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z); + } + + public Vector3i PosMod(int mod) + { + Vector3i v = this; + v.x = Mathf.PosMod(v.x, mod); + v.y = Mathf.PosMod(v.y, mod); + v.z = Mathf.PosMod(v.z, mod); + return v; + } + + public Vector3i PosMod(Vector3i modv) + { + Vector3i v = this; + v.x = Mathf.PosMod(v.x, modv.x); + v.y = Mathf.PosMod(v.y, modv.y); + v.z = Mathf.PosMod(v.z, modv.z); + return v; + } + + public Vector3i Sign() + { + Vector3i v = this; + v.x = Mathf.Sign(v.x); + v.y = Mathf.Sign(v.y); + v.z = Mathf.Sign(v.z); + return v; + } + + // Constants + private static readonly Vector3i _zero = new Vector3i(0, 0, 0); + private static readonly Vector3i _one = new Vector3i(1, 1, 1); + + private static readonly Vector3i _up = new Vector3i(0, 1, 0); + private static readonly Vector3i _down = new Vector3i(0, -1, 0); + private static readonly Vector3i _right = new Vector3i(1, 0, 0); + private static readonly Vector3i _left = new Vector3i(-1, 0, 0); + private static readonly Vector3i _forward = new Vector3i(0, 0, -1); + private static readonly Vector3i _back = new Vector3i(0, 0, 1); + + public static Vector3i Zero { get { return _zero; } } + public static Vector3i One { get { return _one; } } + + public static Vector3i Up { get { return _up; } } + public static Vector3i Down { get { return _down; } } + public static Vector3i Right { get { return _right; } } + public static Vector3i Left { get { return _left; } } + public static Vector3i Forward { get { return _forward; } } + public static Vector3i Back { get { return _back; } } + + // Constructors + public Vector3i(int x, int y, int z) + { + this.x = x; + this.y = y; + this.z = z; + } + public Vector3i(Vector3i vi) + { + this.x = vi.x; + this.y = vi.y; + this.z = vi.z; + } + public Vector3i(Vector3 v) + { + this.x = Mathf.RoundToInt(v.x); + this.y = Mathf.RoundToInt(v.y); + this.z = Mathf.RoundToInt(v.z); + } + + public static Vector3i operator +(Vector3i left, Vector3i right) + { + left.x += right.x; + left.y += right.y; + left.z += right.z; + return left; + } + + public static Vector3i operator -(Vector3i left, Vector3i right) + { + left.x -= right.x; + left.y -= right.y; + left.z -= right.z; + return left; + } + + public static Vector3i operator -(Vector3i vec) + { + vec.x = -vec.x; + vec.y = -vec.y; + vec.z = -vec.z; + return vec; + } + + public static Vector3i operator *(Vector3i vec, int scale) + { + vec.x *= scale; + vec.y *= scale; + vec.z *= scale; + return vec; + } + + public static Vector3i operator *(int scale, Vector3i vec) + { + vec.x *= scale; + vec.y *= scale; + vec.z *= scale; + return vec; + } + + public static Vector3i operator *(Vector3i left, Vector3i right) + { + left.x *= right.x; + left.y *= right.y; + left.z *= right.z; + return left; + } + + public static Vector3i operator /(Vector3i vec, int divisor) + { + vec.x /= divisor; + vec.y /= divisor; + vec.z /= divisor; + return vec; + } + + public static Vector3i operator /(Vector3i vec, Vector3i divisorv) + { + vec.x /= divisorv.x; + vec.y /= divisorv.y; + vec.z /= divisorv.z; + return vec; + } + + public static Vector3i operator %(Vector3i vec, int divisor) + { + vec.x %= divisor; + vec.y %= divisor; + vec.z %= divisor; + return vec; + } + + public static Vector3i operator %(Vector3i vec, Vector3i divisorv) + { + vec.x %= divisorv.x; + vec.y %= divisorv.y; + vec.z %= divisorv.z; + return vec; + } + + public static Vector3i operator &(Vector3i vec, int and) + { + vec.x &= and; + vec.y &= and; + vec.z &= and; + return vec; + } + + public static Vector3i operator &(Vector3i vec, Vector3i andv) + { + vec.x &= andv.x; + vec.y &= andv.y; + vec.z &= andv.z; + return vec; + } + + public static bool operator ==(Vector3i left, Vector3i right) + { + return left.Equals(right); + } + + public static bool operator !=(Vector3i left, Vector3i right) + { + return !left.Equals(right); + } + + public static bool operator <(Vector3i left, Vector3i right) + { + if (left.x == right.x) + { + if (left.y == right.y) + return left.z < right.z; + else + return left.y < right.y; + } + + return left.x < right.x; + } + + public static bool operator >(Vector3i left, Vector3i right) + { + if (left.x == right.x) + { + if (left.y == right.y) + return left.z > right.z; + else + return left.y > right.y; + } + + return left.x > right.x; + } + + public static bool operator <=(Vector3i left, Vector3i right) + { + if (left.x == right.x) + { + if (left.y == right.y) + return left.z <= right.z; + else + return left.y < right.y; + } + + return left.x < right.x; + } + + public static bool operator >=(Vector3i left, Vector3i right) + { + if (left.x == right.x) + { + if (left.y == right.y) + return left.z >= right.z; + else + return left.y > right.y; + } + + return left.x > right.x; + } + + public static implicit operator Vector3(Vector3i value) + { + return new Vector3(value.x, value.y, value.z); + } + + public static explicit operator Vector3i(Vector3 value) + { + return new Vector3i(value); + } + + public override bool Equals(object obj) + { + if (obj is Vector3i) + { + return Equals((Vector3i)obj); + } + + return false; + } + + public bool Equals(Vector3i other) + { + return x == other.x && y == other.y && z == other.z; + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1}, {2})", new object[] + { + this.x.ToString(), + this.y.ToString(), + this.z.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1}, {2})", new object[] + { + this.x.ToString(format), + this.y.ToString(format), + this.z.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 5419cd06e6..ba0bbd7630 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -41,9 +41,11 @@ <Compile Include="Core\Attributes\SignalAttribute.cs" /> <Compile Include="Core\Attributes\ToolAttribute.cs" /> <Compile Include="Core\Basis.cs" /> + <Compile Include="Core\Callable.cs" /> <Compile Include="Core\Color.cs" /> <Compile Include="Core\Colors.cs" /> <Compile Include="Core\DebuggingUtils.cs" /> + <Compile Include="Core\DelegateUtils.cs" /> <Compile Include="Core\Dictionary.cs" /> <Compile Include="Core\Dispatcher.cs" /> <Compile Include="Core\DynamicObject.cs" /> @@ -65,13 +67,18 @@ <Compile Include="Core\Plane.cs" /> <Compile Include="Core\Quat.cs" /> <Compile Include="Core\Rect2.cs" /> + <Compile Include="Core\Rect2i.cs" /> <Compile Include="Core\RID.cs" /> + <Compile Include="Core\SignalInfo.cs" /> <Compile Include="Core\SignalAwaiter.cs" /> <Compile Include="Core\StringExtensions.cs" /> + <Compile Include="Core\StringName.cs" /> <Compile Include="Core\Transform.cs" /> <Compile Include="Core\Transform2D.cs" /> <Compile Include="Core\Vector2.cs" /> + <Compile Include="Core\Vector2i.cs" /> <Compile Include="Core\Vector3.cs" /> + <Compile Include="Core\Vector3i.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> <!-- diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index 8c77220b85..120668d1ef 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -70,8 +70,8 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { if (data) { CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); if (script_binding.inited) { - Ref<MonoGCHandle> &gchandle = script_binding.gchandle; - if (gchandle.is_valid()) { + MonoGCHandleData &gchandle = script_binding.gchandle; + if (!gchandle.is_released()) { CSharpLanguage::release_script_gchandle(p_obj, gchandle); } } @@ -117,8 +117,8 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolea if (data) { CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); if (script_binding.inited) { - Ref<MonoGCHandle> &gchandle = script_binding.gchandle; - if (gchandle.is_valid()) { + MonoGCHandleData &gchandle = script_binding.gchandle; + if (!gchandle.is_released()) { CSharpLanguage::release_script_gchandle(p_obj, gchandle); } } @@ -126,18 +126,25 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolea } } -MethodBind *godot_icall_Object_ClassDB_get_method(MonoString *p_type, MonoString *p_method) { - StringName type(GDMonoMarshal::mono_string_to_godot(p_type)); +void godot_icall_Object_ConnectEventSignals(Object *p_ptr) { + CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance()); + if (csharp_instance) { + csharp_instance->connect_event_signals(); + } +} + +MethodBind *godot_icall_Object_ClassDB_get_method(StringName *p_type, MonoString *p_method) { + StringName type = p_type ? *p_type : StringName(); StringName method(GDMonoMarshal::mono_string_to_godot(p_method)); return ClassDB::get_method(type, method); } -MonoObject *godot_icall_Object_weakref(Object *p_obj) { - if (!p_obj) +MonoObject *godot_icall_Object_weakref(Object *p_ptr) { + if (!p_ptr) return NULL; Ref<WeakRef> wref; - Reference *ref = Object::cast_to<Reference>(p_obj); + Reference *ref = Object::cast_to<Reference>(p_ptr); if (ref) { REF r = ref; @@ -148,15 +155,15 @@ MonoObject *godot_icall_Object_weakref(Object *p_obj) { wref->set_ref(r); } else { wref.instance(); - wref->set_obj(p_obj); + wref->set_obj(p_ptr); } return GDMonoUtils::unmanaged_get_managed(wref.ptr()); } -Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter) { - String signal = GDMonoMarshal::mono_string_to_godot(p_signal); - return SignalAwaiterUtils::connect_signal_awaiter(p_source, signal, p_target, p_awaiter); +Error godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) { + StringName signal = p_signal ? *p_signal : StringName(); + return gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter); } MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) { @@ -225,8 +232,8 @@ MonoString *godot_icall_Object_ToString(Object *p_ptr) { // Cannot happen in C#; would get an ObjectDisposedException instead. CRASH_COND(p_ptr == NULL); #endif - - String result = p_ptr->to_string(); + // Can't call 'Object::to_string()' here, as that can end up calling 'ToString' again resulting in an endless circular loop. + String result = "[" + p_ptr->get_class() + ":" + itos(p_ptr->get_instance_id()) + "]"; return GDMonoMarshal::mono_string_from_godot(result); } diff --git a/modules/mono/glue/base_object_glue.h b/modules/mono/glue/base_object_glue.h index 22532dcff9..67769f3061 100644 --- a/modules/mono/glue/base_object_glue.h +++ b/modules/mono/glue/base_object_glue.h @@ -44,11 +44,13 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr); void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer); -MethodBind *godot_icall_Object_ClassDB_get_method(MonoString *p_type, MonoString *p_method); +void godot_icall_Object_ConnectEventSignals(Object *p_ptr); -MonoObject *godot_icall_Object_weakref(Object *p_obj); +MethodBind *godot_icall_Object_ClassDB_get_method(StringName *p_type, MonoString *p_method); -Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter); +MonoObject *godot_icall_Object_weakref(Object *p_ptr); + +Error godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter); // DynamicGodotObject diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp index cdacd90538..1576d31a3b 100644 --- a/modules/mono/glue/gd_glue.cpp +++ b/modules/mono/glue/gd_glue.cpp @@ -241,8 +241,9 @@ MonoObject *godot_icall_GD_str2var(MonoString *p_str) { return GDMonoMarshal::variant_to_mono_object(ret); } -MonoBoolean godot_icall_GD_type_exists(MonoString *p_type) { - return ClassDB::class_exists(GDMonoMarshal::mono_string_to_godot(p_type)); +MonoBoolean godot_icall_GD_type_exists(StringName *p_type) { + StringName type = p_type ? *p_type : StringName(); + return ClassDB::class_exists(type); } void godot_icall_GD_pusherror(MonoString *p_str) { @@ -273,6 +274,10 @@ MonoString *godot_icall_GD_var2str(MonoObject *p_var) { return GDMonoMarshal::mono_string_from_godot(vars); } +uint32_t godot_icall_TypeToVariantType(MonoReflectionType *p_refl_type) { + return (uint32_t)GDMonoMarshal::managed_to_variant_type(ManagedType::from_reftype(p_refl_type)); +} + MonoObject *godot_icall_DefaultGodotTaskScheduler() { return GDMonoCache::cached_data.task_scheduler_handle->get_target(); } @@ -300,6 +305,7 @@ void godot_register_gd_icalls() { mono_add_internal_call("Godot.GD::godot_icall_GD_type_exists", (void *)godot_icall_GD_type_exists); mono_add_internal_call("Godot.GD::godot_icall_GD_var2bytes", (void *)godot_icall_GD_var2bytes); mono_add_internal_call("Godot.GD::godot_icall_GD_var2str", (void *)godot_icall_GD_var2str); + mono_add_internal_call("Godot.GD::godot_icall_TypeToVariantType", (void *)godot_icall_TypeToVariantType); // Dispatcher mono_add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", (void *)godot_icall_DefaultGodotTaskScheduler); diff --git a/modules/mono/glue/gd_glue.h b/modules/mono/glue/gd_glue.h index f00e2efc5d..3ad6058205 100644 --- a/modules/mono/glue/gd_glue.h +++ b/modules/mono/glue/gd_glue.h @@ -69,7 +69,7 @@ MonoString *godot_icall_GD_str(MonoArray *p_what); MonoObject *godot_icall_GD_str2var(MonoString *p_str); -MonoBoolean godot_icall_GD_type_exists(MonoString *p_type); +MonoBoolean godot_icall_GD_type_exists(StringName *p_type); MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects); diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h index 758b71f719..8130b0cc39 100644 --- a/modules/mono/glue/glue_header.h +++ b/modules/mono/glue/glue_header.h @@ -36,6 +36,7 @@ #include "nodepath_glue.h" #include "rid_glue.h" #include "string_glue.h" +#include "string_name_glue.h" /** * Registers internal calls that were not generated. This function is called @@ -44,6 +45,7 @@ void godot_register_glue_header_icalls() { godot_register_collections_icalls(); godot_register_gd_icalls(); + godot_register_string_name_icalls(); godot_register_nodepath_icalls(); godot_register_object_icalls(); godot_register_rid_icalls(); diff --git a/modules/opus/stub/register_types.cpp b/modules/mono/glue/string_name_glue.cpp index a4329e142c..81006e5849 100644 --- a/modules/opus/stub/register_types.cpp +++ b/modules/mono/glue/string_name_glue.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* register_types.cpp */ +/* string_name_glue.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,10 +28,34 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "register_types.h" +#include "string_name_glue.h" -// Dummy module as libvorbis is needed by other modules (theora ...) +#ifdef MONO_GLUE_ENABLED -void register_opus_types() {} +#include "core/ustring.h" -void unregister_opus_types() {} +StringName *godot_icall_StringName_Ctor(MonoString *p_path) { + return memnew(StringName(GDMonoMarshal::mono_string_to_godot(p_path))); +} + +void godot_icall_StringName_Dtor(StringName *p_ptr) { + ERR_FAIL_NULL(p_ptr); + memdelete(p_ptr); +} + +MonoString *godot_icall_StringName_operator_String(StringName *p_np) { + return GDMonoMarshal::mono_string_from_godot(p_np->operator String()); +} + +MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr) { + return (MonoBoolean)(p_ptr == StringName()); +} + +void godot_register_string_name_icalls() { + mono_add_internal_call("Godot.StringName::godot_icall_StringName_Ctor", (void *)godot_icall_StringName_Ctor); + mono_add_internal_call("Godot.StringName::godot_icall_StringName_Dtor", (void *)godot_icall_StringName_Dtor); + mono_add_internal_call("Godot.StringName::godot_icall_StringName_operator_String", (void *)godot_icall_StringName_operator_String); + mono_add_internal_call("Godot.StringName::godot_icall_StringName_is_empty", (void *)godot_icall_StringName_is_empty); +} + +#endif // MONO_GLUE_ENABLED diff --git a/modules/vorbis/stub/register_types.cpp b/modules/mono/glue/string_name_glue.h index 8874b3887b..88354ddd84 100644 --- a/modules/vorbis/stub/register_types.cpp +++ b/modules/mono/glue/string_name_glue.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* register_types.cpp */ +/* string_name_glue.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,10 +28,27 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "register_types.h" +#ifndef STRING_NAME_GLUE_H +#define STRING_NAME_GLUE_H -// Dummy module as libvorbis is needed by other modules (theora ...) +#ifdef MONO_GLUE_ENABLED -void register_vorbis_types() {} +#include "core/string_name.h" -void unregister_vorbis_types() {} +#include "../mono_gd/gd_mono_marshal.h" + +StringName *godot_icall_StringName_Ctor(MonoString *p_path); + +void godot_icall_StringName_Dtor(StringName *p_ptr); + +MonoString *godot_icall_StringName_operator_String(StringName *p_np); + +MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr); + +// Register internal calls + +void godot_register_string_name_icalls(); + +#endif // MONO_GLUE_ENABLED + +#endif // STRING_NAME_GLUE_H diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 47eb432490..828ab73c82 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -49,13 +49,13 @@ namespace GodotSharpDirs { String _get_expected_build_config() { #ifdef TOOLS_ENABLED - return "Tools"; + return "Debug"; #else #ifdef DEBUG_ENABLED - return "Debug"; + return "ExportDebug"; #else - return "Release"; + return "ExportRelease"; #endif #endif diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp new file mode 100644 index 0000000000..a9cf64d1cc --- /dev/null +++ b/modules/mono/managed_callable.cpp @@ -0,0 +1,145 @@ +/*************************************************************************/ +/* managed_callable.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "managed_callable.h" + +#include "csharp_script.h" +#include "mono_gd/gd_mono_marshal.h" +#include "mono_gd/gd_mono_utils.h" + +#ifdef GD_MONO_HOT_RELOAD +SelfList<ManagedCallable>::List ManagedCallable::instances; +Map<ManagedCallable *, Array> ManagedCallable::instances_pending_reload; +Mutex ManagedCallable::instances_mutex; +#endif + +bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) { + const ManagedCallable *a = static_cast<const ManagedCallable *>(p_a); + const ManagedCallable *b = static_cast<const ManagedCallable *>(p_b); + + MonoDelegate *delegate_a = (MonoDelegate *)a->delegate_handle.get_target(); + MonoDelegate *delegate_b = (MonoDelegate *)b->delegate_handle.get_target(); + + if (!delegate_a || !delegate_b) { + if (!delegate_a && !delegate_b) + return true; + return false; + } + + // Call Delegate's 'Equals' + return GDMonoUtils::mono_delegate_equal(delegate_a, delegate_b); +} + +bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) { + if (compare_equal(p_a, p_b)) + return false; + return p_a < p_b; +} + +uint32_t ManagedCallable::hash() const { + // hmm + uint32_t hash = delegate_invoke->get_name().hash(); + return hash_djb2_one_64(delegate_handle.handle, hash); +} + +String ManagedCallable::get_as_text() const { + return "Delegate::Invoke"; +} + +CallableCustom::CompareEqualFunc ManagedCallable::get_compare_equal_func() const { + return compare_equal_func_ptr; +} + +CallableCustom::CompareLessFunc ManagedCallable::get_compare_less_func() const { + return compare_less_func_ptr; +} + +ObjectID ManagedCallable::get_object() const { + return CSharpLanguage::get_singleton()->get_managed_callable_middleman()->get_instance_id(); +} + +void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { + r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better + r_return_value = Variant(); + +#ifdef GD_MONO_HOT_RELOAD + // Lost during hot-reload + ERR_FAIL_NULL(delegate_invoke); + ERR_FAIL_COND(delegate_handle.is_released()); +#endif + + ERR_FAIL_COND(delegate_invoke->get_parameters_count() < p_argcount); + + MonoObject *delegate = delegate_handle.get_target(); + + MonoException *exc = NULL; + MonoObject *ret = delegate_invoke->invoke(delegate, p_arguments, &exc); + + if (exc) { + GDMonoUtils::set_pending_exception(exc); + } else { + r_return_value = GDMonoMarshal::mono_object_to_variant(ret); + r_call_error.error = Callable::CallError::CALL_OK; + } +} + +void ManagedCallable::set_delegate(MonoDelegate *p_delegate) { + delegate_handle = MonoGCHandleData::new_strong_handle((MonoObject *)p_delegate); + MonoMethod *delegate_invoke_raw = mono_get_delegate_invoke(mono_object_get_class((MonoObject *)p_delegate)); + const StringName &delegate_invoke_name = CSharpLanguage::get_singleton()->get_string_names().delegate_invoke_method_name; + delegate_invoke = memnew(GDMonoMethod(delegate_invoke_name, delegate_invoke_raw)); // TODO: Use pooling for this GDMonoMethod instances +} + +ManagedCallable::ManagedCallable(MonoDelegate *p_delegate) { +#ifdef DEBUG_ENABLED + CRASH_COND(p_delegate == NULL); +#endif + + set_delegate(p_delegate); + +#ifdef GD_MONO_HOT_RELOAD + { + MutexLock lock(instances_mutex); + instances.add(&self_instance); + } +#endif +} + +ManagedCallable::~ManagedCallable() { +#ifdef GD_MONO_HOT_RELOAD + { + MutexLock lock(instances_mutex); + instances.remove(&self_instance); + instances_pending_reload.erase(this); + } +#endif + + delegate_handle.release(); +} diff --git a/modules/mono/utils/thread_local.cpp b/modules/mono/managed_callable.h index 4f10e3fb85..4f71e14a2f 100644 --- a/modules/mono/utils/thread_local.cpp +++ b/modules/mono/managed_callable.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* thread_local.cpp */ +/* managed_callable.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,80 +28,50 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "thread_local.h" +#ifndef MANAGED_CALLABLE_H +#define MANAGED_CALLABLE_H -#ifdef WINDOWS_ENABLED -#include <windows.h> -#else -#include <pthread.h> -#endif - -#include "core/os/memory.h" -#include "core/print_string.h" +#include <mono/metadata/object.h> -struct ThreadLocalStorage::Impl { +#include "core/callable.h" +#include "core/os/mutex.h" +#include "core/self_list.h" -#ifdef WINDOWS_ENABLED - DWORD dwFlsIndex; -#else - pthread_key_t key; -#endif - - void *get_value() const { -#ifdef WINDOWS_ENABLED - return FlsGetValue(dwFlsIndex); -#else - return pthread_getspecific(key); -#endif - } +#include "mono_gc_handle.h" +#include "mono_gd/gd_mono_method.h" - void set_value(void *p_value) const { -#ifdef WINDOWS_ENABLED - FlsSetValue(dwFlsIndex, p_value); -#else - pthread_setspecific(key, p_value); -#endif - } +class ManagedCallable : public CallableCustom { + friend class CSharpLanguage; + MonoGCHandleData delegate_handle; + GDMonoMethod *delegate_invoke; -#ifdef WINDOWS_ENABLED -#define _CALLBACK_FUNC_ __stdcall -#else -#define _CALLBACK_FUNC_ +#ifdef GD_MONO_HOT_RELOAD + SelfList<ManagedCallable> self_instance = this; + static SelfList<ManagedCallable>::List instances; + static Map<ManagedCallable *, Array> instances_pending_reload; + static Mutex instances_mutex; #endif - Impl(void(_CALLBACK_FUNC_ *p_destr_callback_func)(void *)) { -#ifdef WINDOWS_ENABLED - dwFlsIndex = FlsAlloc(p_destr_callback_func); - ERR_FAIL_COND(dwFlsIndex == FLS_OUT_OF_INDEXES); -#else - pthread_key_create(&key, p_destr_callback_func); -#endif - } +public: + uint32_t hash() const override; + String get_as_text() const override; + CompareEqualFunc get_compare_equal_func() const override; + CompareLessFunc get_compare_less_func() const override; + ObjectID get_object() const override; + void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; - ~Impl() { -#ifdef WINDOWS_ENABLED - FlsFree(dwFlsIndex); -#else - pthread_key_delete(key); -#endif - } -}; + _FORCE_INLINE_ MonoDelegate *get_delegate() { return (MonoDelegate *)delegate_handle.get_target(); } -void *ThreadLocalStorage::get_value() const { - return pimpl->get_value(); -} + void set_delegate(MonoDelegate *p_delegate); -void ThreadLocalStorage::set_value(void *p_value) const { - pimpl->set_value(p_value); -} + static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b); + static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b); -void ThreadLocalStorage::alloc(void(_CALLBACK_FUNC_ *p_destr_callback)(void *)) { - pimpl = memnew(ThreadLocalStorage::Impl(p_destr_callback)); -} + static constexpr CompareEqualFunc compare_equal_func_ptr = &ManagedCallable::compare_equal; + static constexpr CompareEqualFunc compare_less_func_ptr = &ManagedCallable::compare_less; -#undef _CALLBACK_FUNC_ + ManagedCallable(MonoDelegate *p_delegate); + ~ManagedCallable(); +}; -void ThreadLocalStorage::free() { - memdelete(pimpl); - pimpl = NULL; -} +#endif // MANAGED_CALLABLE_H diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp index feeea848ee..4b6d7269e9 100644 --- a/modules/mono/mono_gc_handle.cpp +++ b/modules/mono/mono_gc_handle.cpp @@ -32,56 +32,35 @@ #include "mono_gd/gd_mono.h" -uint32_t MonoGCHandle::new_strong_handle(MonoObject *p_object) { - - return mono_gchandle_new(p_object, /* pinned: */ false); -} - -uint32_t MonoGCHandle::new_strong_handle_pinned(MonoObject *p_object) { - - return mono_gchandle_new(p_object, /* pinned: */ true); -} - -uint32_t MonoGCHandle::new_weak_handle(MonoObject *p_object) { - - return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false); -} - -void MonoGCHandle::free_handle(uint32_t p_gchandle) { +void MonoGCHandleData::release() { +#ifdef DEBUG_ENABLED + CRASH_COND(handle && GDMono::get_singleton() == NULL); +#endif - mono_gchandle_free(p_gchandle); + if (handle && GDMono::get_singleton()->is_runtime_initialized()) { + GDMonoUtils::free_gchandle(handle); + handle = 0; + } } -Ref<MonoGCHandle> MonoGCHandle::create_strong(MonoObject *p_object) { - - return memnew(MonoGCHandle(new_strong_handle(p_object), STRONG_HANDLE)); +MonoGCHandleData MonoGCHandleData::new_strong_handle(MonoObject *p_object) { + return MonoGCHandleData(GDMonoUtils::new_strong_gchandle(p_object), gdmono::GCHandleType::STRONG_HANDLE); } -Ref<MonoGCHandle> MonoGCHandle::create_weak(MonoObject *p_object) { - - return memnew(MonoGCHandle(new_weak_handle(p_object), WEAK_HANDLE)); +MonoGCHandleData MonoGCHandleData::new_strong_handle_pinned(MonoObject *p_object) { + return MonoGCHandleData(GDMonoUtils::new_strong_gchandle_pinned(p_object), gdmono::GCHandleType::STRONG_HANDLE); } -void MonoGCHandle::release() { - -#ifdef DEBUG_ENABLED - CRASH_COND(!released && GDMono::get_singleton() == NULL); -#endif - - if (!released && GDMono::get_singleton()->is_runtime_initialized()) { - free_handle(handle); - released = true; - } +MonoGCHandleData MonoGCHandleData::new_weak_handle(MonoObject *p_object) { + return MonoGCHandleData(GDMonoUtils::new_weak_gchandle(p_object), gdmono::GCHandleType::WEAK_HANDLE); } -MonoGCHandle::MonoGCHandle(uint32_t p_handle, HandleType p_handle_type) { +Ref<MonoGCHandleRef> MonoGCHandleRef::create_strong(MonoObject *p_object) { - released = false; - weak = p_handle_type == WEAK_HANDLE; - handle = p_handle; + return memnew(MonoGCHandleRef(MonoGCHandleData::new_strong_handle(p_object))); } -MonoGCHandle::~MonoGCHandle() { +Ref<MonoGCHandleRef> MonoGCHandleRef::create_weak(MonoObject *p_object) { - release(); + return memnew(MonoGCHandleRef(MonoGCHandleData::new_weak_handle(p_object))); } diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h index 37fc7d8a17..705b2265ba 100644 --- a/modules/mono/mono_gc_handle.h +++ b/modules/mono/mono_gc_handle.h @@ -35,42 +35,79 @@ #include "core/reference.h" -class MonoGCHandle : public Reference { +namespace gdmono { - GDCLASS(MonoGCHandle, Reference); +enum class GCHandleType : char { + NIL, + STRONG_HANDLE, + WEAK_HANDLE +}; + +} - bool released; - bool weak; +// Manual release of the GC handle must be done when using this struct +struct MonoGCHandleData { uint32_t handle; + gdmono::GCHandleType type; -public: - enum HandleType { - STRONG_HANDLE, - WEAK_HANDLE - }; + _FORCE_INLINE_ bool is_released() const { return !handle; } + _FORCE_INLINE_ bool is_weak() const { return type == gdmono::GCHandleType::WEAK_HANDLE; } - static uint32_t new_strong_handle(MonoObject *p_object); - static uint32_t new_strong_handle_pinned(MonoObject *p_object); - static uint32_t new_weak_handle(MonoObject *p_object); - static void free_handle(uint32_t p_gchandle); + _FORCE_INLINE_ MonoObject *get_target() const { return handle ? mono_gchandle_get_target(handle) : NULL; } - static Ref<MonoGCHandle> create_strong(MonoObject *p_object); - static Ref<MonoGCHandle> create_weak(MonoObject *p_object); + void release(); - _FORCE_INLINE_ bool is_released() { return released; } - _FORCE_INLINE_ bool is_weak() { return weak; } + MonoGCHandleData &operator=(const MonoGCHandleData &p_other) { +#ifdef DEBUG_ENABLED + CRASH_COND(!is_released()); +#endif + handle = p_other.handle; + type = p_other.type; + return *this; + } - _FORCE_INLINE_ MonoObject *get_target() const { return released ? NULL : mono_gchandle_get_target(handle); } + MonoGCHandleData(const MonoGCHandleData &) = default; - _FORCE_INLINE_ void set_handle(uint32_t p_handle, HandleType p_handle_type) { - released = false; - weak = p_handle_type == WEAK_HANDLE; - handle = p_handle; + MonoGCHandleData() : + handle(0), + type(gdmono::GCHandleType::NIL) { } - void release(); - MonoGCHandle(uint32_t p_handle, HandleType p_handle_type); - ~MonoGCHandle(); + MonoGCHandleData(uint32_t p_handle, gdmono::GCHandleType p_type) : + handle(p_handle), + type(p_type) { + } + + static MonoGCHandleData new_strong_handle(MonoObject *p_object); + static MonoGCHandleData new_strong_handle_pinned(MonoObject *p_object); + static MonoGCHandleData new_weak_handle(MonoObject *p_object); +}; + +class MonoGCHandleRef : public Reference { + + GDCLASS(MonoGCHandleRef, Reference); + + MonoGCHandleData data; + +public: + static Ref<MonoGCHandleRef> create_strong(MonoObject *p_object); + static Ref<MonoGCHandleRef> create_weak(MonoObject *p_object); + + _FORCE_INLINE_ bool is_released() const { return data.is_released(); } + _FORCE_INLINE_ bool is_weak() const { return data.is_weak(); } + + _FORCE_INLINE_ MonoObject *get_target() const { return data.get_target(); } + + void release() { data.release(); } + + _FORCE_INLINE_ void set_handle(uint32_t p_handle, gdmono::GCHandleType p_handle_type) { + data = MonoGCHandleData(p_handle, p_handle_type); + } + + MonoGCHandleRef(const MonoGCHandleData &p_gc_handle_data) : + data(p_gc_handle_data) { + } + ~MonoGCHandleRef() { release(); } }; #endif // CSHARP_GC_HANDLE_H diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 8f489e8d8d..9528c64f8d 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -203,7 +203,7 @@ public: static GDMono *get_singleton() { return singleton; } - GD_NORETURN static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data); + [[noreturn]] static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data); UnhandledExceptionPolicy get_unhandled_exception_policy() const { return unhandled_exception_policy; } diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 6cf5377e2c..6da1db249c 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -78,7 +78,7 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin if (p_custom_config.empty()) { r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir()); } else { - String api_config = p_custom_config == "Release" ? "Release" : "Debug"; + String api_config = p_custom_config == "ExportRelease" ? "Release" : "Debug"; r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir().plus_file(api_config)); } @@ -148,7 +148,7 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d return res ? res->get_assembly() : NULL; } -static _THREAD_LOCAL_(MonoImage *) image_corlib_loading = NULL; +static thread_local MonoImage *image_corlib_loading = NULL; MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, void *user_data, bool refonly) { diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index 0ad90a510e..be0b846702 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -103,15 +103,19 @@ void CachedData::clear_godot_api_cache() { rawclass_Dictionary = NULL; class_Vector2 = NULL; + class_Vector2i = NULL; class_Rect2 = NULL; + class_Rect2i = NULL; class_Transform2D = NULL; class_Vector3 = NULL; + class_Vector3i = NULL; class_Basis = NULL; class_Quat = NULL; class_Transform = NULL; class_AABB = NULL; class_Color = NULL; class_Plane = NULL; + class_StringName = NULL; class_NodePath = NULL; class_RID = NULL; class_GodotObject = NULL; @@ -120,6 +124,8 @@ void CachedData::clear_godot_api_cache() { class_Control = NULL; class_Spatial = NULL; class_WeakRef = NULL; + class_Callable = NULL; + class_SignalInfo = NULL; class_Array = NULL; class_Dictionary = NULL; class_MarshalUtils = NULL; @@ -145,6 +151,7 @@ void CachedData::clear_godot_api_cache() { field_GodotMethodAttribute_methodName = NULL; field_GodotObject_ptr = NULL; + field_StringName_ptr = NULL; field_NodePath_ptr = NULL; field_Image_ptr = NULL; field_RID_ptr = NULL; @@ -153,9 +160,13 @@ void CachedData::clear_godot_api_cache() { methodthunk_Array_GetPtr.nullify(); methodthunk_Dictionary_GetPtr.nullify(); methodthunk_SignalAwaiter_SignalCallback.nullify(); - methodthunk_SignalAwaiter_FailureCallback.nullify(); methodthunk_GodotTaskScheduler_Activate.nullify(); + methodthunk_Delegate_Equals.nullify(); + + methodthunk_DelegateUtils_TrySerializeDelegate.nullify(); + methodthunk_DelegateUtils_TryDeserializeDelegate.nullify(); + // Start of MarshalUtils methods methodthunk_MarshalUtils_TypeIsGenericArray.nullify(); @@ -178,7 +189,7 @@ void CachedData::clear_godot_api_cache() { // End of MarshalUtils methods - task_scheduler_handle = Ref<MonoGCHandle>(); + task_scheduler_handle = Ref<MonoGCHandleRef>(); } #define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) @@ -211,6 +222,8 @@ void update_corlib_cache() { CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true)); #endif + CACHE_METHOD_THUNK_AND_CHECK(Delegate, Equals, GDMono::get_singleton()->get_corlib_assembly()->get_class("System", "Delegate")->get_method_with_desc("System.Delegate:Equals(object)", 1)); + CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException")); cached_data.corlib_cache_updated = true; @@ -219,15 +232,19 @@ void update_corlib_cache() { void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(Vector2, GODOT_API_CLASS(Vector2)); + CACHE_CLASS_AND_CHECK(Vector2i, GODOT_API_CLASS(Vector2i)); CACHE_CLASS_AND_CHECK(Rect2, GODOT_API_CLASS(Rect2)); + CACHE_CLASS_AND_CHECK(Rect2i, GODOT_API_CLASS(Rect2i)); CACHE_CLASS_AND_CHECK(Transform2D, GODOT_API_CLASS(Transform2D)); CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3)); + CACHE_CLASS_AND_CHECK(Vector3i, GODOT_API_CLASS(Vector3i)); CACHE_CLASS_AND_CHECK(Basis, GODOT_API_CLASS(Basis)); CACHE_CLASS_AND_CHECK(Quat, GODOT_API_CLASS(Quat)); CACHE_CLASS_AND_CHECK(Transform, GODOT_API_CLASS(Transform)); CACHE_CLASS_AND_CHECK(AABB, GODOT_API_CLASS(AABB)); CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color)); CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane)); + CACHE_CLASS_AND_CHECK(StringName, GODOT_API_CLASS(StringName)); CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath)); CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID)); CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object)); @@ -236,6 +253,8 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control)); CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial)); CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef)); + CACHE_CLASS_AND_CHECK(Callable, GODOT_API_CLASS(Callable)); + CACHE_CLASS_AND_CHECK(SignalInfo, GODOT_API_CLASS(SignalInfo)); CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array)); CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)); CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils)); @@ -261,6 +280,7 @@ void update_godot_api_cache() { CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName")); CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD)); + CACHE_FIELD_AND_CHECK(StringName, ptr, CACHED_CLASS(StringName)->get_field(BINDINGS_PTR_FIELD)); CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD)); CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD)); @@ -268,9 +288,11 @@ void update_godot_api_cache() { CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method("GetPtr", 0)); CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method("GetPtr", 0)); CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1)); - CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0)); CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)); + CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegate", 2)); + CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegate", 2)); + // Start of MarshalUtils methods CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericArray", 1)); @@ -300,7 +322,7 @@ void update_godot_api_cache() { // TODO Move to CSharpLanguage::init() and do handle disposal MonoObject *task_scheduler = mono_object_new(mono_domain_get(), GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr()); GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler)); - cached_data.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler); + cached_data.task_scheduler_handle = MonoGCHandleRef::create_strong(task_scheduler); cached_data.godot_api_cache_updated = true; } diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 0458e91240..b2dacee67c 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -73,15 +73,19 @@ struct CachedData { // ----------------------------------------------- GDMonoClass *class_Vector2; + GDMonoClass *class_Vector2i; GDMonoClass *class_Rect2; + GDMonoClass *class_Rect2i; GDMonoClass *class_Transform2D; GDMonoClass *class_Vector3; + GDMonoClass *class_Vector3i; GDMonoClass *class_Basis; GDMonoClass *class_Quat; GDMonoClass *class_Transform; GDMonoClass *class_AABB; GDMonoClass *class_Color; GDMonoClass *class_Plane; + GDMonoClass *class_StringName; GDMonoClass *class_NodePath; GDMonoClass *class_RID; GDMonoClass *class_GodotObject; @@ -90,6 +94,8 @@ struct CachedData { GDMonoClass *class_Control; GDMonoClass *class_Spatial; GDMonoClass *class_WeakRef; + GDMonoClass *class_Callable; + GDMonoClass *class_SignalInfo; GDMonoClass *class_Array; GDMonoClass *class_Dictionary; GDMonoClass *class_MarshalUtils; @@ -115,6 +121,7 @@ struct CachedData { GDMonoField *field_GodotMethodAttribute_methodName; GDMonoField *field_GodotObject_ptr; + GDMonoField *field_StringName_ptr; GDMonoField *field_NodePath_ptr; GDMonoField *field_Image_ptr; GDMonoField *field_RID_ptr; @@ -123,9 +130,13 @@ struct CachedData { GDMonoMethodThunkR<Array *, MonoObject *> methodthunk_Array_GetPtr; GDMonoMethodThunkR<Dictionary *, MonoObject *> methodthunk_Dictionary_GetPtr; GDMonoMethodThunk<MonoObject *, MonoArray *> methodthunk_SignalAwaiter_SignalCallback; - GDMonoMethodThunk<MonoObject *> methodthunk_SignalAwaiter_FailureCallback; GDMonoMethodThunk<MonoObject *> methodthunk_GodotTaskScheduler_Activate; + GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoObject *> methodthunk_Delegate_Equals; + + GDMonoMethodThunkR<MonoBoolean, MonoDelegate *, MonoObject *> methodthunk_DelegateUtils_TrySerializeDelegate; + GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoDelegate **> methodthunk_DelegateUtils_TryDeserializeDelegate; + // Start of MarshalUtils methods GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericArray; @@ -148,7 +159,7 @@ struct CachedData { // End of MarshalUtils methods - Ref<MonoGCHandle> task_scheduler_handle; + Ref<MonoGCHandleRef> task_scheduler_handle; bool corlib_cache_updated; bool godot_api_cache_updated; @@ -193,10 +204,4 @@ _FORCE_INLINE_ bool tools_godot_api_check() { #define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoCache::cached_data.methodthunk_##m_class##_##m_method) #define CACHED_PROPERTY(m_class, m_property) (GDMonoCache::cached_data.property_##m_class##_##m_property) -#ifdef REAL_T_IS_DOUBLE -#define REAL_T_MONOCLASS CACHED_CLASS_RAW(double) -#else -#define REAL_T_MONOCLASS CACHED_CLASS_RAW(float) -#endif - #endif // GD_MONO_CACHE_H diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 03b56c9949..11942c47d9 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -37,6 +37,10 @@ #include "gd_mono_marshal.h" #include "gd_mono_utils.h" +void GDMonoField::set_value(MonoObject *p_object, MonoObject *p_value) { + mono_field_set_value(p_object, mono_field, p_value); +} + void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) { mono_field_set_value(p_object, mono_field, &p_ptr); } @@ -128,11 +132,21 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } + if (tclass == CACHED_CLASS(Vector2i)) { + SET_FROM_STRUCT(Vector2i); + break; + } + if (tclass == CACHED_CLASS(Rect2)) { SET_FROM_STRUCT(Rect2); break; } + if (tclass == CACHED_CLASS(Rect2i)) { + SET_FROM_STRUCT(Rect2i); + break; + } + if (tclass == CACHED_CLASS(Transform2D)) { SET_FROM_STRUCT(Transform2D); break; @@ -143,6 +157,11 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } + if (tclass == CACHED_CLASS(Vector3i)) { + SET_FROM_STRUCT(Vector3i); + break; + } + if (tclass == CACHED_CLASS(Basis)) { SET_FROM_STRUCT(Basis); break; @@ -173,6 +192,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } + if (tclass == CACHED_CLASS(Callable)) { + GDMonoMarshal::M_Callable val = GDMonoMarshal::callable_to_managed(p_value.operator Callable()); + mono_field_set_value(p_object, mono_field, &val); + break; + } + + if (tclass == CACHED_CLASS(SignalInfo)) { + GDMonoMarshal::M_SignalInfo val = GDMonoMarshal::signal_info_to_managed(p_value.operator Signal()); + mono_field_set_value(p_object, mono_field, &val); + break; + } + if (mono_class_is_enum(tclass->get_mono_ptr())) { MonoType *enum_basetype = mono_class_enum_basetype(tclass->get_mono_ptr()); switch (mono_type_get_type(enum_basetype)) { @@ -256,11 +287,21 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } - if (array_type->eklass == REAL_T_MONOCLASS) { + if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) { + SET_FROM_ARRAY(PackedInt64Array); + break; + } + + if (array_type->eklass == CACHED_CLASS_RAW(float)) { SET_FROM_ARRAY(PackedFloat32Array); break; } + if (array_type->eklass == CACHED_CLASS_RAW(double)) { + SET_FROM_ARRAY(PackedFloat64Array); + break; + } + if (array_type->eklass == CACHED_CLASS_RAW(String)) { SET_FROM_ARRAY(PackedStringArray); break; @@ -294,6 +335,12 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } + if (CACHED_CLASS(StringName) == type_class) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName()); + mono_field_set_value(p_object, mono_field, managed); + break; + } + if (CACHED_CLASS(NodePath) == type_class) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath()); mono_field_set_value(p_object, mono_field, managed); @@ -386,12 +433,21 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ case Variant::VECTOR2: { SET_FROM_STRUCT(Vector2); } break; + case Variant::VECTOR2I: { + SET_FROM_STRUCT(Vector2i); + } break; case Variant::RECT2: { SET_FROM_STRUCT(Rect2); } break; + case Variant::RECT2I: { + SET_FROM_STRUCT(Rect2i); + } break; case Variant::VECTOR3: { SET_FROM_STRUCT(Vector3); } break; + case Variant::VECTOR3I: { + SET_FROM_STRUCT(Vector3i); + } break; case Variant::TRANSFORM2D: { SET_FROM_STRUCT(Transform2D); } break; @@ -413,6 +469,10 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ case Variant::COLOR: { SET_FROM_STRUCT(Color); } break; + case Variant::STRING_NAME: { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName()); + mono_field_set_value(p_object, mono_field, managed); + } break; case Variant::NODE_PATH: { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath()); mono_field_set_value(p_object, mono_field, managed); @@ -424,8 +484,15 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ case Variant::OBJECT: { MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *()); mono_field_set_value(p_object, mono_field, managed); - break; - } + } break; + case Variant::CALLABLE: { + GDMonoMarshal::M_Callable val = GDMonoMarshal::callable_to_managed(p_value.operator Callable()); + mono_field_set_value(p_object, mono_field, &val); + } break; + case Variant::SIGNAL: { + GDMonoMarshal::M_SignalInfo val = GDMonoMarshal::signal_info_to_managed(p_value.operator Signal()); + mono_field_set_value(p_object, mono_field, &val); + } break; case Variant::DICTIONARY: { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); mono_field_set_value(p_object, mono_field, managed); @@ -440,9 +507,15 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ case Variant::PACKED_INT32_ARRAY: { SET_FROM_ARRAY(PackedInt32Array); } break; + case Variant::PACKED_INT64_ARRAY: { + SET_FROM_ARRAY(PackedInt64Array); + } break; case Variant::PACKED_FLOAT32_ARRAY: { SET_FROM_ARRAY(PackedFloat32Array); } break; + case Variant::PACKED_FLOAT64_ARRAY: { + SET_FROM_ARRAY(PackedFloat64Array); + } break; case Variant::PACKED_STRING_ARRAY: { SET_FROM_ARRAY(PackedStringArray); } break; diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h index 76ee0963c4..61f2c8f071 100644 --- a/modules/mono/mono_gd/gd_mono_field.h +++ b/modules/mono/mono_gd/gd_mono_field.h @@ -47,21 +47,22 @@ class GDMonoField : public IMonoClassMember { MonoCustomAttrInfo *attributes; public: - virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; } + virtual GDMonoClass *get_enclosing_class() const final { return owner; } - virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_FIELD; } + virtual MemberType get_member_type() const final { return MEMBER_TYPE_FIELD; } - virtual StringName get_name() const GD_FINAL { return name; } + virtual StringName get_name() const final { return name; } - virtual bool is_static() GD_FINAL; - virtual Visibility get_visibility() GD_FINAL; + virtual bool is_static() final; + virtual Visibility get_visibility() final; - virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL; - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL; + virtual bool has_attribute(GDMonoClass *p_attr_class) final; + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final; void fetch_attributes(); _FORCE_INLINE_ ManagedType get_type() const { return type; } + void set_value(MonoObject *p_object, MonoObject *p_value); void set_value_raw(MonoObject *p_object, void *p_ptr); void set_value_from_variant(MonoObject *p_object, const Variant &p_value); diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index b179b484f3..53e642f317 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -33,7 +33,6 @@ #include "../csharp_script.h" #include "../mono_gc_handle.h" #include "../utils/macros.h" -#include "../utils/thread_local.h" #include "gd_mono_class.h" #include "gd_mono_marshal.h" #include "gd_mono_utils.h" @@ -76,7 +75,7 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { script_binding.inited = true; script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass); script_binding.wrapper_class = klass; - script_binding.gchandle = ref ? MonoGCHandle::create_weak(managed) : MonoGCHandle::create_strong(managed); + script_binding.gchandle = ref ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed); script_binding.owner = unmanaged; if (ref) { @@ -102,15 +101,17 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { return; } - Ref<MonoGCHandle> gchandle = ref ? MonoGCHandle::create_weak(managed) : MonoGCHandle::create_strong(managed); + MonoGCHandleData gchandle = ref ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed); Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass, native); CRASH_COND(script.is_null()); - ScriptInstance *si = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle); + CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle); - unmanaged->set_script_and_instance(script, si); + unmanaged->set_script_and_instance(script, csharp_instance); + + csharp_instance->connect_event_signals(); } void unhandled_exception(MonoException *p_exc) { diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 695be64d6e..0de5352752 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -30,13 +30,14 @@ #include "gd_mono_marshal.h" +#include "../signal_awaiter_utils.h" #include "gd_mono.h" #include "gd_mono_cache.h" #include "gd_mono_class.h" namespace GDMonoMarshal { -Variant::Type managed_to_variant_type(const ManagedType &p_type) { +Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant) { switch (p_type.type_encoding) { case MONO_TYPE_BOOLEAN: return Variant::BOOL; @@ -74,15 +75,24 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { if (vtclass == CACHED_CLASS(Vector2)) return Variant::VECTOR2; + if (vtclass == CACHED_CLASS(Vector2i)) + return Variant::VECTOR2I; + if (vtclass == CACHED_CLASS(Rect2)) return Variant::RECT2; + if (vtclass == CACHED_CLASS(Rect2i)) + return Variant::RECT2I; + if (vtclass == CACHED_CLASS(Transform2D)) return Variant::TRANSFORM2D; if (vtclass == CACHED_CLASS(Vector3)) return Variant::VECTOR3; + if (vtclass == CACHED_CLASS(Vector3i)) + return Variant::VECTOR3I; + if (vtclass == CACHED_CLASS(Basis)) return Variant::BASIS; @@ -101,6 +111,12 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { if (vtclass == CACHED_CLASS(Plane)) return Variant::PLANE; + if (vtclass == CACHED_CLASS(Callable)) + return Variant::CALLABLE; + + if (vtclass == CACHED_CLASS(SignalInfo)) + return Variant::SIGNAL; + if (mono_class_is_enum(vtclass->get_mono_ptr())) return Variant::INT; } break; @@ -118,9 +134,15 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) return Variant::PACKED_INT32_ARRAY; - if (array_type->eklass == REAL_T_MONOCLASS) + if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) + return Variant::PACKED_INT64_ARRAY; + + if (array_type->eklass == CACHED_CLASS_RAW(float)) return Variant::PACKED_FLOAT32_ARRAY; + if (array_type->eklass == CACHED_CLASS_RAW(double)) + return Variant::PACKED_FLOAT64_ARRAY; + if (array_type->eklass == CACHED_CLASS_RAW(String)) return Variant::PACKED_STRING_ARRAY; @@ -142,6 +164,10 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { return Variant::OBJECT; } + if (CACHED_CLASS(StringName) == type_class) { + return Variant::STRING_NAME; + } + if (CACHED_CLASS(NodePath) == type_class) { return Variant::NODE_PATH; } @@ -179,6 +205,12 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { } } break; + case MONO_TYPE_OBJECT: { + if (r_nil_is_variant) + *r_nil_is_variant = true; + return Variant::NIL; + } break; + case MONO_TYPE_GENERICINST: { MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); @@ -211,6 +243,9 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { } break; } + if (r_nil_is_variant) + *r_nil_is_variant = false; + // Unknown return Variant::NIL; } @@ -387,11 +422,21 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from); } + if (vtclass == CACHED_CLASS(Vector2i)) { + GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var->operator ::Vector2i()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from); + } + if (vtclass == CACHED_CLASS(Rect2)) { GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from); } + if (vtclass == CACHED_CLASS(Rect2i)) { + GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var->operator ::Rect2i()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from); + } + if (vtclass == CACHED_CLASS(Transform2D)) { GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from); @@ -402,6 +447,11 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from); } + if (vtclass == CACHED_CLASS(Vector3i)) { + GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var->operator ::Vector3i()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from); + } + if (vtclass == CACHED_CLASS(Basis)) { GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var->operator ::Basis()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from); @@ -432,6 +482,16 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from); } + if (vtclass == CACHED_CLASS(Callable)) { + GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var->operator Callable()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from); + } + + if (vtclass == CACHED_CLASS(SignalInfo)) { + GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var->operator Signal()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from); + } + if (mono_class_is_enum(vtclass->get_mono_ptr())) { MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr()); MonoClass *enum_baseclass = mono_class_from_mono_type(enum_basetype); @@ -496,9 +556,15 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array()); - if (array_type->eklass == REAL_T_MONOCLASS) + if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) + return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array()); + + if (array_type->eklass == CACHED_CLASS_RAW(float)) return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array()); + if (array_type->eklass == CACHED_CLASS_RAW(double)) + return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array()); + if (array_type->eklass == CACHED_CLASS_RAW(String)) return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray()); @@ -522,6 +588,10 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *()); } + if (CACHED_CLASS(StringName) == type_class) { + return GDMonoUtils::create_managed_from(p_var->operator StringName()); + } + if (CACHED_CLASS(NodePath) == type_class) { return GDMonoUtils::create_managed_from(p_var->operator NodePath()); } @@ -592,14 +662,26 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var->operator ::Vector2()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from); } + case Variant::VECTOR2I: { + GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var->operator ::Vector2i()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from); + } case Variant::RECT2: { GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from); } + case Variant::RECT2I: { + GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var->operator ::Rect2i()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from); + } case Variant::VECTOR3: { GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var->operator ::Vector3()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from); } + case Variant::VECTOR3I: { + GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var->operator ::Vector3i()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from); + } case Variant::TRANSFORM2D: { GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from); @@ -628,12 +710,22 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from); } + case Variant::STRING_NAME: + return GDMonoUtils::create_managed_from(p_var->operator StringName()); case Variant::NODE_PATH: return GDMonoUtils::create_managed_from(p_var->operator NodePath()); case Variant::_RID: return GDMonoUtils::create_managed_from(p_var->operator RID()); case Variant::OBJECT: return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *()); + case Variant::CALLABLE: { + GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var->operator Callable()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from); + } + case Variant::SIGNAL: { + GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var->operator Signal()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from); + } case Variant::DICTIONARY: return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); case Variant::ARRAY: @@ -642,8 +734,12 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty return (MonoObject *)PackedByteArray_to_mono_array(p_var->operator PackedByteArray()); case Variant::PACKED_INT32_ARRAY: return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array()); + case Variant::PACKED_INT64_ARRAY: + return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array()); case Variant::PACKED_FLOAT32_ARRAY: return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array()); + case Variant::PACKED_FLOAT64_ARRAY: + return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array()); case Variant::PACKED_STRING_ARRAY: return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray()); case Variant::PACKED_VECTOR2_ARRAY: @@ -744,34 +840,49 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type GDMonoClass *vtclass = p_type.type_class; if (vtclass == CACHED_CLASS(Vector2)) - return MARSHALLED_IN(Vector2, (GDMonoMarshal::M_Vector2 *)mono_object_unbox(p_obj)); + return MARSHALLED_IN(Vector2, unbox_addr<GDMonoMarshal::M_Vector2>(p_obj)); + + if (vtclass == CACHED_CLASS(Vector2i)) + return MARSHALLED_IN(Vector2i, unbox_addr<GDMonoMarshal::M_Vector2i>(p_obj)); if (vtclass == CACHED_CLASS(Rect2)) - return MARSHALLED_IN(Rect2, (GDMonoMarshal::M_Rect2 *)mono_object_unbox(p_obj)); + return MARSHALLED_IN(Rect2, unbox_addr<GDMonoMarshal::M_Rect2>(p_obj)); + + if (vtclass == CACHED_CLASS(Rect2i)) + return MARSHALLED_IN(Rect2i, unbox_addr<GDMonoMarshal::M_Rect2i>(p_obj)); if (vtclass == CACHED_CLASS(Transform2D)) - return MARSHALLED_IN(Transform2D, (GDMonoMarshal::M_Transform2D *)mono_object_unbox(p_obj)); + return MARSHALLED_IN(Transform2D, unbox_addr<GDMonoMarshal::M_Transform2D>(p_obj)); if (vtclass == CACHED_CLASS(Vector3)) - return MARSHALLED_IN(Vector3, (GDMonoMarshal::M_Vector3 *)mono_object_unbox(p_obj)); + return MARSHALLED_IN(Vector3, unbox_addr<GDMonoMarshal::M_Vector3>(p_obj)); + + if (vtclass == CACHED_CLASS(Vector3i)) + return MARSHALLED_IN(Vector3i, unbox_addr<GDMonoMarshal::M_Vector3i>(p_obj)); if (vtclass == CACHED_CLASS(Basis)) - return MARSHALLED_IN(Basis, (GDMonoMarshal::M_Basis *)mono_object_unbox(p_obj)); + return MARSHALLED_IN(Basis, unbox_addr<GDMonoMarshal::M_Basis>(p_obj)); if (vtclass == CACHED_CLASS(Quat)) - return MARSHALLED_IN(Quat, (GDMonoMarshal::M_Quat *)mono_object_unbox(p_obj)); + return MARSHALLED_IN(Quat, unbox_addr<GDMonoMarshal::M_Quat>(p_obj)); if (vtclass == CACHED_CLASS(Transform)) - return MARSHALLED_IN(Transform, (GDMonoMarshal::M_Transform *)mono_object_unbox(p_obj)); + return MARSHALLED_IN(Transform, unbox_addr<GDMonoMarshal::M_Transform>(p_obj)); if (vtclass == CACHED_CLASS(AABB)) - return MARSHALLED_IN(AABB, (GDMonoMarshal::M_AABB *)mono_object_unbox(p_obj)); + return MARSHALLED_IN(AABB, unbox_addr<GDMonoMarshal::M_AABB>(p_obj)); if (vtclass == CACHED_CLASS(Color)) - return MARSHALLED_IN(Color, (GDMonoMarshal::M_Color *)mono_object_unbox(p_obj)); + return MARSHALLED_IN(Color, unbox_addr<GDMonoMarshal::M_Color>(p_obj)); if (vtclass == CACHED_CLASS(Plane)) - return MARSHALLED_IN(Plane, (GDMonoMarshal::M_Plane *)mono_object_unbox(p_obj)); + return MARSHALLED_IN(Plane, unbox_addr<GDMonoMarshal::M_Plane>(p_obj)); + + if (vtclass == CACHED_CLASS(Callable)) + return managed_to_callable(unbox<GDMonoMarshal::M_Callable>(p_obj)); + + if (vtclass == CACHED_CLASS(SignalInfo)) + return managed_to_signal_info(unbox<GDMonoMarshal::M_SignalInfo>(p_obj)); if (mono_class_is_enum(vtclass->get_mono_ptr())) return unbox<int32_t>(p_obj); @@ -790,9 +901,15 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) return mono_array_to_PackedInt32Array((MonoArray *)p_obj); - if (array_type->eklass == REAL_T_MONOCLASS) + if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) + return mono_array_to_PackedInt64Array((MonoArray *)p_obj); + + if (array_type->eklass == CACHED_CLASS_RAW(float)) return mono_array_to_PackedFloat32Array((MonoArray *)p_obj); + if (array_type->eklass == CACHED_CLASS_RAW(double)) + return mono_array_to_PackedFloat64Array((MonoArray *)p_obj); + if (array_type->eklass == CACHED_CLASS_RAW(String)) return mono_array_to_PackedStringArray((MonoArray *)p_obj); @@ -825,6 +942,11 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type return Variant(); } + if (CACHED_CLASS(StringName) == type_class) { + StringName *ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_obj)); + return ptr ? Variant(*ptr) : Variant(); + } + if (CACHED_CLASS(NodePath) == type_class) { NodePath *ptr = unbox<NodePath *>(CACHED_FIELD(NodePath, ptr)->get_value(p_obj)); return ptr ? Variant(*ptr) : Variant(); @@ -960,9 +1082,10 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) { } MonoArray *Array_to_mono_array(const Array &p_array) { - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_array.size()); + int length = p_array.size(); + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), length); - for (int i = 0; i < p_array.size(); i++) { + for (int i = 0; i < length; i++) { MonoObject *boxed = variant_to_mono_object(p_array[i]); mono_array_setref(ret, i, boxed); } @@ -985,16 +1108,14 @@ Array mono_array_to_Array(MonoArray *p_array) { return ret; } -// TODO: Use memcpy where possible - MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array) { - const int *r = p_array.ptr(); + const int32_t *src = p_array.ptr(); + int length = p_array.size(); - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), p_array.size()); + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), length); - for (int i = 0; i < p_array.size(); i++) { - mono_array_set(ret, int32_t, i, r[i]); - } + int32_t *dst = (int32_t *)mono_array_addr(ret, int32_t, 0); + memcpy(dst, src, length); return ret; } @@ -1005,23 +1126,48 @@ PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array) { return ret; int length = mono_array_length(p_array); ret.resize(length); - int *w = ret.ptrw(); + int32_t *dst = ret.ptrw(); - for (int i = 0; i < length; i++) { - w[i] = mono_array_get(p_array, int32_t, i); - } + const int32_t *src = (const int32_t *)mono_array_addr(p_array, int32_t, 0); + memcpy(dst, src, length); + + return ret; +} + +MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array) { + const int64_t *src = p_array.ptr(); + int length = p_array.size(); + + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int64_t), length); + + int64_t *dst = (int64_t *)mono_array_addr(ret, int64_t, 0); + memcpy(dst, src, length); + + return ret; +} + +PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array) { + PackedInt64Array ret; + if (!p_array) + return ret; + int length = mono_array_length(p_array); + ret.resize(length); + int64_t *dst = ret.ptrw(); + + const int64_t *src = (const int64_t *)mono_array_addr(p_array, int64_t, 0); + memcpy(dst, src, length); return ret; } MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array) { - const uint8_t *r = p_array.ptr(); + const uint8_t *src = p_array.ptr(); + int length = p_array.size(); - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), p_array.size()); + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), length); - for (int i = 0; i < p_array.size(); i++) { - mono_array_set(ret, uint8_t, i, r[i]); - } + uint8_t *dst = (uint8_t *)mono_array_addr(ret, uint8_t, 0); + memcpy(dst, src, length); return ret; } @@ -1032,23 +1178,22 @@ PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array) { return ret; int length = mono_array_length(p_array); ret.resize(length); - uint8_t *w = ret.ptrw(); + uint8_t *dst = ret.ptrw(); - for (int i = 0; i < length; i++) { - w[i] = mono_array_get(p_array, uint8_t, i); - } + const uint8_t *src = (const uint8_t *)mono_array_addr(p_array, uint8_t, 0); + memcpy(dst, src, length); return ret; } MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array) { - const real_t *r = p_array.ptr(); + const float *src = p_array.ptr(); + int length = p_array.size(); - MonoArray *ret = mono_array_new(mono_domain_get(), REAL_T_MONOCLASS, p_array.size()); + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(float), length); - for (int i = 0; i < p_array.size(); i++) { - mono_array_set(ret, real_t, i, r[i]); - } + float *dst = (float *)mono_array_addr(ret, float, 0); + memcpy(dst, src, length); return ret; } @@ -1059,21 +1204,47 @@ PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array) { return ret; int length = mono_array_length(p_array); ret.resize(length); - real_t *w = ret.ptrw(); + float *dst = ret.ptrw(); - for (int i = 0; i < length; i++) { - w[i] = mono_array_get(p_array, real_t, i); - } + const float *src = (const float *)mono_array_addr(p_array, float, 0); + memcpy(dst, src, length); + + return ret; +} + +MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array) { + const double *src = p_array.ptr(); + int length = p_array.size(); + + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(double), length); + + double *dst = (double *)mono_array_addr(ret, double, 0); + memcpy(dst, src, length); + + return ret; +} + +PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array) { + PackedFloat64Array ret; + if (!p_array) + return ret; + int length = mono_array_length(p_array); + ret.resize(length); + double *dst = ret.ptrw(); + + const double *src = (const double *)mono_array_addr(p_array, double, 0); + memcpy(dst, src, length); return ret; } MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array) { const String *r = p_array.ptr(); + int length = p_array.size(); - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), p_array.size()); + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), length); - for (int i = 0; i < p_array.size(); i++) { + for (int i = 0; i < length; i++) { MonoString *boxed = mono_string_from_godot(r[i]); mono_array_setref(ret, i, boxed); } @@ -1098,13 +1269,19 @@ PackedStringArray mono_array_to_PackedStringArray(MonoArray *p_array) { } MonoArray *PackedColorArray_to_mono_array(const PackedColorArray &p_array) { - const Color *r = p_array.ptr(); + const Color *src = p_array.ptr(); + int length = p_array.size(); - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), p_array.size()); + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), length); - for (int i = 0; i < p_array.size(); i++) { - M_Color *raw = (M_Color *)mono_array_addr_with_size(ret, sizeof(M_Color), i); - *raw = MARSHALLED_OUT(Color, r[i]); + if constexpr (InteropLayout::MATCHES_Color) { + Color *dst = (Color *)mono_array_addr(ret, Color, 0); + memcpy(dst, src, length); + } else { + for (int i = 0; i < length; i++) { + M_Color *raw = (M_Color *)mono_array_addr_with_size(ret, sizeof(M_Color), i); + *raw = MARSHALLED_OUT(Color, src[i]); + } } return ret; @@ -1116,23 +1293,34 @@ PackedColorArray mono_array_to_PackedColorArray(MonoArray *p_array) { return ret; int length = mono_array_length(p_array); ret.resize(length); - Color *w = ret.ptrw(); + Color *dst = ret.ptrw(); - for (int i = 0; i < length; i++) { - w[i] = MARSHALLED_IN(Color, (M_Color *)mono_array_addr_with_size(p_array, sizeof(M_Color), i)); + if constexpr (InteropLayout::MATCHES_Color) { + const Color *src = (const Color *)mono_array_addr(p_array, Color, 0); + memcpy(dst, src, length); + } else { + for (int i = 0; i < length; i++) { + dst[i] = MARSHALLED_IN(Color, (M_Color *)mono_array_addr_with_size(p_array, sizeof(M_Color), i)); + } } return ret; } MonoArray *PackedVector2Array_to_mono_array(const PackedVector2Array &p_array) { - const Vector2 *r = p_array.ptr(); + const Vector2 *src = p_array.ptr(); + int length = p_array.size(); - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), p_array.size()); + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), length); - for (int i = 0; i < p_array.size(); i++) { - M_Vector2 *raw = (M_Vector2 *)mono_array_addr_with_size(ret, sizeof(M_Vector2), i); - *raw = MARSHALLED_OUT(Vector2, r[i]); + if constexpr (InteropLayout::MATCHES_Vector2) { + Vector2 *dst = (Vector2 *)mono_array_addr(ret, Vector2, 0); + memcpy(dst, src, length); + } else { + for (int i = 0; i < length; i++) { + M_Vector2 *raw = (M_Vector2 *)mono_array_addr_with_size(ret, sizeof(M_Vector2), i); + *raw = MARSHALLED_OUT(Vector2, src[i]); + } } return ret; @@ -1144,23 +1332,34 @@ PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array) { return ret; int length = mono_array_length(p_array); ret.resize(length); - Vector2 *w = ret.ptrw(); + Vector2 *dst = ret.ptrw(); - for (int i = 0; i < length; i++) { - w[i] = MARSHALLED_IN(Vector2, (M_Vector2 *)mono_array_addr_with_size(p_array, sizeof(M_Vector2), i)); + if constexpr (InteropLayout::MATCHES_Vector2) { + const Vector2 *src = (const Vector2 *)mono_array_addr(p_array, Vector2, 0); + memcpy(dst, src, length); + } else { + for (int i = 0; i < length; i++) { + dst[i] = MARSHALLED_IN(Vector2, (M_Vector2 *)mono_array_addr_with_size(p_array, sizeof(M_Vector2), i)); + } } return ret; } MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array) { - const Vector3 *r = p_array.ptr(); + const Vector3 *src = p_array.ptr(); + int length = p_array.size(); - MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), p_array.size()); + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), length); - for (int i = 0; i < p_array.size(); i++) { - M_Vector3 *raw = (M_Vector3 *)mono_array_addr_with_size(ret, sizeof(M_Vector3), i); - *raw = MARSHALLED_OUT(Vector3, r[i]); + if constexpr (InteropLayout::MATCHES_Vector3) { + Vector3 *dst = (Vector3 *)mono_array_addr(ret, Vector3, 0); + memcpy(dst, src, length); + } else { + for (int i = 0; i < length; i++) { + M_Vector3 *raw = (M_Vector3 *)mono_array_addr_with_size(ret, sizeof(M_Vector3), i); + *raw = MARSHALLED_OUT(Vector3, src[i]); + } } return ret; @@ -1172,13 +1371,85 @@ PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array) { return ret; int length = mono_array_length(p_array); ret.resize(length); - Vector3 *w = ret.ptrw(); + Vector3 *dst = ret.ptrw(); - for (int i = 0; i < length; i++) { - w[i] = MARSHALLED_IN(Vector3, (M_Vector3 *)mono_array_addr_with_size(p_array, sizeof(M_Vector3), i)); + if constexpr (InteropLayout::MATCHES_Vector3) { + const Vector3 *src = (const Vector3 *)mono_array_addr(p_array, Vector3, 0); + memcpy(dst, src, length); + } else { + for (int i = 0; i < length; i++) { + dst[i] = MARSHALLED_IN(Vector3, (M_Vector3 *)mono_array_addr_with_size(p_array, sizeof(M_Vector3), i)); + } } return ret; } +Callable managed_to_callable(const M_Callable &p_managed_callable) { + if (p_managed_callable.delegate) { + // TODO: Use pooling for ManagedCallable instances. + CallableCustom *managed_callable = memnew(ManagedCallable(p_managed_callable.delegate)); + return Callable(managed_callable); + } else { + Object *target = p_managed_callable.target ? + unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_callable.target)) : + NULL; + StringName *method_ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name)); + StringName method = method_ptr ? *method_ptr : StringName(); + return Callable(target, method); + } +} + +M_Callable callable_to_managed(const Callable &p_callable) { + if (p_callable.is_custom()) { + CallableCustom *custom = p_callable.get_custom(); + CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func(); + + if (compare_equal_func == ManagedCallable::compare_equal_func_ptr) { + ManagedCallable *managed_callable = static_cast<ManagedCallable *>(custom); + return { + NULL, NULL, + managed_callable->get_delegate() + }; + } else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) { + SignalAwaiterCallable *signal_awaiter_callable = static_cast<SignalAwaiterCallable *>(custom); + return { + GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(signal_awaiter_callable->get_object())), + GDMonoUtils::create_managed_from(signal_awaiter_callable->get_signal()), + NULL + }; + } else if (compare_equal_func == EventSignalCallable::compare_equal_func_ptr) { + EventSignalCallable *event_signal_callable = static_cast<EventSignalCallable *>(custom); + return { + GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(event_signal_callable->get_object())), + GDMonoUtils::create_managed_from(event_signal_callable->get_signal()), + NULL + }; + } + + // Some other CallableCustom. We only support ManagedCallable. + return { NULL, NULL, NULL }; + } else { + MonoObject *target_managed = GDMonoUtils::unmanaged_get_managed(p_callable.get_object()); + MonoObject *method_string_name_managed = GDMonoUtils::create_managed_from(p_callable.get_method()); + return { target_managed, method_string_name_managed, NULL }; + } +} + +Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal) { + Object *owner = p_managed_signal.owner ? + unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_signal.owner)) : + NULL; + StringName *name_ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name)); + StringName name = name_ptr ? *name_ptr : StringName(); + return Signal(owner, name); +} + +M_SignalInfo signal_info_to_managed(const Signal &p_signal) { + Object *owner = p_signal.get_object(); + MonoObject *owner_managed = GDMonoUtils::unmanaged_get_managed(owner); + MonoObject *name_string_name_managed = GDMonoUtils::create_managed_from(p_signal.get_name()); + return { owner_managed, name_string_name_managed }; +} + } // namespace GDMonoMarshal diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 5db59522ce..7d09f46b00 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -33,6 +33,7 @@ #include "core/variant.h" +#include "../managed_callable.h" #include "gd_mono.h" #include "gd_mono_utils.h" @@ -62,7 +63,7 @@ T *unbox_addr(MonoObject *p_obj) { #define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x) #define BOX_ENUM(m_enum_class, x) mono_value_box(mono_domain_get(), m_enum_class, &x) -Variant::Type managed_to_variant_type(const ManagedType &p_type); +Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant = NULL); bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type); bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type); @@ -132,6 +133,11 @@ Array mono_array_to_Array(MonoArray *p_array); MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array); PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array); +// PackedInt64Array + +MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array); +PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array); + // PackedByteArray MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array); @@ -142,6 +148,11 @@ PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array); MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array); PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array); +// PackedFloat64Array + +MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array); +PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array); + // PackedStringArray MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array); @@ -162,11 +173,36 @@ PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array); MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array); PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array); +#pragma pack(push, 1) + +struct M_Callable { + MonoObject *target; + MonoObject *method_string_name; + MonoDelegate *delegate; +}; + +struct M_SignalInfo { + MonoObject *owner; + MonoObject *name_string_name; +}; + +#pragma pack(pop) + +// Callable +Callable managed_to_callable(const M_Callable &p_managed_callable); +M_Callable callable_to_managed(const Callable &p_callable); + +// SignalInfo +Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal); +M_SignalInfo signal_info_to_managed(const Signal &p_signal); + // Structures namespace InteropLayout { enum { + MATCHES_int = (sizeof(int32_t) == sizeof(uint32_t)), + MATCHES_float = (sizeof(float) == sizeof(uint32_t)), MATCHES_double = (sizeof(double) == sizeof(uint64_t)), @@ -181,10 +217,18 @@ enum { offsetof(Vector2, x) == (sizeof(real_t) * 0) && offsetof(Vector2, y) == (sizeof(real_t) * 1)), + MATCHES_Vector2i = (MATCHES_int && (sizeof(Vector2i) == (sizeof(int32_t) * 2)) && + offsetof(Vector2i, x) == (sizeof(int32_t) * 0) && + offsetof(Vector2i, y) == (sizeof(int32_t) * 1)), + MATCHES_Rect2 = (MATCHES_Vector2 && (sizeof(Rect2) == (sizeof(Vector2) * 2)) && offsetof(Rect2, position) == (sizeof(Vector2) * 0) && offsetof(Rect2, size) == (sizeof(Vector2) * 1)), + MATCHES_Rect2i = (MATCHES_Vector2i && (sizeof(Rect2i) == (sizeof(Vector2i) * 2)) && + offsetof(Rect2i, position) == (sizeof(Vector2i) * 0) && + offsetof(Rect2i, size) == (sizeof(Vector2i) * 1)), + MATCHES_Transform2D = (MATCHES_Vector2 && (sizeof(Transform2D) == (sizeof(Vector2) * 3))), // No field offset required, it stores an array MATCHES_Vector3 = (MATCHES_real_t && (sizeof(Vector3) == (sizeof(real_t) * 3)) && @@ -192,6 +236,11 @@ enum { offsetof(Vector3, y) == (sizeof(real_t) * 1) && offsetof(Vector3, z) == (sizeof(real_t) * 2)), + MATCHES_Vector3i = (MATCHES_int && (sizeof(Vector3i) == (sizeof(int32_t) * 3)) && + offsetof(Vector3i, x) == (sizeof(int32_t) * 0) && + offsetof(Vector3i, y) == (sizeof(int32_t) * 1) && + offsetof(Vector3i, z) == (sizeof(int32_t) * 2)), + MATCHES_Basis = (MATCHES_Vector3 && (sizeof(Basis) == (sizeof(Vector3) * 3))), // No field offset required, it stores an array MATCHES_Quat = (MATCHES_real_t && (sizeof(Quat) == (sizeof(real_t) * 4)) && @@ -222,8 +271,9 @@ enum { // In the future we may force this if we want to ref return these structs #ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY /* clang-format off */ -GD_STATIC_ASSERT(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 && - MATCHES_Basis && MATCHES_Quat && MATCHES_Transform && MATCHES_AABB && MATCHES_Color &&MATCHES_Plane); +static_assert(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 && + MATCHES_Basis && MATCHES_Quat && MATCHES_Transform && MATCHES_AABB && MATCHES_Color && + MATCHES_Plane && MATCHES_Vector2i && MATCHES_Rect2i && MATCHES_Vector3i); /* clang-format on */ #endif @@ -244,6 +294,19 @@ struct M_Vector2 { } }; +struct M_Vector2i { + int32_t x, y; + + static _FORCE_INLINE_ Vector2i convert_to(const M_Vector2i &p_from) { + return Vector2i(p_from.x, p_from.y); + } + + static _FORCE_INLINE_ M_Vector2i convert_from(const Vector2i &p_from) { + M_Vector2i ret = { p_from.x, p_from.y }; + return ret; + } +}; + struct M_Rect2 { M_Vector2 position; M_Vector2 size; @@ -259,6 +322,21 @@ struct M_Rect2 { } }; +struct M_Rect2i { + M_Vector2i position; + M_Vector2i size; + + static _FORCE_INLINE_ Rect2i convert_to(const M_Rect2i &p_from) { + return Rect2i(M_Vector2i::convert_to(p_from.position), + M_Vector2i::convert_to(p_from.size)); + } + + static _FORCE_INLINE_ M_Rect2i convert_from(const Rect2i &p_from) { + M_Rect2i ret = { M_Vector2i::convert_from(p_from.position), M_Vector2i::convert_from(p_from.size) }; + return ret; + } +}; + struct M_Transform2D { M_Vector2 elements[3]; @@ -291,6 +369,19 @@ struct M_Vector3 { } }; +struct M_Vector3i { + int32_t x, y, z; + + static _FORCE_INLINE_ Vector3i convert_to(const M_Vector3i &p_from) { + return Vector3i(p_from.x, p_from.y, p_from.z); + } + + static _FORCE_INLINE_ M_Vector3i convert_from(const Vector3i &p_from) { + M_Vector3i ret = { p_from.x, p_from.y, p_from.z }; + return ret; + } +}; + struct M_Basis { M_Vector3 elements[3]; @@ -416,9 +507,12 @@ struct M_Plane { } DECL_TYPE_MARSHAL_TEMPLATES(Vector2) +DECL_TYPE_MARSHAL_TEMPLATES(Vector2i) DECL_TYPE_MARSHAL_TEMPLATES(Rect2) +DECL_TYPE_MARSHAL_TEMPLATES(Rect2i) DECL_TYPE_MARSHAL_TEMPLATES(Transform2D) DECL_TYPE_MARSHAL_TEMPLATES(Vector3) +DECL_TYPE_MARSHAL_TEMPLATES(Vector3i) DECL_TYPE_MARSHAL_TEMPLATES(Basis) DECL_TYPE_MARSHAL_TEMPLATES(Quat) DECL_TYPE_MARSHAL_TEMPLATES(Transform) diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index 971c5ac737..e6a1ec2697 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -101,50 +101,41 @@ IMonoClassMember::Visibility GDMonoMethod::get_visibility() { } } -MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) { - if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) { - MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count()); +MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) const { + MonoException *exc = NULL; + MonoObject *ret; + + if (params_count > 0) { + MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), params_count); for (int i = 0; i < params_count; i++) { MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object(p_params[i], param_types[i]); mono_array_setref(params, i, boxed_param); } - MonoException *exc = NULL; - MonoObject *ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc); - - if (exc) { - ret = NULL; - if (r_exc) { - *r_exc = exc; - } else { - GDMonoUtils::set_pending_exception(exc); - } - } - - return ret; + ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc); } else { - MonoException *exc = NULL; - GDMonoUtils::runtime_invoke(mono_method, p_object, NULL, &exc); - - if (exc) { - if (r_exc) { - *r_exc = exc; - } else { - GDMonoUtils::set_pending_exception(exc); - } - } + ret = GDMonoUtils::runtime_invoke(mono_method, p_object, NULL, &exc); + } - return NULL; + if (exc) { + ret = NULL; + if (r_exc) { + *r_exc = exc; + } else { + GDMonoUtils::set_pending_exception(exc); + } } + + return ret; } -MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) { +MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) const { ERR_FAIL_COND_V(get_parameters_count() > 0, NULL); return invoke_raw(p_object, NULL, r_exc); } -MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) { +MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) const { MonoException *exc = NULL; MonoObject *ret = GDMonoUtils::runtime_invoke(mono_method, p_object, p_params, &exc); @@ -247,7 +238,7 @@ void GDMonoMethod::get_parameter_names(Vector<StringName> &names) const { } void GDMonoMethod::get_parameter_types(Vector<ManagedType> &types) const { - for (int i = 0; i < param_types.size(); ++i) { + for (int i = 0; i < params_count; ++i) { types.push_back(param_types[i]); } } @@ -256,13 +247,22 @@ const MethodInfo &GDMonoMethod::get_method_info() { if (!method_info_fetched) { method_info.name = name; - method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type), ""); + + bool nil_is_variant = false; + method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type, &nil_is_variant), ""); + if (method_info.return_val.type == Variant::NIL && nil_is_variant) + method_info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; Vector<StringName> names; get_parameter_names(names); for (int i = 0; i < params_count; ++i) { - method_info.arguments.push_back(PropertyInfo(GDMonoMarshal::managed_to_variant_type(param_types[i]), names[i])); + nil_is_variant = false; + PropertyInfo arg_info = PropertyInfo(GDMonoMarshal::managed_to_variant_type(param_types[i], &nil_is_variant), names[i]); + if (arg_info.type == Variant::NIL && nil_is_variant) + arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + + method_info.arguments.push_back(arg_info); } // TODO: default arguments diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h index b47e42dec2..d4379f41fe 100644 --- a/modules/mono/mono_gd/gd_mono_method.h +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -57,28 +57,28 @@ class GDMonoMethod : public IMonoClassMember { MonoMethod *mono_method; public: - virtual GDMonoClass *get_enclosing_class() const GD_FINAL; + virtual GDMonoClass *get_enclosing_class() const final; - virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_METHOD; } + virtual MemberType get_member_type() const final { return MEMBER_TYPE_METHOD; } - virtual StringName get_name() const GD_FINAL { return name; } + virtual StringName get_name() const final { return name; } - virtual bool is_static() GD_FINAL; + virtual bool is_static() final; - virtual Visibility get_visibility() GD_FINAL; + virtual Visibility get_visibility() final; - virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL; - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL; + virtual bool has_attribute(GDMonoClass *p_attr_class) final; + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final; void fetch_attributes(); - _FORCE_INLINE_ MonoMethod *get_mono_ptr() { return mono_method; } + _FORCE_INLINE_ MonoMethod *get_mono_ptr() const { return mono_method; } - _FORCE_INLINE_ int get_parameters_count() { return params_count; } - _FORCE_INLINE_ ManagedType get_return_type() { return return_type; } + _FORCE_INLINE_ int get_parameters_count() const { return params_count; } + _FORCE_INLINE_ ManagedType get_return_type() const { return return_type; } - MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL); - MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = NULL); - MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL); + MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL) const; + MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = NULL) const; + MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL) const; String get_full_name(bool p_signature = false) const; String get_full_name_no_class() const; diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h index 692037f76a..2aec64f565 100644 --- a/modules/mono/mono_gd/gd_mono_property.h +++ b/modules/mono/mono_gd/gd_mono_property.h @@ -47,17 +47,17 @@ class GDMonoProperty : public IMonoClassMember { MonoCustomAttrInfo *attributes; public: - virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; } + virtual GDMonoClass *get_enclosing_class() const final { return owner; } - virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_PROPERTY; } + virtual MemberType get_member_type() const final { return MEMBER_TYPE_PROPERTY; } - virtual StringName get_name() const GD_FINAL { return name; } + virtual StringName get_name() const final { return name; } - virtual bool is_static() GD_FINAL; - virtual Visibility get_visibility() GD_FINAL; + virtual bool is_static() final; + virtual Visibility get_visibility() final; - virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL; - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL; + virtual bool has_attribute(GDMonoClass *p_attr_class) final; + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final; void fetch_attributes(); bool has_getter(); diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index bc290f3a7f..cdb26ae61b 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -86,10 +86,9 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) { } } - Ref<MonoGCHandle> &gchandle = script_binding.gchandle; - ERR_FAIL_COND_V(gchandle.is_null(), NULL); + MonoGCHandleData &gchandle = script_binding.gchandle; - MonoObject *target = gchandle->get_target(); + MonoObject *target = gchandle.get_target(); if (target) return target; @@ -106,7 +105,7 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) { MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged); ERR_FAIL_NULL_V(mono_object, NULL); - gchandle->set_handle(MonoGCHandle::new_strong_handle(mono_object), MonoGCHandle::STRONG_HANDLE); + gchandle = MonoGCHandleData::new_strong_handle(mono_object); // Tie managed to unmanaged Reference *ref = Object::cast_to<Reference>(unmanaged); @@ -156,12 +155,35 @@ bool is_thread_attached() { return mono_domain_get() != NULL; } +uint32_t new_strong_gchandle(MonoObject *p_object) { + return mono_gchandle_new(p_object, /* pinned: */ false); +} + +uint32_t new_strong_gchandle_pinned(MonoObject *p_object) { + return mono_gchandle_new(p_object, /* pinned: */ true); +} + +uint32_t new_weak_gchandle(MonoObject *p_object) { + return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false); +} + +void free_gchandle(uint32_t p_gchandle) { + mono_gchandle_free(p_gchandle); +} + void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc) { GDMonoMethod *ctor = p_class->get_method(".ctor", 0); ERR_FAIL_NULL(ctor); ctor->invoke_raw(p_this_obj, NULL, r_exc); } +bool mono_delegate_equal(MonoDelegate *p_a, MonoDelegate *p_b) { + MonoException *exc = NULL; + MonoBoolean res = CACHED_METHOD_THUNK(Delegate, Equals).invoke((MonoObject *)p_a, (MonoObject *)p_b, &exc); + UNHANDLED_EXCEPTION(exc); + return (bool)res; +} + GDMonoClass *get_object_class(MonoObject *p_object) { return GDMono::get_singleton()->get_class(mono_object_get_class(p_object)); } @@ -220,6 +242,18 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa return mono_object; } +MonoObject *create_managed_from(const StringName &p_from) { + MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(StringName)); + ERR_FAIL_NULL_V(mono_object, NULL); + + // Construct + GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(StringName)); + + CACHED_FIELD(StringName, ptr)->set_value_raw(mono_object, memnew(StringName(p_from))); + + return mono_object; +} + MonoObject *create_managed_from(const NodePath &p_from) { MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(NodePath)); ERR_FAIL_NULL_V(mono_object, NULL); @@ -362,7 +396,11 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) { return; } - _TLS_RECURSION_GUARD_; + static thread_local bool _recursion_flag_ = false; + if (_recursion_flag_) + return; + _recursion_flag_ = true; + SCOPE_EXIT { _recursion_flag_ = false; }; ScriptLanguage::StackInfo separator; separator.file = String(); @@ -439,8 +477,7 @@ void set_pending_exception(MonoException *p_exc) { #endif } -_THREAD_LOCAL_(int) -current_invoke_count = 0; +thread_local int current_invoke_count = 0; MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc) { GD_MONO_BEGIN_RUNTIME_INVOKE; @@ -644,6 +681,10 @@ ScopeThreadAttach::~ScopeThreadAttach() { } } -// namespace Marshal +StringName get_native_godot_class_name(GDMonoClass *p_class) { + MonoObject *native_name_obj = p_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL); + StringName *ptr = GDMonoMarshal::unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(native_name_obj)); + return ptr ? *ptr : StringName(); +} } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index db9f99bfdc..fd02907d87 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -35,7 +35,6 @@ #include "../mono_gc_handle.h" #include "../utils/macros.h" -#include "../utils/thread_local.h" #include "gd_mono_header.h" #include "core/object.h" @@ -93,14 +92,22 @@ _FORCE_INLINE_ bool is_main_thread() { return mono_domain_get() != NULL && mono_thread_get_main() == mono_thread_current(); } +uint32_t new_strong_gchandle(MonoObject *p_object); +uint32_t new_strong_gchandle_pinned(MonoObject *p_object); +uint32_t new_weak_gchandle(MonoObject *p_object); +void free_gchandle(uint32_t p_gchandle); + void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc = NULL); +bool mono_delegate_equal(MonoDelegate *p_a, MonoDelegate *p_b); + GDMonoClass *get_object_class(MonoObject *p_object); GDMonoClass *type_get_proxy_class(const StringName &p_type); GDMonoClass *get_class_native_base(GDMonoClass *p_class); MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object); +MonoObject *create_managed_from(const StringName &p_from); MonoObject *create_managed_from(const NodePath &p_from); MonoObject *create_managed_from(const RID &p_from); MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class); @@ -123,7 +130,7 @@ void print_unhandled_exception(MonoException *p_exc); */ void set_pending_exception(MonoException *p_exc); -extern _THREAD_LOCAL_(int) current_invoke_count; +extern thread_local int current_invoke_count; _FORCE_INLINE_ int get_runtime_invoke_count() { return current_invoke_count; @@ -152,9 +159,11 @@ private: MonoThread *mono_thread; }; +StringName get_native_godot_class_name(GDMonoClass *p_class); + } // namespace GDMonoUtils -#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL))) +#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoUtils::get_native_godot_class_name(m_class)) #define GD_MONO_BEGIN_RUNTIME_INVOKE \ int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \ diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index 718bc2bb93..25e5a41215 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -36,103 +36,192 @@ #include "mono_gd/gd_mono_marshal.h" #include "mono_gd/gd_mono_utils.h" -namespace SignalAwaiterUtils { - -Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p_target, MonoObject *p_awaiter) { - +Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter) { ERR_FAIL_NULL_V(p_source, ERR_INVALID_DATA); ERR_FAIL_NULL_V(p_target, ERR_INVALID_DATA); - Ref<SignalAwaiterHandle> sa_con = memnew(SignalAwaiterHandle(p_awaiter)); -#ifdef DEBUG_ENABLED - sa_con->set_connection_target(p_target); -#endif + // TODO: Use pooling for ManagedCallable instances. + SignalAwaiterCallable *awaiter_callable = memnew(SignalAwaiterCallable(p_target, p_awaiter, p_signal)); + Callable callable = Callable(awaiter_callable); + + return p_source->connect(p_signal, callable, Vector<Variant>(), Object::CONNECT_ONESHOT); +} + +bool SignalAwaiterCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) { + const SignalAwaiterCallable *a = static_cast<const SignalAwaiterCallable *>(p_a); + const SignalAwaiterCallable *b = static_cast<const SignalAwaiterCallable *>(p_b); - Vector<Variant> binds; - binds.push_back(sa_con); + if (a->target_id != b->target_id) + return false; + + if (a->signal != b->signal) + return false; + + return true; +} + +bool SignalAwaiterCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) { + if (compare_equal(p_a, p_b)) + return false; + return p_a < p_b; +} + +uint32_t SignalAwaiterCallable::hash() const { + uint32_t hash = signal.hash(); + return hash_djb2_one_64(target_id, hash); +} - Error err = p_source->connect_compat(p_signal, sa_con.ptr(), - CSharpLanguage::get_singleton()->get_string_names()._signal_callback, - binds, Object::CONNECT_ONESHOT); +String SignalAwaiterCallable::get_as_text() const { + Object *base = ObjectDB::get_instance(target_id); + if (base) { + String class_name = base->get_class(); + Ref<Script> script = base->get_script(); + if (script.is_valid() && script->get_path().is_resource_file()) { - if (err != OK) { - // Set it as completed to prevent it from calling the failure callback when released. - // The awaiter will be aware of the failure by checking the returned error. - sa_con->set_completed(true); + class_name += "(" + script->get_path().get_file() + ")"; + } + return class_name + "::SignalAwaiterMiddleman::" + String(signal); + } else { + return "null::SignalAwaiterMiddleman::" + String(signal); } +} - return err; +CallableCustom::CompareEqualFunc SignalAwaiterCallable::get_compare_equal_func() const { + return compare_equal_func_ptr; } -} // namespace SignalAwaiterUtils -Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +CallableCustom::CompareLessFunc SignalAwaiterCallable::get_compare_less_func() const { + return compare_less_func_ptr; +} + +ObjectID SignalAwaiterCallable::get_object() const { + return target_id; +} + +void SignalAwaiterCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { + r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better + r_return_value = Variant(); #ifdef DEBUG_ENABLED - ERR_FAIL_COND_V_MSG(conn_target_id.is_valid() && !ObjectDB::get_instance(conn_target_id), Variant(), + ERR_FAIL_COND_MSG(target_id.is_valid() && !ObjectDB::get_instance(target_id), "Resumed after await, but class instance is gone."); #endif - if (p_argcount < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - return Variant(); - } + MonoArray *signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_argcount); - Ref<SignalAwaiterHandle> self = *p_args[p_argcount - 1]; - - if (self.is_null()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = p_argcount - 1; - r_error.expected = Variant::OBJECT; - return Variant(); + for (int i = 0; i < p_argcount; i++) { + MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_arguments[i]); + mono_array_setref(signal_args, i, boxed); } - set_completed(true); - - int signal_argc = p_argcount - 1; - MonoArray *signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), signal_argc); + MonoObject *awaiter = awaiter_handle.get_target(); - for (int i = 0; i < signal_argc; i++) { - MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_args[i]); - mono_array_setref(signal_args, i, boxed); + if (!awaiter) { + r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; + return; } MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback).invoke(get_target(), signal_args, &exc); - GD_MONO_END_RUNTIME_INVOKE; + CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback).invoke(awaiter, signal_args, &exc); if (exc) { GDMonoUtils::set_pending_exception(exc); - ERR_FAIL_V(Variant()); + ERR_FAIL(); + } else { + r_call_error.error = Callable::CallError::CALL_OK; + } +} + +SignalAwaiterCallable::SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal) : + target_id(p_target->get_instance_id()), + awaiter_handle(MonoGCHandleData::new_strong_handle(p_awaiter)), + signal(p_signal) { +} + +SignalAwaiterCallable::~SignalAwaiterCallable() { + awaiter_handle.release(); +} + +bool EventSignalCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) { + const EventSignalCallable *a = static_cast<const EventSignalCallable *>(p_a); + const EventSignalCallable *b = static_cast<const EventSignalCallable *>(p_b); + + if (a->owner != b->owner) + return false; + + if (a->event_signal != b->event_signal) + return false; + + return true; +} + +bool EventSignalCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) { + if (compare_equal(p_a, p_b)) + return false; + return p_a < p_b; +} + +uint32_t EventSignalCallable::hash() const { + uint32_t hash = event_signal->field->get_name().hash(); + return hash_djb2_one_64(owner->get_instance_id(), hash); +} + +String EventSignalCallable::get_as_text() const { + String class_name = owner->get_class(); + Ref<Script> script = owner->get_script(); + if (script.is_valid() && script->get_path().is_resource_file()) { + class_name += "(" + script->get_path().get_file() + ")"; } + StringName signal = event_signal->field->get_name(); + return class_name + "::EventSignalMiddleman::" + String(signal); +} - return Variant(); +CallableCustom::CompareEqualFunc EventSignalCallable::get_compare_equal_func() const { + return compare_equal_func_ptr; } -void SignalAwaiterHandle::_bind_methods() { +CallableCustom::CompareLessFunc EventSignalCallable::get_compare_less_func() const { + return compare_less_func_ptr; +} - ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &SignalAwaiterHandle::_signal_callback, MethodInfo("_signal_callback")); +ObjectID EventSignalCallable::get_object() const { + return owner->get_instance_id(); } -SignalAwaiterHandle::SignalAwaiterHandle(MonoObject *p_managed) : - MonoGCHandle(MonoGCHandle::new_strong_handle(p_managed), STRONG_HANDLE) {} +StringName EventSignalCallable::get_signal() const { + return event_signal->field->get_name(); +} -SignalAwaiterHandle::~SignalAwaiterHandle() { +void EventSignalCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { + r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better + r_return_value = Variant(); - if (!completed) { - MonoObject *awaiter = get_target(); + ERR_FAIL_COND(p_argcount < event_signal->invoke_method->get_parameters_count()); - if (awaiter) { - MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback).invoke(awaiter, &exc); - GD_MONO_END_RUNTIME_INVOKE; + CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(owner->get_script_instance()); + ERR_FAIL_NULL(csharp_instance); - if (exc) { - GDMonoUtils::set_pending_exception(exc); - ERR_FAIL(); - } - } + MonoObject *owner_managed = csharp_instance->get_mono_object(); + ERR_FAIL_NULL(owner_managed); + + MonoObject *delegate_field_value = event_signal->field->get_value(owner_managed); + if (!delegate_field_value) { + r_call_error.error = Callable::CallError::CALL_OK; + return; } + + MonoException *exc = NULL; + event_signal->invoke_method->invoke(delegate_field_value, p_arguments, &exc); + + if (exc) { + GDMonoUtils::set_pending_exception(exc); + ERR_FAIL(); + } else { + r_call_error.error = Callable::CallError::CALL_OK; + } +} + +EventSignalCallable::EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal) : + owner(p_owner), + event_signal(p_event_signal) { } diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h index 012f6e5bb3..c550315a23 100644 --- a/modules/mono/signal_awaiter_utils.h +++ b/modules/mono/signal_awaiter_utils.h @@ -32,40 +32,66 @@ #define SIGNAL_AWAITER_UTILS_H #include "core/reference.h" + +#include "csharp_script.h" #include "mono_gc_handle.h" -namespace SignalAwaiterUtils { +Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter); + +class SignalAwaiterCallable : public CallableCustom { + ObjectID target_id; + MonoGCHandleData awaiter_handle; + StringName signal; + +public: + static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b); + static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b); -Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p_target, MonoObject *p_awaiter); -} + static constexpr CompareEqualFunc compare_equal_func_ptr = &SignalAwaiterCallable::compare_equal; + static constexpr CompareEqualFunc compare_less_func_ptr = &SignalAwaiterCallable::compare_less; -class SignalAwaiterHandle : public MonoGCHandle { + uint32_t hash() const override; - GDCLASS(SignalAwaiterHandle, MonoGCHandle); + String get_as_text() const override; - bool completed; + CompareEqualFunc get_compare_equal_func() const override; + CompareLessFunc get_compare_less_func() const override; -#ifdef DEBUG_ENABLED - ObjectID conn_target_id; -#endif + ObjectID get_object() const override; - Variant _signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + _FORCE_INLINE_ StringName get_signal() const { return signal; } + + void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; + + SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal); + ~SignalAwaiterCallable(); +}; -protected: - static void _bind_methods(); +class EventSignalCallable : public CallableCustom { + Object *owner; + const CSharpScript::EventSignal *event_signal; public: - _FORCE_INLINE_ bool is_completed() { return completed; } - _FORCE_INLINE_ void set_completed(bool p_completed) { completed = p_completed; } + static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b); + static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b); + + static constexpr CompareEqualFunc compare_equal_func_ptr = &EventSignalCallable::compare_equal; + static constexpr CompareEqualFunc compare_less_func_ptr = &EventSignalCallable::compare_less; + + uint32_t hash() const override; + + String get_as_text() const override; + + CompareEqualFunc get_compare_equal_func() const override; + CompareLessFunc get_compare_less_func() const override; + + ObjectID get_object() const override; + + StringName get_signal() const; -#ifdef DEBUG_ENABLED - _FORCE_INLINE_ void set_connection_target(Object *p_target) { - conn_target_id = p_target->get_instance_id(); - } -#endif + void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; - SignalAwaiterHandle(MonoObject *p_managed); - ~SignalAwaiterHandle(); + EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal); }; #endif // SIGNAL_AWAITER_UTILS_H diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h index 754000dc14..8650d6cc09 100644 --- a/modules/mono/utils/macros.h +++ b/modules/mono/utils/macros.h @@ -36,38 +36,6 @@ #define _GD_VARNAME_CONCAT_(m_a, m_b, m_c) _GD_VARNAME_CONCAT_A_(m_a, m_b, m_c) #define GD_UNIQUE_NAME(m_name) _GD_VARNAME_CONCAT_(m_name, _, __COUNTER__) -// static assert -// TODO: Get rid of this macro once we upgrade to C++11 - -#ifdef __cpp_static_assert -#define GD_STATIC_ASSERT(m_cond) static_assert((m_cond), "Condition '" #m_cond "' failed") -#else -#define GD_STATIC_ASSERT(m_cond) typedef int GD_UNIQUE_NAME(godot_static_assert)[((m_cond) ? 1 : -1)] -#endif - -// final -// TODO: Get rid of this macro once we upgrade to C++11 - -#if (__cplusplus >= 201103L) -#define GD_FINAL final -#else -#define GD_FINAL -#endif - -// noreturn -// TODO: Get rid of this macro once we upgrade to C++11 - -#if (__cplusplus >= 201103L) -#define GD_NORETURN [[noreturn]] -#elif defined(__GNUC__) -#define GD_NORETURN __attribute__((noreturn)) -#elif defined(_MSC_VER) -#define GD_NORETURN __declspec(noreturn) -#else -#define GD_NORETURN -#pragma message "Macro GD_NORETURN will have no effect" -#endif - // unreachable #if defined(_MSC_VER) @@ -81,4 +49,25 @@ } while (true); #endif +namespace gdmono { + +template <typename F> +struct ScopeExit { + ScopeExit(F p_exit_func) : + exit_func(p_exit_func) {} + ~ScopeExit() { exit_func(); } + F exit_func; +}; + +class ScopeExitAux { +public: + template <typename F> + ScopeExit<F> operator+(F p_exit_func) { return ScopeExit<F>(p_exit_func); } +}; + +} // namespace gdmono + +#define SCOPE_EXIT \ + auto GD_UNIQUE_NAME(gd_scope_exit) = gdmono::ScopeExitAux() + [=]() + #endif // UTIL_MACROS_H diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h deleted file mode 100644 index b1cc2e37ea..0000000000 --- a/modules/mono/utils/thread_local.h +++ /dev/null @@ -1,177 +0,0 @@ -/*************************************************************************/ -/* thread_local.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef THREAD_LOCAL_H -#define THREAD_LOCAL_H - -#ifdef HAVE_CXX11_THREAD_LOCAL -#define _THREAD_LOCAL_(m_t) thread_local m_t -#else - -#if !defined(__GNUC__) && !defined(_MSC_VER) -#error Platform or compiler not supported -#endif - -#if defined(__GNUC__) - -#ifdef HAVE_GCC___THREAD -#define _THREAD_LOCAL_(m_t) __thread m_t -#else -#define USE_CUSTOM_THREAD_LOCAL -#endif - -#elif defined(_MSC_VER) - -#ifdef HAVE_DECLSPEC_THREAD -#define _THREAD_LOCAL_(m_t) __declspec(thread) m_t -#else -#define USE_CUSTOM_THREAD_LOCAL -#endif - -#endif // __GNUC__ _MSC_VER - -#endif // HAVE_CXX11_THREAD_LOCAL - -#ifdef USE_CUSTOM_THREAD_LOCAL -#define _THREAD_LOCAL_(m_t) ThreadLocal<m_t> -#endif - -#include "core/typedefs.h" - -#ifdef WINDOWS_ENABLED -#define _CALLBACK_FUNC_ __stdcall -#else -#define _CALLBACK_FUNC_ -#endif - -struct ThreadLocalStorage { - - void *get_value() const; - void set_value(void *p_value) const; - - void alloc(void(_CALLBACK_FUNC_ *p_destr_callback)(void *)); - void free(); - -private: - struct Impl; - Impl *pimpl; -}; - -template <typename T> -class ThreadLocal { - - ThreadLocalStorage storage; - - T init_val; - - static void _CALLBACK_FUNC_ destr_callback(void *tls_data) { - memdelete(static_cast<T *>(tls_data)); - } - - T *_tls_get_value() const { - void *tls_data = storage.get_value(); - - if (tls_data) - return static_cast<T *>(tls_data); - - T *data = memnew(T(init_val)); - - storage.set_value(data); - - return data; - } - - void _initialize(const T &p_init_val) { - init_val = p_init_val; - storage.alloc(&destr_callback); - } - -public: - ThreadLocal() { - _initialize(T()); - } - - ThreadLocal(const T &p_init_val) { - _initialize(p_init_val); - } - - ThreadLocal(const ThreadLocal &other) { - _initialize(*other._tls_get_value()); - } - - ~ThreadLocal() { - storage.free(); - } - - _FORCE_INLINE_ T *operator&() const { - return _tls_get_value(); - } - - _FORCE_INLINE_ operator T &() const { - return *_tls_get_value(); - } - - _FORCE_INLINE_ ThreadLocal &operator=(const T &val) { - T *ptr = _tls_get_value(); - *ptr = val; - return *this; - } -}; - -struct FlagScopeGuard { - - FlagScopeGuard(bool &p_flag) : - flag(p_flag) { - flag = !flag; - } - - ~FlagScopeGuard() { - flag = !flag; - } - -private: - bool &flag; -}; - -#undef _CALLBACK_FUNC_ - -#define _TLS_RECURSION_GUARD_V_(m_ret) \ - static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \ - if (_recursion_flag_) \ - return m_ret; \ - FlagScopeGuard _recursion_guard_(_recursion_flag_); - -#define _TLS_RECURSION_GUARD_ \ - static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \ - if (_recursion_flag_) \ - return; \ - FlagScopeGuard _recursion_guard_(_recursion_flag_); - -#endif // THREAD_LOCAL_H diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub index 6a72a519fe..44c7963cd3 100644 --- a/modules/ogg/SCsub +++ b/modules/ogg/SCsub @@ -3,6 +3,9 @@ Import('env') Import('env_modules') +# Only kept to build the thirdparty library used by the theora and webm +# modules. + env_ogg = env_modules.Clone() # Thirdparty source files diff --git a/modules/opus/SCsub b/modules/opus/SCsub index 1db5b0987e..fec2911d6d 100644 --- a/modules/opus/SCsub +++ b/modules/opus/SCsub @@ -3,7 +3,9 @@ Import('env') Import('env_modules') -stub = True +# Only kept to build the thirdparty library used by the webm module. +# AudioStreamOpus was dropped in 3.0 due to incompatibility with the new audio +# engine. If you want to port it, fetch it from the Git history. env_opus = env_modules.Clone() @@ -235,9 +237,5 @@ if env['builtin_opus']: env_thirdparty.disable_warnings() env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) -if not stub: - # Module files - env_opus.add_source_files(env.modules_sources, "*.cpp") -else: - # Module files - env_opus.add_source_files(env.modules_sources, "stub/register_types.cpp") +# Module files +env_opus.add_source_files(env.modules_sources, "register_types.cpp") diff --git a/modules/opus/audio_stream_opus.cpp b/modules/opus/audio_stream_opus.cpp deleted file mode 100644 index a983edee91..0000000000 --- a/modules/opus/audio_stream_opus.cpp +++ /dev/null @@ -1,379 +0,0 @@ -/*************************************************************************/ -/* audio_stream_opus.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "audio_stream_opus.h" - -/** - @author George Marques <george@gmarqu.es> -*/ - -const float AudioStreamPlaybackOpus::osrate = 48000.0f; - -int AudioStreamPlaybackOpus::_op_read_func(void *_stream, unsigned char *_ptr, int _nbytes) { - FileAccess *fa = (FileAccess *)_stream; - - if (fa->eof_reached()) - return 0; - - uint8_t *dst = (uint8_t *)_ptr; - - int read = fa->get_buffer(dst, _nbytes); - - return read; -} - -int AudioStreamPlaybackOpus::_op_seek_func(void *_stream, opus_int64 _offset, int _whence) { - -#ifdef SEEK_SET - FileAccess *fa = (FileAccess *)_stream; - - switch (_whence) { - case SEEK_SET: { - fa->seek(_offset); - } break; - case SEEK_CUR: { - fa->seek(fa->get_position() + _offset); - } break; - case SEEK_END: { - fa->seek_end(_offset); - } break; - default: { - ERR_PRINT("Opus seek function failure: Unexpected value in _whence\n"); - } - } - int ret = fa->eof_reached() ? -1 : 0; - return ret; -#else - return -1; // no seeking -#endif -} - -int AudioStreamPlaybackOpus::_op_close_func(void *_stream) { - if (!_stream) - return 0; - FileAccess *fa = (FileAccess *)_stream; - if (fa->is_open()) - fa->close(); - return 0; -} - -opus_int64 AudioStreamPlaybackOpus::_op_tell_func(void *_stream) { - FileAccess *_fa = (FileAccess *)_stream; - return (opus_int64)_fa->get_position(); -} - -void AudioStreamPlaybackOpus::_clear_stream() { - if (!stream_loaded) - return; - - op_free(opus_file); - _close_file(); - - stream_loaded = false; - stream_channels = 1; - playing = false; -} - -void AudioStreamPlaybackOpus::_close_file() { - if (f) { - memdelete(f); - f = NULL; - } -} - -Error AudioStreamPlaybackOpus::_load_stream() { - - ERR_FAIL_COND_V(!stream_valid, ERR_UNCONFIGURED); - - _clear_stream(); - if (file == "") - return ERR_INVALID_DATA; - - Error err; - f = FileAccess::open(file, FileAccess::READ, &err); - - ERR_FAIL_COND_V_MSG(err, err, "Cannot open file '" + file + "'."); - - int _err = 0; - - opus_file = op_open_callbacks(f, &_op_callbacks, NULL, 0, &_err); - - switch (_err) { - case OP_EREAD: { // - Can't read the file. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_FILE_CANT_READ); - } break; - case OP_EVERSION: // - Unrecognized version number. - case OP_ENOTFORMAT: // - Stream is not Opus data. - case OP_EIMPL: { // - Stream used non-implemented feature. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_FILE_UNRECOGNIZED); - } break; - case OP_EBADLINK: // - Failed to find old data after seeking. - case OP_EBADTIMESTAMP: // - Timestamp failed the validity checks. - case OP_EBADHEADER: { // - Invalid or missing Opus bitstream header. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_FILE_CORRUPT); - } break; - case OP_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_BUG); - } break; - } - repeats = 0; - stream_loaded = true; - - return OK; -} - -AudioStreamPlaybackOpus::AudioStreamPlaybackOpus() { - loops = false; - playing = false; - f = NULL; - stream_loaded = false; - stream_valid = false; - repeats = 0; - paused = true; - stream_channels = 0; - current_section = 0; - length = 0; - loop_restart_time = 0; - pre_skip = 0; - - _op_callbacks.read = _op_read_func; - _op_callbacks.seek = _op_seek_func; - _op_callbacks.tell = _op_tell_func; - _op_callbacks.close = _op_close_func; -} - -Error AudioStreamPlaybackOpus::set_file(const String &p_file) { - file = p_file; - stream_valid = false; - Error err; - f = FileAccess::open(file, FileAccess::READ, &err); - - ERR_FAIL_COND_V_MSG(err, err, "Cannot open file '" + file + "'."); - - int _err; - - opus_file = op_open_callbacks(f, &_op_callbacks, NULL, 0, &_err); - - switch (_err) { - case OP_EREAD: { // - Can't read the file. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_FILE_CANT_READ); - } break; - case OP_EVERSION: // - Unrecognized version number. - case OP_ENOTFORMAT: // - Stream is not Opus data. - case OP_EIMPL: { // - Stream used non-implemented feature. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_FILE_UNRECOGNIZED); - } break; - case OP_EBADLINK: // - Failed to find old data after seeking. - case OP_EBADTIMESTAMP: // - Timestamp failed the validity checks. - case OP_EBADHEADER: { // - Invalid or missing Opus bitstream header. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_FILE_CORRUPT); - } break; - case OP_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_BUG); - } break; - } - - const OpusHead *oinfo = op_head(opus_file, -1); - - stream_channels = oinfo->channel_count; - pre_skip = oinfo->pre_skip; - frames_mixed = pre_skip; - ogg_int64_t len = op_pcm_total(opus_file, -1); - if (len < 0) { - length = 0; - } else { - length = (len / osrate); - } - - op_free(opus_file); - memdelete(f); - f = NULL; - stream_valid = true; - - return OK; -} - -void AudioStreamPlaybackOpus::play(float p_from) { - if (playing) - stop(); - - if (_load_stream() != OK) - return; - - frames_mixed = pre_skip; - playing = true; - if (p_from > 0) { - seek(p_from); - } -} - -void AudioStreamPlaybackOpus::stop() { - _clear_stream(); - playing = false; -} - -void AudioStreamPlaybackOpus::seek(float p_time) { - if (!playing) return; - ogg_int64_t pcm_offset = (ogg_int64_t)(p_time * osrate); - bool ok = op_pcm_seek(opus_file, pcm_offset) == 0; - if (!ok) { - ERR_PRINT("Seek time over stream size."); - return; - } - frames_mixed = osrate * p_time; -} - -int AudioStreamPlaybackOpus::mix(int16_t *p_buffer, int p_frames) { - if (!playing) - return 0; - - int total = p_frames; - - while (true) { - - int todo = p_frames; - - if (todo < MIN_MIX) { - break; - } - - int ret = op_read(opus_file, (opus_int16 *)p_buffer, todo * stream_channels, ¤t_section); - if (ret < 0) { - playing = false; - ERR_BREAK_MSG(ret < 0, "Error reading Opus file: " + file + "."); - } else if (ret == 0) { // end of song, reload? - op_free(opus_file); - - _close_file(); - - f = FileAccess::open(file, FileAccess::READ); - - int errv = 0; - opus_file = op_open_callbacks(f, &_op_callbacks, NULL, 0, &errv); - if (errv != 0) { - playing = false; - break; // :( - } - - if (!has_loop()) { - playing = false; - repeats = 1; - break; - } - - if (loop_restart_time) { - bool ok = op_pcm_seek(opus_file, (loop_restart_time * osrate) + pre_skip) == 0; - if (!ok) { - playing = false; - ERR_PRINT("Loop restart time rejected"); - } - - frames_mixed = (loop_restart_time * osrate) + pre_skip; - } else { - frames_mixed = pre_skip; - } - repeats++; - continue; - } - - stream_channels = op_head(opus_file, current_section)->channel_count; - - frames_mixed += ret; - - p_buffer += ret * stream_channels; - p_frames -= ret; - } - - return total - p_frames; -} - -float AudioStreamPlaybackOpus::get_length() const { - if (!stream_loaded) { - if (const_cast<AudioStreamPlaybackOpus *>(this)->_load_stream() != OK) - return 0; - } - return length; -} - -float AudioStreamPlaybackOpus::get_playback_position() const { - - int32_t frames = int32_t(frames_mixed); - if (frames < 0) - frames = 0; - return double(frames) / osrate; -} - -int AudioStreamPlaybackOpus::get_minimum_buffer_size() const { - return MIN_MIX; -} - -AudioStreamPlaybackOpus::~AudioStreamPlaybackOpus() { - _clear_stream(); -} - -RES ResourceFormatLoaderAudioStreamOpus::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) { - if (r_error) - *r_error = OK; - - AudioStreamOpus *opus_stream = memnew(AudioStreamOpus); - opus_stream->set_file(p_path); - return Ref<AudioStreamOpus>(opus_stream); -} - -void ResourceFormatLoaderAudioStreamOpus::get_recognized_extensions(List<String> *p_extensions) const { - - p_extensions->push_back("opus"); -} -String ResourceFormatLoaderAudioStreamOpus::get_resource_type(const String &p_path) const { - - if (p_path.get_extension().to_lower() == "opus") - return "AudioStreamOpus"; - return ""; -} - -bool ResourceFormatLoaderAudioStreamOpus::handles_type(const String &p_type) const { - return (p_type == "AudioStream" || p_type == "AudioStreamOpus"); -} diff --git a/modules/opus/audio_stream_opus.h b/modules/opus/audio_stream_opus.h deleted file mode 100644 index 343cbc6b83..0000000000 --- a/modules/opus/audio_stream_opus.h +++ /dev/null @@ -1,142 +0,0 @@ -/*************************************************************************/ -/* audio_stream_opus.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef AUDIO_STREAM_OPUS_H -#define AUDIO_STREAM_OPUS_H - -#include "core/io/resource_loader.h" -#include "core/os/file_access.h" -#include "scene/resources/audio_stream.h" - -#include <opus/opusfile.h> - -/** - @author George Marques <george@gmarqu.es> -*/ - -class AudioStreamPlaybackOpus : public AudioStreamPlayback { - - GDCLASS(AudioStreamPlaybackOpus, AudioStreamPlayback); - - enum { - MIN_MIX = 1024 - }; - - FileAccess *f; - - OpusFileCallbacks _op_callbacks; - float length; - static int _op_read_func(void *_stream, unsigned char *_ptr, int _nbytes); - static int _op_seek_func(void *_stream, opus_int64 _offset, int _whence); - static int _op_close_func(void *_stream); - static opus_int64 _op_tell_func(void *_stream); - static const float osrate; - - String file; - int64_t frames_mixed; - - bool stream_loaded; - volatile bool playing; - OggOpusFile *opus_file; - int stream_channels; - int current_section; - int pre_skip; - - bool paused; - bool loops; - int repeats; - - Error _load_stream(); - void _clear_stream(); - void _close_file(); - - bool stream_valid; - float loop_restart_time; - -public: - Error set_file(const String &p_file); - - virtual void play(float p_from = 0); - virtual void stop(); - virtual bool is_playing() const { return playing; } - - virtual void set_loop_restart_time(float p_time) { loop_restart_time = p_time; } - - virtual void set_paused(bool p_paused) { paused = p_paused; } - virtual bool is_paused() const { return paused; } - - virtual void set_loop(bool p_enable) { loops = p_enable; } - virtual bool has_loop() const { return loops; } - - virtual float get_length() const; - - virtual String get_stream_name() const { return ""; } - - virtual int get_loop_count() const { return repeats; } - - virtual float get_playback_position() const; - virtual void seek(float p_time); - - virtual int get_channels() const { return stream_channels; } - virtual int get_mix_rate() const { return osrate; } - - virtual int get_minimum_buffer_size() const; - - virtual int mix(int16_t *p_buffer, int p_frames); - - AudioStreamPlaybackOpus(); - ~AudioStreamPlaybackOpus(); -}; - -class AudioStreamOpus : public AudioStream { - - GDCLASS(AudioStreamOpus, AudioStream); - - String file; - -public: - Ref<AudioStreamPlayback> instance_playback() { - Ref<AudioStreamPlaybackOpus> pb = memnew(AudioStreamPlaybackOpus); - pb->set_file(file); - return pb; - } - - void set_file(const String &p_file) { file = p_file; } -}; - -class ResourceFormatLoaderAudioStreamOpus : public ResourceFormatLoader { -public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; -}; - -#endif // AUDIO_STREAM_OPUS_H diff --git a/modules/opus/config.py b/modules/opus/config.py index a1cde10ea0..1c8cd12a2d 100644 --- a/modules/opus/config.py +++ b/modules/opus/config.py @@ -3,11 +3,3 @@ def can_build(env, platform): def configure(env): pass - -def get_doc_classes(): - return [ - "AudioStreamOpus", - ] - -def get_doc_path(): - return "doc_classes" diff --git a/modules/opus/register_types.cpp b/modules/opus/register_types.cpp index dff309b49c..a4329e142c 100644 --- a/modules/opus/register_types.cpp +++ b/modules/opus/register_types.cpp @@ -30,23 +30,8 @@ #include "register_types.h" -#include "audio_stream_opus.h" +// Dummy module as libvorbis is needed by other modules (theora ...) -static Ref<ResourceFormatLoaderAudioStreamOpus> opus_stream_loader; +void register_opus_types() {} -void register_opus_types() { - // Sorry guys, do not enable this unless you can figure out a way - // to get Opus to not do any memory allocation or system calls - // in the audio thread. - // Currently the implementation even reads files from the audio thread, - // and this is not how audio programming works. - - //opus_stream_loader = memnew(ResourceFormatLoaderAudioStreamOpus); - //ResourceLoader::add_resource_format_loader(opus_stream_loader); - //ClassDB::register_class<AudioStreamOpus>(); -} - -void unregister_opus_types() { - - //memdelete(opus_stream_loader); -} +void unregister_opus_types() {} diff --git a/modules/opus/stub/register_types.h b/modules/opus/stub/register_types.h deleted file mode 100644 index 445be4e166..0000000000 --- a/modules/opus/stub/register_types.h +++ /dev/null @@ -1,32 +0,0 @@ -/*************************************************************************/ -/* register_types.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -void register_opus_types(); -void unregister_opus_types(); diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub index 3824fdd789..bde4359595 100644 --- a/modules/vorbis/SCsub +++ b/modules/vorbis/SCsub @@ -3,6 +3,9 @@ Import('env') Import('env_modules') +# Only kept to build the thirdparty library used by the theora and webm +# modules. We now use stb_vorbis for AudioStreamOGGVorbis. + env_vorbis = env_modules.Clone() stub = True @@ -50,9 +53,5 @@ if env['builtin_libvorbis']: env_thirdparty.disable_warnings() env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) -if not stub: - # Module files - env_vorbis.add_source_files(env.modules_sources, "*.cpp") -else: - # Module files - env_vorbis.add_source_files(env.modules_sources, "stub/register_types.cpp") +# Module files +env_vorbis.add_source_files(env.modules_sources, "register_types.cpp") diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp deleted file mode 100644 index 1e8ee9b17e..0000000000 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ /dev/null @@ -1,406 +0,0 @@ -/*************************************************************************/ -/* audio_stream_ogg_vorbis.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "audio_stream_ogg_vorbis.h" - -size_t AudioStreamPlaybackOGGVorbis::_ov_read_func(void *p_dst, size_t p_data, size_t p_count, void *_f) { - - //printf("read to %p, %i bytes, %i nmemb, %p\n",p_dst,p_data,p_count,_f); - FileAccess *fa = (FileAccess *)_f; - size_t read_total = p_data * p_count; - - if (fa->eof_reached()) - return 0; - - uint8_t *dst = (uint8_t *)p_dst; - - int read = fa->get_buffer(dst, read_total); - - return read; -} - -int AudioStreamPlaybackOGGVorbis::_ov_seek_func(void *_f, ogg_int64_t offs, int whence) { - - //printf("seek to %p, offs %i, whence %i\n",_f,(int)offs,whence); - -#ifdef SEEK_SET - //printf("seek set defined\n"); - FileAccess *fa = (FileAccess *)_f; - - if (whence == SEEK_SET) { - - fa->seek(offs); - } else if (whence == SEEK_CUR) { - - fa->seek(fa->get_position() + offs); - } else if (whence == SEEK_END) { - - fa->seek_end(offs); - } else { - - ERR_PRINT("Vorbis seek function failure: Unexpected value in _whence\n"); - } - int ret = fa->eof_reached() ? -1 : 0; - //printf("returning %i\n",ret); - return ret; - -#else - return -1; // no seeking -#endif -} -int AudioStreamPlaybackOGGVorbis::_ov_close_func(void *_f) { - - //printf("close %p\n",_f); - if (!_f) - return 0; - FileAccess *fa = (FileAccess *)_f; - if (fa->is_open()) - fa->close(); - return 0; -} -long AudioStreamPlaybackOGGVorbis::_ov_tell_func(void *_f) { - - //printf("close %p\n",_f); - - FileAccess *fa = (FileAccess *)_f; - return fa->get_position(); -} - -int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_buffer, int p_frames) { - - if (!playing) - return 0; - - int total = p_frames; - while (true) { - - int todo = p_frames; - - if (todo < MIN_MIX) { - break; - } - -#ifdef BIG_ENDIAN_ENABLED - long ret = ov_read(&vf, (char *)p_buffer, todo * stream_channels * sizeof(int16_t), 1, 2, 1, ¤t_section); -#else - long ret = ov_read(&vf, (char *)p_buffer, todo * stream_channels * sizeof(int16_t), 0, 2, 1, ¤t_section); -#endif - - if (ret < 0) { - - playing = false; - ERR_BREAK_MSG(ret < 0, "Error reading OGG Vorbis file: " + file + "."); - } else if (ret == 0) { // end of song, reload? - - ov_clear(&vf); - - _close_file(); - - if (!has_loop()) { - - playing = false; - repeats = 1; - break; - } - - f = FileAccess::open(file, FileAccess::READ); - - int errv = ov_open_callbacks(f, &vf, NULL, 0, _ov_callbacks); - if (errv != 0) { - playing = false; - break; // :( - } - - if (loop_restart_time) { - bool ok = ov_time_seek(&vf, loop_restart_time) == 0; - if (!ok) { - playing = false; - ERR_PRINT("Loop restart time rejected"); - } - - frames_mixed = stream_srate * loop_restart_time; - } else { - - frames_mixed = 0; - } - repeats++; - continue; - } - - ret /= stream_channels; - ret /= sizeof(int16_t); - - frames_mixed += ret; - - p_buffer += ret * stream_channels; - p_frames -= ret; - } - - return total - p_frames; -} - -void AudioStreamPlaybackOGGVorbis::play(float p_from) { - - if (playing) - stop(); - - if (_load_stream() != OK) - return; - - frames_mixed = 0; - playing = true; - if (p_from > 0) { - seek(p_from); - } -} - -void AudioStreamPlaybackOGGVorbis::_close_file() { - - if (f) { - - memdelete(f); - f = NULL; - } -} - -bool AudioStreamPlaybackOGGVorbis::is_playing() const { - return playing; -} -void AudioStreamPlaybackOGGVorbis::stop() { - - _clear_stream(); - playing = false; - //_clear(); -} - -float AudioStreamPlaybackOGGVorbis::get_playback_position() const { - - int32_t frames = int32_t(frames_mixed); - if (frames < 0) - frames = 0; - return double(frames) / stream_srate; -} - -void AudioStreamPlaybackOGGVorbis::seek(float p_time) { - - if (!playing) - return; - bool ok = ov_time_seek(&vf, p_time) == 0; - ERR_FAIL_COND(!ok); - frames_mixed = stream_srate * p_time; -} - -String AudioStreamPlaybackOGGVorbis::get_stream_name() const { - - return ""; -} - -void AudioStreamPlaybackOGGVorbis::set_loop(bool p_enable) { - - loops = p_enable; -} - -bool AudioStreamPlaybackOGGVorbis::has_loop() const { - - return loops; -} - -int AudioStreamPlaybackOGGVorbis::get_loop_count() const { - return repeats; -} - -Error AudioStreamPlaybackOGGVorbis::set_file(const String &p_file) { - - file = p_file; - stream_valid = false; - Error err; - f = FileAccess::open(file, FileAccess::READ, &err); - ERR_FAIL_COND_V_MSG(err, err, "Cannot open file '" + p_file + "'."); - - int errv = ov_open_callbacks(f, &vf, NULL, 0, _ov_callbacks); - switch (errv) { - - case OV_EREAD: { // - A read from media returned an error. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_FILE_CANT_READ); - } break; - case OV_EVERSION: // - Vorbis version mismatch. - case OV_ENOTVORBIS: { // - Bitstream is not Vorbis data. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_FILE_UNRECOGNIZED); - } break; - case OV_EBADHEADER: { // - Invalid Vorbis bitstream header. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_FILE_CORRUPT); - } break; - case OV_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_BUG); - } break; - } - const vorbis_info *vinfo = ov_info(&vf, -1); - stream_channels = vinfo->channels; - stream_srate = vinfo->rate; - length = ov_time_total(&vf, -1); - ov_clear(&vf); - memdelete(f); - f = NULL; - stream_valid = true; - - return OK; -} - -Error AudioStreamPlaybackOGGVorbis::_load_stream() { - - ERR_FAIL_COND_V(!stream_valid, ERR_UNCONFIGURED); - - _clear_stream(); - if (file == "") - return ERR_INVALID_DATA; - - Error err; - f = FileAccess::open(file, FileAccess::READ, &err); - ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + file + "'."); - - int errv = ov_open_callbacks(f, &vf, NULL, 0, _ov_callbacks); - switch (errv) { - - case OV_EREAD: { // - A read from media returned an error. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_FILE_CANT_READ); - } break; - case OV_EVERSION: // - Vorbis version mismatch. - case OV_ENOTVORBIS: { // - Bitstream is not Vorbis data. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_FILE_UNRECOGNIZED); - } break; - case OV_EBADHEADER: { // - Invalid Vorbis bitstream header. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_FILE_CORRUPT); - } break; - case OV_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption. - memdelete(f); - f = NULL; - ERR_FAIL_V(ERR_BUG); - } break; - } - repeats = 0; - stream_loaded = true; - - return OK; -} - -float AudioStreamPlaybackOGGVorbis::get_length() const { - - if (!stream_loaded) { - if (const_cast<AudioStreamPlaybackOGGVorbis *>(this)->_load_stream() != OK) - return 0; - } - return length; -} - -void AudioStreamPlaybackOGGVorbis::_clear_stream() { - - if (!stream_loaded) - return; - - ov_clear(&vf); - _close_file(); - - stream_loaded = false; - //stream_channels=1; - playing = false; -} - -void AudioStreamPlaybackOGGVorbis::set_paused(bool p_paused) { - - paused = p_paused; -} - -bool AudioStreamPlaybackOGGVorbis::is_paused() const { - - return paused; -} - -AudioStreamPlaybackOGGVorbis::AudioStreamPlaybackOGGVorbis() { - - loops = false; - playing = false; - _ov_callbacks.read_func = _ov_read_func; - _ov_callbacks.seek_func = _ov_seek_func; - _ov_callbacks.close_func = _ov_close_func; - _ov_callbacks.tell_func = _ov_tell_func; - f = NULL; - stream_loaded = false; - stream_valid = false; - repeats = 0; - paused = true; - stream_channels = 0; - stream_srate = 0; - current_section = 0; - length = 0; - loop_restart_time = 0; -} - -AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() { - - _clear_stream(); -} - -RES ResourceFormatLoaderAudioStreamOGGVorbis::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) { - if (r_error) - *r_error = OK; - - AudioStreamOGGVorbis *ogg_stream = memnew(AudioStreamOGGVorbis); - ogg_stream->set_file(p_path); - return Ref<AudioStreamOGGVorbis>(ogg_stream); -} - -void ResourceFormatLoaderAudioStreamOGGVorbis::get_recognized_extensions(List<String> *p_extensions) const { - - p_extensions->push_back("ogg"); -} -String ResourceFormatLoaderAudioStreamOGGVorbis::get_resource_type(const String &p_path) const { - - if (p_path.get_extension().to_lower() == "ogg") - return "AudioStreamOGGVorbis"; - return ""; -} - -bool ResourceFormatLoaderAudioStreamOGGVorbis::handles_type(const String &p_type) const { - return (p_type == "AudioStream" || p_type == "AudioStreamOGG" || p_type == "AudioStreamOGGVorbis"); -} diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h deleted file mode 100644 index db621f88d2..0000000000 --- a/modules/vorbis/audio_stream_ogg_vorbis.h +++ /dev/null @@ -1,137 +0,0 @@ -/*************************************************************************/ -/* audio_stream_ogg_vorbis.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef AUDIO_STREAM_OGG_VORBIS_H -#define AUDIO_STREAM_OGG_VORBIS_H - -#include "core/io/resource_loader.h" -#include "core/os/file_access.h" -#include "core/os/thread_safe.h" -#include "scene/resources/audio_stream.h" - -#include <vorbis/vorbisfile.h> - -class AudioStreamPlaybackOGGVorbis : public AudioStreamPlayback { - - GDCLASS(AudioStreamPlaybackOGGVorbis, AudioStreamPlayback); - - enum { - MIN_MIX = 1024 - }; - - FileAccess *f; - - ov_callbacks _ov_callbacks; - float length; - static size_t _ov_read_func(void *p_dst, size_t p_data, size_t p_count, void *_f); - static int _ov_seek_func(void *_f, ogg_int64_t offs, int whence); - static int _ov_close_func(void *_f); - static long _ov_tell_func(void *_f); - - String file; - int64_t frames_mixed; - - bool stream_loaded; - volatile bool playing; - OggVorbis_File vf; - int stream_channels; - int stream_srate; - int current_section; - - bool paused; - bool loops; - int repeats; - - Error _load_stream(); - void _clear_stream(); - void _close_file(); - - bool stream_valid; - float loop_restart_time; - -public: - Error set_file(const String &p_file); - - virtual void play(float p_from = 0); - virtual void stop(); - virtual bool is_playing() const; - - virtual void set_loop_restart_time(float p_time) { loop_restart_time = p_time; } - - virtual void set_paused(bool p_paused); - virtual bool is_paused() const; - - virtual void set_loop(bool p_enable); - virtual bool has_loop() const; - - virtual float get_length() const; - - virtual String get_stream_name() const; - - virtual int get_loop_count() const; - - virtual float get_playback_position() const; - virtual void seek(float p_time); - - virtual int get_channels() const { return stream_channels; } - virtual int get_mix_rate() const { return stream_srate; } - - virtual int get_minimum_buffer_size() const { return 0; } - virtual int mix(int16_t *p_buffer, int p_frames); - - AudioStreamPlaybackOGGVorbis(); - ~AudioStreamPlaybackOGGVorbis(); -}; - -class AudioStreamOGGVorbis : public AudioStream { - - GDCLASS(AudioStreamOGGVorbis, AudioStream); - - String file; - -public: - Ref<AudioStreamPlayback> instance_playback() { - Ref<AudioStreamPlaybackOGGVorbis> pb = memnew(AudioStreamPlaybackOGGVorbis); - pb->set_file(file); - return pb; - } - - void set_file(const String &p_file) { file = p_file; } -}; - -class ResourceFormatLoaderAudioStreamOGGVorbis : public ResourceFormatLoader { -public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; -}; - -#endif // AUDIO_STREAM_OGG_H diff --git a/modules/vorbis/register_types.cpp b/modules/vorbis/register_types.cpp index de055551ad..8874b3887b 100644 --- a/modules/vorbis/register_types.cpp +++ b/modules/vorbis/register_types.cpp @@ -30,19 +30,8 @@ #include "register_types.h" -#include "audio_stream_ogg_vorbis.h" +// Dummy module as libvorbis is needed by other modules (theora ...) -static Ref<ResourceFormatLoaderAudioStreamOGGVorbis> vorbis_stream_loader; +void register_vorbis_types() {} -void register_vorbis_types() { - - vorbis_stream_loader.instance(); - ResourceLoader::add_resource_format_loader(vorbis_stream_loader); - ClassDB::register_class<AudioStreamOGGVorbis>(); -} - -void unregister_vorbis_types() { - - ResourceLoader::remove_resource_format_loader(vorbis_stream_loader); - vorbis_stream_loader.unref(); -} +void unregister_vorbis_types() {} diff --git a/modules/vorbis/stub/register_types.h b/modules/vorbis/stub/register_types.h deleted file mode 100644 index 83d4904a87..0000000000 --- a/modules/vorbis/stub/register_types.h +++ /dev/null @@ -1,32 +0,0 @@ -/*************************************************************************/ -/* register_types.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -void register_vorbis_types(); -void unregister_vorbis_types(); diff --git a/platform/android/SCsub b/platform/android/SCsub index 46f0703a65..5ca5ce548c 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -1,8 +1,5 @@ #!/usr/bin/env python -from detect import get_ndk_version -from distutils.version import LooseVersion - Import('env') android_files = [ diff --git a/platform/android/detect.py b/platform/android/detect.py index 8f74ae0ef0..ff3ca0706c 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -164,7 +164,6 @@ def configure(env): env.Tool('gcc') env.use_windows_spawn_fix() - mt_link = True if (sys.platform.startswith("linux")): host_subpath = "linux-x86_64" elif (sys.platform.startswith("darwin")): @@ -173,12 +172,8 @@ def configure(env): if (platform.machine().endswith('64')): host_subpath = "windows-x86_64" else: - mt_link = False host_subpath = "windows" - if env["android_arch"] == "arm64v8": - mt_link = False - compiler_path = env["ANDROID_NDK_ROOT"] + "/toolchains/llvm/prebuilt/" + host_subpath + "/bin" gcc_toolchain_path = env["ANDROID_NDK_ROOT"] + "/toolchains/" + target_subpath + "/prebuilt/" + host_subpath tools_path = gcc_toolchain_path + "/" + abi_subpath + "/bin" diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index 5e37f538e9..1a3bb77670 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -71,6 +71,7 @@ android { packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' + doNotStrip '**/*.so' } // Both signing and zip-aligning will be done at export time diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index 976a5bda99..865b61956c 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -25,7 +25,7 @@ ext { sconsExt = org.gradle.internal.os.OperatingSystem.current().isWindows() ? ".bat" : "" supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"] - supportedTargets = ['release': "release", 'debug': "release_debug"] + supportedTargets = ["release", "debug"] // Used by gradle to specify which architecture to build for by default when running `./gradlew build`. // This command is usually used by Android Studio. @@ -136,14 +136,14 @@ task zipCustomBuild(type: Zip) { */ task generateGodotTemplates(type: GradleBuild) { // We exclude these gradle tasks so we can run the scons command manually. - for (String buildType : supportedTargets.keySet()) { + for (String buildType : supportedTargets) { startParameter.excludedTaskNames += ":lib:" + getSconsTaskName(buildType) } tasks = ["copyGodotPaymentPluginToAppModule"] // Only build the apks and aar files for which we have native shared libraries. - for (String target : supportedTargets.keySet()) { + for (String target : supportedTargets) { File targetLibs = new File("lib/libs/" + target) if (targetLibs != null && targetLibs.isDirectory() diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle index ca8aaf8af0..062f91e08e 100644 --- a/platform/android/java/lib/build.gradle +++ b/platform/android/java/lib/build.gradle @@ -26,6 +26,7 @@ android { packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' + doNotStrip '**/*.so' } sourceSets { @@ -56,7 +57,7 @@ android { // files is only setup for editing support. gradle.startParameter.excludedTaskNames += taskPrefix + "externalNativeBuild" + buildType - def releaseTarget = supportedTargets[buildType.toLowerCase()] + def releaseTarget = buildType.toLowerCase() if (releaseTarget == null || releaseTarget == "") { throw new GradleException("Invalid build type: " + buildType) } diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 7db4aa6597..4e605f9950 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -95,7 +95,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Locale; import org.godotengine.godot.input.GodotEditText; -import org.godotengine.godot.payments.PaymentsManager; import org.godotengine.godot.plugin.GodotPlugin; import org.godotengine.godot.plugin.GodotPluginRegistry; import org.godotengine.godot.utils.GodotNetUtils; @@ -174,21 +173,17 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe } public ResultCallback result_callback; - private PaymentsManager mPaymentsManager = null; - @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == PaymentsManager.REQUEST_CODE_FOR_PURCHASE) { - mPaymentsManager.processPurchaseResponse(resultCode, data); - } else if (result_callback != null) { + if (result_callback != null) { result_callback.callback(requestCode, resultCode, data); result_callback = null; - }; + } for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { plugin.onMainActivityResult(requestCode, resultCode, data); } - }; + } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { @@ -445,8 +440,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe result_callback = null; - mPaymentsManager = PaymentsManager.createManager(this).initService(); - godot_initialized = true; } @@ -603,7 +596,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe @Override protected void onDestroy() { - if (mPaymentsManager != null) mPaymentsManager.destroy(); for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { plugin.onMainDestroy(); } @@ -938,10 +930,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe return true; } - public PaymentsManager getPaymentsManager() { - return mPaymentsManager; - } - public boolean requestPermission(String p_name) { return PermissionsUtil.requestPermission(p_name, this); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/GodotPaymentInterface.java b/platform/android/java/lib/src/org/godotengine/godot/payments/GodotPaymentInterface.java deleted file mode 100644 index 6ac7338b30..0000000000 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/GodotPaymentInterface.java +++ /dev/null @@ -1,97 +0,0 @@ -/*************************************************************************/ -/* GodotPaymentInterface.java */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -package org.godotengine.godot.payments; - -public interface GodotPaymentInterface { - void purchase(String sku, String transactionId); - - void consumeUnconsumedPurchases(); - - String getSignature(); - - void callbackSuccess(String ticket, String signature, String sku); - - void callbackSuccessProductMassConsumed(String ticket, String signature, String sku); - - void callbackSuccessNoUnconsumedPurchases(); - - void callbackFailConsume(String message); - - void callbackFail(String message); - - void callbackCancel(); - - void callbackAlreadyOwned(String sku); - - int getPurchaseCallbackId(); - - void setPurchaseCallbackId(int purchaseCallbackId); - - String getPurchaseValidationUrlPrefix(); - - void setPurchaseValidationUrlPrefix(String url); - - String getAccessToken(); - - void setAccessToken(String accessToken); - - void setTransactionId(String transactionId); - - String getTransactionId(); - - // request purchased items are not consumed - void requestPurchased(); - - // callback for requestPurchased() - void callbackPurchased(String receipt, String signature, String sku); - - void callbackDisconnected(); - - void callbackConnected(); - - // true if connected, false otherwise - boolean isConnected(); - - // consume item automatically after purchase. default is true. - void setAutoConsume(boolean autoConsume); - - // consume a specific item - void consume(String sku); - - // query in app item detail info - void querySkuDetails(String[] list); - - void addSkuDetail(String itemJson); - - void completeSkuDetail(); - - void errorSkuDetail(String errorMessage); -} diff --git a/platform/android/java/lib/aidl/com/android/vending/billing/IInAppBillingService.aidl b/platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl index 0f2bcae338..0f2bcae338 100644 --- a/platform/android/java/lib/aidl/com/android/vending/billing/IInAppBillingService.aidl +++ b/platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/ConsumeTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java index 95cc48f536..c15bc232ce 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/ConsumeTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.payments; +package org.godotengine.godot.plugin.payment; import android.content.Context; import android.os.AsyncTask; diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java index 4a6b611c4d..c7d0a5de65 100644 --- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java @@ -30,6 +30,7 @@ package org.godotengine.godot.plugin.payment; +import android.content.Intent; import android.support.annotation.NonNull; import android.util.Log; import java.util.ArrayList; @@ -38,28 +39,40 @@ import java.util.List; import org.godotengine.godot.Dictionary; import org.godotengine.godot.Godot; import org.godotengine.godot.GodotLib; -import org.godotengine.godot.payments.GodotPaymentInterface; -import org.godotengine.godot.payments.PaymentsManager; import org.godotengine.godot.plugin.GodotPlugin; import org.json.JSONException; import org.json.JSONObject; -public class GodotPayment extends GodotPlugin implements GodotPaymentInterface { +public class GodotPayment extends GodotPlugin { private Integer purchaseCallbackId = 0; private String accessToken; private String purchaseValidationUrlPrefix; private String transactionId; - private PaymentsManager mPaymentManager; - private Dictionary mSkuDetails = new Dictionary(); + private final PaymentsManager mPaymentManager; + private final Dictionary mSkuDetails = new Dictionary(); public GodotPayment(Godot godot) { super(godot); - mPaymentManager = godot.getPaymentsManager(); - mPaymentManager.setBaseSingleton(this); + mPaymentManager = new PaymentsManager(godot, this); + mPaymentManager.initService(); } @Override + public void onMainActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == PaymentsManager.REQUEST_CODE_FOR_PURCHASE) { + mPaymentManager.processPurchaseResponse(resultCode, data); + } + } + + @Override + public void onMainDestroy() { + super.onMainDestroy(); + if (mPaymentManager != null) { + mPaymentManager.destroy(); + } + } + public void purchase(final String sku, final String transactionId) { runOnUiThread(new Runnable() { @Override @@ -69,7 +82,6 @@ public class GodotPayment extends GodotPlugin implements GodotPaymentInterface { }); } - @Override public void consumeUnconsumedPurchases() { runOnUiThread(new Runnable() { @Override @@ -81,89 +93,72 @@ public class GodotPayment extends GodotPlugin implements GodotPaymentInterface { private String signature; - @Override public String getSignature() { return this.signature; } - @Override public void callbackSuccess(String ticket, String signature, String sku) { GodotLib.calldeferred(purchaseCallbackId, "purchase_success", new Object[] { ticket, signature, sku }); } - @Override public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku) { Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > " + ticket + "," + signature + "," + sku); GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[] { ticket, signature, sku }); } - @Override public void callbackSuccessNoUnconsumedPurchases() { GodotLib.calldeferred(purchaseCallbackId, "consume_not_required", new Object[] {}); } - @Override public void callbackFailConsume(String message) { GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[] { message }); } - @Override public void callbackFail(String message) { GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[] { message }); } - @Override public void callbackCancel() { GodotLib.calldeferred(purchaseCallbackId, "purchase_cancel", new Object[] {}); } - @Override public void callbackAlreadyOwned(String sku) { GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[] { sku }); } - @Override public int getPurchaseCallbackId() { return purchaseCallbackId; } - @Override public void setPurchaseCallbackId(int purchaseCallbackId) { this.purchaseCallbackId = purchaseCallbackId; } - @Override public String getPurchaseValidationUrlPrefix() { return this.purchaseValidationUrlPrefix; } - @Override public void setPurchaseValidationUrlPrefix(String url) { this.purchaseValidationUrlPrefix = url; } - @Override public String getAccessToken() { return accessToken; } - @Override public void setAccessToken(String accessToken) { this.accessToken = accessToken; } - @Override public void setTransactionId(String transactionId) { this.transactionId = transactionId; } - @Override public String getTransactionId() { return this.transactionId; } // request purchased items are not consumed - @Override public void requestPurchased() { runOnUiThread(new Runnable() { @Override @@ -174,41 +169,34 @@ public class GodotPayment extends GodotPlugin implements GodotPaymentInterface { } // callback for requestPurchased() - @Override public void callbackPurchased(String receipt, String signature, String sku) { GodotLib.calldeferred(purchaseCallbackId, "has_purchased", new Object[] { receipt, signature, sku }); } - @Override public void callbackDisconnected() { GodotLib.calldeferred(purchaseCallbackId, "iap_disconnected", new Object[] {}); } - @Override public void callbackConnected() { GodotLib.calldeferred(purchaseCallbackId, "iap_connected", new Object[] {}); } // true if connected, false otherwise - @Override public boolean isConnected() { return mPaymentManager.isConnected(); } // consume item automatically after purchase. default is true. - @Override public void setAutoConsume(boolean autoConsume) { mPaymentManager.setAutoConsume(autoConsume); } // consume a specific item - @Override public void consume(String sku) { mPaymentManager.consume(sku); } // query in app item detail info - @Override public void querySkuDetails(String[] list) { List<String> nKeys = Arrays.asList(list); List<String> cKeys = Arrays.asList(mSkuDetails.get_keys()); @@ -225,7 +213,6 @@ public class GodotPayment extends GodotPlugin implements GodotPaymentInterface { } } - @Override public void addSkuDetail(String itemJson) { JSONObject o = null; try { @@ -244,12 +231,10 @@ public class GodotPayment extends GodotPlugin implements GodotPaymentInterface { } } - @Override public void completeSkuDetail() { GodotLib.calldeferred(purchaseCallbackId, "sku_details_complete", new Object[] { mSkuDetails }); } - @Override public void errorSkuDetail(String errorMessage) { GodotLib.calldeferred(purchaseCallbackId, "sku_details_error", new Object[] { errorMessage }); } @@ -263,6 +248,8 @@ public class GodotPayment extends GodotPlugin implements GodotPaymentInterface { @NonNull @Override public List<String> getPluginMethods() { - return Arrays.asList("purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume", "querySkuDetails", "isConnected"); + return Arrays.asList("purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", + "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", + "setAutoConsume", "consume", "querySkuDetails", "isConnected"); } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/HandlePurchaseTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java index 23d693cc8c..fe5685288b 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/HandlePurchaseTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.payments; +package org.godotengine.godot.plugin.payment; import android.app.Activity; import android.content.Intent; diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsCache.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java index 84a7eda6e0..d5919e3d9d 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsCache.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java @@ -28,11 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.payments; +package org.godotengine.godot.plugin.payment; import android.content.Context; import android.content.SharedPreferences; -import android.util.Log; public class PaymentsCache { diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java index 9bf6650f84..bded1f452f 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.payments; +package org.godotengine.godot.plugin.payment; import android.app.Activity; import android.content.ComponentName; @@ -52,20 +52,13 @@ public class PaymentsManager { public static final int REQUEST_CODE_FOR_PURCHASE = 0x1001; private static boolean auto_consume = true; - private Activity activity; + private final Activity activity; + private final GodotPayment godotPayment; IInAppBillingService mService; - public void setActivity(Activity activity) { - this.activity = activity; - } - - public static PaymentsManager createManager(Activity activity) { - PaymentsManager manager = new PaymentsManager(activity); - return manager; - } - - private PaymentsManager(Activity activity) { + PaymentsManager(Activity activity, GodotPayment godotPayment) { this.activity = activity; + this.godotPayment = godotPayment; } public PaymentsManager initService() { @@ -409,10 +402,4 @@ public class PaymentsManager { })) .start(); } - - private GodotPaymentInterface godotPayment; - - public void setBaseSingleton(GodotPaymentInterface godotPaymentInterface) { - this.godotPayment = godotPaymentInterface; - } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/PurchaseTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java index 09c9349124..eecd1d2151 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/PurchaseTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.payments; +package org.godotengine.godot.plugin.payment; import android.app.Activity; import android.app.PendingIntent; diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java index a101780511..b7bd638feb 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.payments; +package org.godotengine.godot.plugin.payment; import android.content.Context; import android.os.AsyncTask; diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java index 10c314aecf..179cc08ed1 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.payments; +package org.godotengine.godot.plugin.payment; import android.app.Activity; import android.app.ProgressDialog; @@ -42,7 +42,7 @@ import org.json.JSONObject; abstract public class ValidateTask { private Activity context; - private GodotPaymentInterface godotPayments; + private GodotPayment godotPayments; private ProgressDialog dialog; private String mSku; @@ -79,7 +79,7 @@ abstract public class ValidateTask { } } - public ValidateTask(Activity context, GodotPaymentInterface godotPayments) { + public ValidateTask(Activity context, GodotPayment godotPayments) { this.context = context; this.godotPayments = godotPayments; } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index dfd55e2577..8ef3bdd04e 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2948,12 +2948,14 @@ Control *Viewport::get_modal_stack_top() const { } String Viewport::get_configuration_warning() const { - /*if (get_parent() && !Object::cast_to<Control>(get_parent()) && !render_target) { return TTR("This viewport is not set as render target. If you intend for it to display its contents directly to the screen, make it a child of a Control so it can obtain a size. Otherwise, make it a RenderTarget and assign its internal texture to some node for display."); }*/ + if (size.x == 0 || size.y == 0) { + return TTR("Viewport size must be greater than 0 to render anything."); + } return String(); } diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index 98f1b2e460..e73f8b8fc4 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -2114,7 +2114,7 @@ const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[] { NULL, 0 } }; -bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) { +bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) { ERR_FAIL_COND_V(p_func->op != OP_CALL && p_func->op != OP_CONSTRUCT, false); @@ -2185,16 +2185,68 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p if (arg_idx < argcount) { - if (p_func->arguments[arg_idx + 1]->type != Node::TYPE_VARIABLE) { - _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable"); + if (p_func->arguments[arg_idx + 1]->type != Node::TYPE_VARIABLE && p_func->arguments[arg_idx + 1]->type != Node::TYPE_MEMBER && p_func->arguments[arg_idx + 1]->type != Node::TYPE_ARRAY) { + _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member."); return false; } - StringName var_name = static_cast<const VariableNode *>(p_func->arguments[arg_idx + 1])->name; + if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) { + ArrayNode *mn = static_cast<ArrayNode *>(p_func->arguments[arg_idx + 1]); + if (mn->is_const) { + fail = true; + } + } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) { + MemberNode *mn = static_cast<MemberNode *>(p_func->arguments[arg_idx + 1]); + if (mn->basetype_const) { + fail = true; + } + } else { // TYPE_VARIABLE + VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg_idx + 1]); + if (vn->is_const) { + fail = true; + } else { + StringName varname = vn->name; + if (shader->uniforms.has(varname)) { + fail = true; + } else { + if (p_builtin_types.has(varname)) { + BuiltInInfo info = p_builtin_types[varname]; + if (info.constant) { + fail = true; + } + } + } + } + } + if (fail) { + _set_error(vformat("Constant value cannot be passed for '%s' parameter!", "out")); + return false; + } + + StringName var_name; + if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) { + var_name = static_cast<const ArrayNode *>(p_func->arguments[arg_idx + 1])->name; + } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) { + Node *n = static_cast<const MemberNode *>(p_func->arguments[arg_idx + 1])->owner; + while (n->type == Node::TYPE_MEMBER) { + n = static_cast<const MemberNode *>(n)->owner; + } + if (n->type != Node::TYPE_VARIABLE && n->type != Node::TYPE_ARRAY) { + _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member."); + return false; + } + if (n->type == Node::TYPE_VARIABLE) { + var_name = static_cast<const VariableNode *>(n)->name; + } else { // TYPE_ARRAY + var_name = static_cast<const ArrayNode *>(n)->name; + } + } else { // TYPE_VARIABLE + var_name = static_cast<const VariableNode *>(p_func->arguments[arg_idx + 1])->name; + } const BlockNode *b = p_block; bool valid = false; while (b) { - if (b->variables.has(var_name)) { + if (b->variables.has(var_name) || p_builtin_types.has(var_name)) { valid = true; break; } @@ -2210,7 +2262,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p } if (!valid) { - _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' can only take a local variable"); + _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' can only take a local variable, array or member."); return false; } } @@ -3197,7 +3249,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (!ok) return NULL; - if (!_validate_function_call(p_block, func, &func->return_cache, &func->struct_name)) { + if (!_validate_function_call(p_block, p_builtin_types, func, &func->return_cache, &func->struct_name)) { _set_error("No matching constructor found for: '" + String(funcname->name) + "'"); return NULL; } @@ -3461,7 +3513,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (!ok) return NULL; - if (!_validate_function_call(p_block, func, &func->return_cache, &func->struct_name)) { + if (!_validate_function_call(p_block, p_builtin_types, func, &func->return_cache, &func->struct_name)) { _set_error("No matching function found for: '" + String(funcname->name) + "'"); return NULL; } @@ -6753,6 +6805,14 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct calltip += CharType(0xFFFF); } + if (shader->functions[i].function->arguments[j].qualifier != ArgumentQualifier::ARGUMENT_QUALIFIER_IN) { + if (shader->functions[i].function->arguments[j].qualifier == ArgumentQualifier::ARGUMENT_QUALIFIER_OUT) { + calltip += "out "; + } else { // ArgumentQualifier::ARGUMENT_QUALIFIER_INOUT + calltip += "inout "; + } + } + calltip += get_datatype_name(shader->functions[i].function->arguments[j].type); calltip += " "; calltip += shader->functions[i].function->arguments[j].name; @@ -6783,6 +6843,16 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct continue; } + int idx2 = 0; + int out_arg = -1; + while (builtin_func_out_args[idx2].name != nullptr) { + if (builtin_func_out_args[idx2].name == builtin_func_defs[idx].name) { + out_arg = builtin_func_out_args[idx2].argument; + break; + } + idx2++; + } + if (completion_function == builtin_func_defs[idx].name) { if (builtin_func_defs[idx].tag != completion_class) { @@ -6813,6 +6883,10 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct calltip += CharType(0xFFFF); } + if (out_arg >= 0 && i == out_arg) { + calltip += "out "; + } + calltip += get_datatype_name(builtin_func_defs[idx].args[i]); if (i == completion_argument) { diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h index f8391dc273..239805a18e 100644 --- a/servers/visual/shader_language.h +++ b/servers/visual/shader_language.h @@ -859,7 +859,7 @@ private: Error _validate_datatype(DataType p_type); bool _compare_datatypes_in_nodes(Node *a, Node *b) const; - bool _validate_function_call(BlockNode *p_block, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str); + bool _validate_function_call(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str); bool _parse_function_arguments(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, int *r_complete_arg = NULL); bool _propagate_function_call_sampler_uniform_settings(StringName p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat); bool _propagate_function_call_sampler_builtin_reference(StringName p_name, int p_argument, const StringName &p_builtin); |