summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/bind/core_bind.cpp6
-rw-r--r--core/bind/core_bind.h2
-rw-r--r--core/callable.cpp6
-rw-r--r--core/callable.h1
-rw-r--r--core/type_info.h2
-rw-r--r--doc/classes/AnimationNodeStateMachine.xml21
-rw-r--r--doc/classes/Array.xml12
-rw-r--r--doc/classes/Dictionary.xml2
-rw-r--r--doc/classes/Directory.xml3
-rw-r--r--doc/classes/Signal.xml2
-rw-r--r--doc/classes/Transform.xml4
-rw-r--r--doc/classes/Transform2D.xml4
-rw-r--r--editor/editor_node.cpp4
-rw-r--r--editor/editor_plugin.cpp5
-rw-r--r--editor/editor_plugin.h3
-rw-r--r--editor/filesystem_dock.cpp12
-rw-r--r--editor/filesystem_dock.h4
-rw-r--r--methods.py19
-rw-r--r--modules/mono/SCsub7
-rw-r--r--modules/mono/build_scripts/tls_configure.py36
-rw-r--r--modules/mono/config.py3
-rw-r--r--modules/mono/csharp_script.cpp546
-rw-r--r--modules/mono/csharp_script.h115
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs78
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs73
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs12
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BuildTab.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs61
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs2
-rw-r--r--modules/mono/editor/bindings_generator.cpp339
-rw-r--r--modules/mono/editor/bindings_generator.h49
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs31
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs395
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs37
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs59
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs29
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs262
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs12
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs82
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs26
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs380
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs18
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs402
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj7
-rw-r--r--modules/mono/glue/base_object_glue.cpp37
-rw-r--r--modules/mono/glue/base_object_glue.h8
-rw-r--r--modules/mono/glue/gd_glue.cpp10
-rw-r--r--modules/mono/glue/gd_glue.h2
-rw-r--r--modules/mono/glue/glue_header.h2
-rw-r--r--modules/mono/glue/string_name_glue.cpp (renamed from modules/opus/stub/register_types.cpp)34
-rw-r--r--modules/mono/glue/string_name_glue.h (renamed from modules/vorbis/stub/register_types.cpp)27
-rw-r--r--modules/mono/godotsharp_dirs.cpp6
-rw-r--r--modules/mono/managed_callable.cpp145
-rw-r--r--modules/mono/managed_callable.h (renamed from modules/mono/utils/thread_local.cpp)100
-rw-r--r--modules/mono/mono_gc_handle.cpp57
-rw-r--r--modules/mono/mono_gc_handle.h87
-rw-r--r--modules/mono/mono_gd/gd_mono.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp4
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp30
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h21
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp79
-rw-r--r--modules/mono/mono_gd/gd_mono_field.h15
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp11
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp413
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h100
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp64
-rw-r--r--modules/mono/mono_gd/gd_mono_method.h26
-rw-r--r--modules/mono/mono_gd/gd_mono_property.h14
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp57
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h15
-rw-r--r--modules/mono/signal_awaiter_utils.cpp213
-rw-r--r--modules/mono/signal_awaiter_utils.h68
-rw-r--r--modules/mono/utils/macros.h53
-rw-r--r--modules/mono/utils/thread_local.h177
-rw-r--r--modules/ogg/SCsub3
-rw-r--r--modules/opus/SCsub12
-rw-r--r--modules/opus/audio_stream_opus.cpp379
-rw-r--r--modules/opus/audio_stream_opus.h142
-rw-r--r--modules/opus/config.py8
-rw-r--r--modules/opus/register_types.cpp21
-rw-r--r--modules/opus/stub/register_types.h32
-rw-r--r--modules/vorbis/SCsub11
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp406
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.h137
-rw-r--r--modules/vorbis/register_types.cpp17
-rw-r--r--modules/vorbis/stub/register_types.h32
-rw-r--r--platform/android/SCsub3
-rw-r--r--platform/android/detect.py5
-rw-r--r--platform/android/java/app/build.gradle1
-rw-r--r--platform/android/java/build.gradle6
-rw-r--r--platform/android/java/lib/build.gradle3
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java18
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/payments/GodotPaymentInterface.java97
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl (renamed from platform/android/java/lib/aidl/com/android/vending/billing/IInAppBillingService.aidl)0
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/ConsumeTask.java)2
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java59
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/HandlePurchaseTask.java)2
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsCache.java)3
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java)23
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/PurchaseTask.java)2
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java)2
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java)6
-rw-r--r--scene/main/viewport.cpp4
-rw-r--r--servers/visual/shader_language.cpp90
-rw-r--r--servers/visual/shader_language.h2
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> &params) {
+bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params) {
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> &params = E->value();
+ for (int i = 0; i < params.size(); i++) {
+ const SignalParameter &param = 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> &params = event_signal.parameters;
+ for (int i = 0; i < params.size(); i++) {
+ const SignalParameter &param = 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> &params);
+ bool _get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params);
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, &current_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, &current_section);
-#else
- long ret = ov_read(&vf, (char *)p_buffer, todo * stream_channels * sizeof(int16_t), 0, 2, 1, &current_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);