summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/config/engine.cpp39
-rw-r--r--core/config/engine.h10
-rw-r--r--core/config/project_settings.cpp1
-rw-r--r--core/core_bind.cpp96
-rw-r--r--core/core_bind.h33
-rw-r--r--core/extension/extension_api_dump.cpp280
-rw-r--r--core/extension/extension_api_dump.h1
-rw-r--r--core/extension/gdextension.cpp109
-rw-r--r--core/extension/gdextension.h6
-rw-r--r--core/extension/gdextension_interface.cpp904
-rw-r--r--core/extension/gdextension_interface.h1930
-rw-r--r--core/extension/make_interface_dumper.py35
-rw-r--r--core/input/input.cpp2
-rw-r--r--core/input/input_event.cpp89
-rw-r--r--core/input/input_event.h24
-rw-r--r--core/io/http_client.cpp5
-rw-r--r--core/io/http_client_tcp.cpp1
-rw-r--r--core/io/resource_loader.cpp41
-rw-r--r--core/math/basis.cpp7
-rw-r--r--core/math/basis.h2
-rw-r--r--core/math/color.cpp12
-rw-r--r--core/math/transform_3d.cpp8
-rw-r--r--core/math/transform_3d.h4
-rw-r--r--core/object/class_db.cpp130
-rw-r--r--core/object/class_db.h74
-rw-r--r--core/object/message_queue.cpp36
-rw-r--r--core/object/message_queue.h6
-rw-r--r--core/object/object.cpp4
-rw-r--r--core/object/object.h55
-rw-r--r--core/object/ref_counted.h17
-rw-r--r--core/object/worker_thread_pool.cpp208
-rw-r--r--core/object/worker_thread_pool.h11
-rw-r--r--core/os/os.cpp62
-rw-r--r--core/os/os.h19
-rw-r--r--core/os/semaphore.h55
-rw-r--r--core/os/thread.cpp4
-rw-r--r--core/os/thread_safe.cpp46
-rw-r--r--core/os/thread_safe.h3
-rw-r--r--core/register_core_types.cpp7
-rw-r--r--core/string/string_name.h9
-rw-r--r--core/templates/safe_refcount.h16
-rw-r--r--core/templates/self_list.h11
-rw-r--r--core/variant/variant_call.cpp11
-rw-r--r--core/variant/variant_internal.h12
44 files changed, 3577 insertions, 858 deletions
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 814ad3d076..7fdea7d1aa 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -33,9 +33,7 @@
#include "core/authors.gen.h"
#include "core/config/project_settings.h"
#include "core/donors.gen.h"
-#include "core/io/json.h"
#include "core/license.gen.h"
-#include "core/os/os.h"
#include "core/variant/typed_array.h"
#include "core/version.h"
@@ -319,43 +317,6 @@ Engine::Engine() {
singleton = this;
}
-void Engine::startup_begin() {
- startup_benchmark_total_from = OS::get_singleton()->get_ticks_usec();
-}
-
-void Engine::startup_benchmark_begin_measure(const String &p_what) {
- startup_benchmark_section = p_what;
- startup_benchmark_from = OS::get_singleton()->get_ticks_usec();
-}
-void Engine::startup_benchmark_end_measure() {
- uint64_t total = OS::get_singleton()->get_ticks_usec() - startup_benchmark_from;
- double total_f = double(total) / double(1000000);
-
- startup_benchmark_json[startup_benchmark_section] = total_f;
-}
-
-void Engine::startup_dump(const String &p_to_file) {
- uint64_t total = OS::get_singleton()->get_ticks_usec() - startup_benchmark_total_from;
- double total_f = double(total) / double(1000000);
- startup_benchmark_json["total_time"] = total_f;
-
- if (!p_to_file.is_empty()) {
- Ref<FileAccess> f = FileAccess::open(p_to_file, FileAccess::WRITE);
- if (f.is_valid()) {
- Ref<JSON> json;
- json.instantiate();
- f->store_string(json->stringify(startup_benchmark_json, "\t", false, true));
- }
- } else {
- List<Variant> keys;
- startup_benchmark_json.get_key_list(&keys);
- print_line("STARTUP BENCHMARK:");
- for (const Variant &K : keys) {
- print_line("\t-", K, ": ", startup_benchmark_json[K], +" sec.");
- }
- }
-}
-
Engine::Singleton::Singleton(const StringName &p_name, Object *p_ptr, const StringName &p_class_name) :
name(p_name),
ptr(p_ptr),
diff --git a/core/config/engine.h b/core/config/engine.h
index 52408f4be1..5ea653ba6c 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -83,11 +83,6 @@ private:
String write_movie_path;
String shader_cache_path;
- Dictionary startup_benchmark_json;
- String startup_benchmark_section;
- uint64_t startup_benchmark_from = 0;
- uint64_t startup_benchmark_total_from = 0;
-
public:
static Engine *get_singleton();
@@ -163,11 +158,6 @@ public:
bool is_validation_layers_enabled() const;
int32_t get_gpu_index() const;
- void startup_begin();
- void startup_benchmark_begin_measure(const String &p_what);
- void startup_benchmark_end_measure();
- void startup_dump(const String &p_to_file);
-
Engine();
virtual ~Engine() {}
};
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 264259eb2f..c3df4a2ebe 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1290,6 +1290,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("display/window/energy_saving/keep_screen_on.editor", false);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "audio/buses/default_bus_layout", PROPERTY_HINT_FILE, "*.tres"), "res://default_bus_layout.tres");
+ GLOBAL_DEF_RST("audio/general/text_to_speech", false);
GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/2d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f);
GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/3d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f);
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 8fa7aad0ac..8771aa88cc 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -224,6 +224,14 @@ int OS::get_low_processor_usage_mode_sleep_usec() const {
return ::OS::get_singleton()->get_low_processor_usage_mode_sleep_usec();
}
+void OS::set_delta_smoothing(bool p_enabled) {
+ ::OS::get_singleton()->set_delta_smoothing(p_enabled);
+}
+
+bool OS::is_delta_smoothing_enabled() const {
+ return ::OS::get_singleton()->is_delta_smoothing_enabled();
+}
+
void OS::alert(const String &p_alert, const String &p_title) {
::OS::get_singleton()->alert(p_alert, p_title);
}
@@ -556,6 +564,9 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_low_processor_usage_mode_sleep_usec", "usec"), &OS::set_low_processor_usage_mode_sleep_usec);
ClassDB::bind_method(D_METHOD("get_low_processor_usage_mode_sleep_usec"), &OS::get_low_processor_usage_mode_sleep_usec);
+ ClassDB::bind_method(D_METHOD("set_delta_smoothing", "delta_smoothing_enabled"), &OS::set_delta_smoothing);
+ ClassDB::bind_method(D_METHOD("is_delta_smoothing_enabled"), &OS::is_delta_smoothing_enabled);
+
ClassDB::bind_method(D_METHOD("get_processor_count"), &OS::get_processor_count);
ClassDB::bind_method(D_METHOD("get_processor_name"), &OS::get_processor_name);
@@ -631,6 +642,7 @@ void OS::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "low_processor_usage_mode"), "set_low_processor_usage_mode", "is_in_low_processor_usage_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "low_processor_usage_mode_sleep_usec"), "set_low_processor_usage_mode_sleep_usec", "get_low_processor_usage_mode_sleep_usec");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "delta_smoothing"), "set_delta_smoothing", "is_delta_smoothing_enabled");
// Those default values need to be specified for the docs generator,
// to avoid using values from the documentation writer's own OS instance.
@@ -1178,14 +1190,30 @@ void Thread::_start_func(void *ud) {
String func_name = t->target_callable.is_custom() ? t->target_callable.get_custom()->get_as_text() : String(t->target_callable.get_method());
::Thread::set_name(func_name);
+ // To avoid a circular reference between the thread and the script which can possibly contain a reference
+ // to the thread, we will do the call (keeping a reference up to that point) and then break chains with it.
+ // When the call returns, we will reference the thread again if possible.
+ ObjectID th_instance_id = t->get_instance_id();
+ Callable target_callable = t->target_callable;
+ t = Ref<Thread>();
+
Callable::CallError ce;
- t->target_callable.callp(nullptr, 0, t->ret, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
+ Variant ret;
+ target_callable.callp(nullptr, 0, ret, ce);
+ // If script properly kept a reference to the thread, we should be able to re-reference it now
+ // (well, or if the call failed, since we had to break chains anyway because the outcome isn't known upfront).
+ t = Ref<Thread>(ObjectDB::get_instance(th_instance_id));
+ if (t.is_valid()) {
+ t->ret = ret;
t->running.clear();
- ERR_FAIL_MSG("Could not call function '" + func_name + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, nullptr, 0, ce) + ".");
+ } else {
+ // We could print a warning here, but the Thread object will be eventually destroyed
+ // noticing wait_to_finish() hasn't been called on it, and it will print a warning itself.
}
- t->running.clear();
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_MSG("Could not call function '" + func_name + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, nullptr, 0, ce) + ".");
+ }
}
Error Thread::start(const Callable &p_callable, Priority p_priority) {
@@ -1301,11 +1329,11 @@ Variant ClassDB::instantiate(const StringName &p_class) const {
}
}
-bool ClassDB::has_signal(StringName p_class, StringName p_signal) const {
+bool ClassDB::class_has_signal(StringName p_class, StringName p_signal) const {
return ::ClassDB::has_signal(p_class, p_signal);
}
-Dictionary ClassDB::get_signal(StringName p_class, StringName p_signal) const {
+Dictionary ClassDB::class_get_signal(StringName p_class, StringName p_signal) const {
MethodInfo signal;
if (::ClassDB::get_signal(p_class, p_signal, &signal)) {
return signal.operator Dictionary();
@@ -1314,7 +1342,7 @@ Dictionary ClassDB::get_signal(StringName p_class, StringName p_signal) const {
}
}
-TypedArray<Dictionary> ClassDB::get_signal_list(StringName p_class, bool p_no_inheritance) const {
+TypedArray<Dictionary> ClassDB::class_get_signal_list(StringName p_class, bool p_no_inheritance) const {
List<MethodInfo> signals;
::ClassDB::get_signal_list(p_class, &signals, p_no_inheritance);
TypedArray<Dictionary> ret;
@@ -1326,7 +1354,7 @@ TypedArray<Dictionary> ClassDB::get_signal_list(StringName p_class, bool p_no_in
return ret;
}
-TypedArray<Dictionary> ClassDB::get_property_list(StringName p_class, bool p_no_inheritance) const {
+TypedArray<Dictionary> ClassDB::class_get_property_list(StringName p_class, bool p_no_inheritance) const {
List<PropertyInfo> plist;
::ClassDB::get_property_list(p_class, &plist, p_no_inheritance);
TypedArray<Dictionary> ret;
@@ -1337,13 +1365,13 @@ TypedArray<Dictionary> ClassDB::get_property_list(StringName p_class, bool p_no_
return ret;
}
-Variant ClassDB::get_property(Object *p_object, const StringName &p_property) const {
+Variant ClassDB::class_get_property(Object *p_object, const StringName &p_property) const {
Variant ret;
::ClassDB::get_property(p_object, p_property, ret);
return ret;
}
-Error ClassDB::set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const {
+Error ClassDB::class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const {
Variant ret;
bool valid;
if (!::ClassDB::set_property(p_object, p_property, p_value, &valid)) {
@@ -1354,11 +1382,11 @@ Error ClassDB::set_property(Object *p_object, const StringName &p_property, cons
return OK;
}
-bool ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inheritance) const {
+bool ClassDB::class_has_method(StringName p_class, StringName p_method, bool p_no_inheritance) const {
return ::ClassDB::has_method(p_class, p_method, p_no_inheritance);
}
-TypedArray<Dictionary> ClassDB::get_method_list(StringName p_class, bool p_no_inheritance) const {
+TypedArray<Dictionary> ClassDB::class_get_method_list(StringName p_class, bool p_no_inheritance) const {
List<MethodInfo> methods;
::ClassDB::get_method_list(p_class, &methods, p_no_inheritance);
TypedArray<Dictionary> ret;
@@ -1376,7 +1404,7 @@ TypedArray<Dictionary> ClassDB::get_method_list(StringName p_class, bool p_no_in
return ret;
}
-PackedStringArray ClassDB::get_integer_constant_list(const StringName &p_class, bool p_no_inheritance) const {
+PackedStringArray ClassDB::class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance) const {
List<String> constants;
::ClassDB::get_integer_constant_list(p_class, &constants, p_no_inheritance);
@@ -1390,24 +1418,24 @@ PackedStringArray ClassDB::get_integer_constant_list(const StringName &p_class,
return ret;
}
-bool ClassDB::has_integer_constant(const StringName &p_class, const StringName &p_name) const {
+bool ClassDB::class_has_integer_constant(const StringName &p_class, const StringName &p_name) const {
bool success;
::ClassDB::get_integer_constant(p_class, p_name, &success);
return success;
}
-int64_t ClassDB::get_integer_constant(const StringName &p_class, const StringName &p_name) const {
+int64_t ClassDB::class_get_integer_constant(const StringName &p_class, const StringName &p_name) const {
bool found;
int64_t c = ::ClassDB::get_integer_constant(p_class, p_name, &found);
ERR_FAIL_COND_V(!found, 0);
return c;
}
-bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const {
+bool ClassDB::class_has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const {
return ::ClassDB::has_enum(p_class, p_name, p_no_inheritance);
}
-PackedStringArray ClassDB::get_enum_list(const StringName &p_class, bool p_no_inheritance) const {
+PackedStringArray ClassDB::class_get_enum_list(const StringName &p_class, bool p_no_inheritance) const {
List<StringName> enums;
::ClassDB::get_enum_list(p_class, &enums, p_no_inheritance);
@@ -1421,7 +1449,7 @@ PackedStringArray ClassDB::get_enum_list(const StringName &p_class, bool p_no_in
return ret;
}
-PackedStringArray ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance) const {
+PackedStringArray ClassDB::class_get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance) const {
List<StringName> constants;
::ClassDB::get_enum_constants(p_class, p_enum, &constants, p_no_inheritance);
@@ -1435,7 +1463,7 @@ PackedStringArray ClassDB::get_enum_constants(const StringName &p_class, const S
return ret;
}
-StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const {
+StringName ClassDB::class_get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const {
return ::ClassDB::get_integer_constant_enum(p_class, p_name, p_no_inheritance);
}
@@ -1452,27 +1480,27 @@ void ClassDB::_bind_methods() {
::ClassDB::bind_method(D_METHOD("can_instantiate", "class"), &ClassDB::can_instantiate);
::ClassDB::bind_method(D_METHOD("instantiate", "class"), &ClassDB::instantiate);
- ::ClassDB::bind_method(D_METHOD("class_has_signal", "class", "signal"), &ClassDB::has_signal);
- ::ClassDB::bind_method(D_METHOD("class_get_signal", "class", "signal"), &ClassDB::get_signal);
- ::ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &ClassDB::get_signal_list, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_has_signal", "class", "signal"), &ClassDB::class_has_signal);
+ ::ClassDB::bind_method(D_METHOD("class_get_signal", "class", "signal"), &ClassDB::class_get_signal);
+ ::ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &ClassDB::class_get_signal_list, DEFVAL(false));
- ::ClassDB::bind_method(D_METHOD("class_get_property_list", "class", "no_inheritance"), &ClassDB::get_property_list, DEFVAL(false));
- ::ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &ClassDB::get_property);
- ::ClassDB::bind_method(D_METHOD("class_set_property", "object", "property", "value"), &ClassDB::set_property);
+ ::ClassDB::bind_method(D_METHOD("class_get_property_list", "class", "no_inheritance"), &ClassDB::class_get_property_list, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &ClassDB::class_get_property);
+ ::ClassDB::bind_method(D_METHOD("class_set_property", "object", "property", "value"), &ClassDB::class_set_property);
- ::ClassDB::bind_method(D_METHOD("class_has_method", "class", "method", "no_inheritance"), &ClassDB::has_method, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_has_method", "class", "method", "no_inheritance"), &ClassDB::class_has_method, DEFVAL(false));
- ::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::get_method_list, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::class_get_method_list, DEFVAL(false));
- ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &ClassDB::get_integer_constant_list, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &ClassDB::class_get_integer_constant_list, DEFVAL(false));
- ::ClassDB::bind_method(D_METHOD("class_has_integer_constant", "class", "name"), &ClassDB::has_integer_constant);
- ::ClassDB::bind_method(D_METHOD("class_get_integer_constant", "class", "name"), &ClassDB::get_integer_constant);
+ ::ClassDB::bind_method(D_METHOD("class_has_integer_constant", "class", "name"), &ClassDB::class_has_integer_constant);
+ ::ClassDB::bind_method(D_METHOD("class_get_integer_constant", "class", "name"), &ClassDB::class_get_integer_constant);
- ::ClassDB::bind_method(D_METHOD("class_has_enum", "class", "name", "no_inheritance"), &ClassDB::has_enum, DEFVAL(false));
- ::ClassDB::bind_method(D_METHOD("class_get_enum_list", "class", "no_inheritance"), &ClassDB::get_enum_list, DEFVAL(false));
- ::ClassDB::bind_method(D_METHOD("class_get_enum_constants", "class", "enum", "no_inheritance"), &ClassDB::get_enum_constants, DEFVAL(false));
- ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_enum", "class", "name", "no_inheritance"), &ClassDB::get_integer_constant_enum, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_has_enum", "class", "name", "no_inheritance"), &ClassDB::class_has_enum, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_get_enum_list", "class", "no_inheritance"), &ClassDB::class_get_enum_list, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_get_enum_constants", "class", "enum", "no_inheritance"), &ClassDB::class_get_enum_constants, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_enum", "class", "name", "no_inheritance"), &ClassDB::class_get_integer_constant_enum, DEFVAL(false));
::ClassDB::bind_method(D_METHOD("is_class_enabled", "class"), &ClassDB::is_class_enabled);
}
diff --git a/core/core_bind.h b/core/core_bind.h
index be43ae2c9d..55c365eb7c 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -141,6 +141,9 @@ public:
void set_low_processor_usage_mode_sleep_usec(int p_usec);
int get_low_processor_usage_mode_sleep_usec() const;
+ void set_delta_smoothing(bool p_enabled);
+ bool is_delta_smoothing_enabled() const;
+
void alert(const String &p_alert, const String &p_title = "ALERT!");
void crash(const String &p_message);
@@ -424,26 +427,26 @@ public:
bool can_instantiate(const StringName &p_class) const;
Variant instantiate(const StringName &p_class) const;
- bool has_signal(StringName p_class, StringName p_signal) const;
- Dictionary get_signal(StringName p_class, StringName p_signal) const;
- TypedArray<Dictionary> get_signal_list(StringName p_class, bool p_no_inheritance = false) const;
+ bool class_has_signal(StringName p_class, StringName p_signal) const;
+ Dictionary class_get_signal(StringName p_class, StringName p_signal) const;
+ TypedArray<Dictionary> class_get_signal_list(StringName p_class, bool p_no_inheritance = false) const;
- TypedArray<Dictionary> get_property_list(StringName p_class, bool p_no_inheritance = false) const;
- Variant get_property(Object *p_object, const StringName &p_property) const;
- Error set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const;
+ TypedArray<Dictionary> class_get_property_list(StringName p_class, bool p_no_inheritance = false) const;
+ Variant class_get_property(Object *p_object, const StringName &p_property) const;
+ Error class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const;
- bool has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false) const;
+ bool class_has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false) const;
- TypedArray<Dictionary> get_method_list(StringName p_class, bool p_no_inheritance = false) const;
+ TypedArray<Dictionary> class_get_method_list(StringName p_class, bool p_no_inheritance = false) const;
- PackedStringArray get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const;
- bool has_integer_constant(const StringName &p_class, const StringName &p_name) const;
- int64_t get_integer_constant(const StringName &p_class, const StringName &p_name) const;
+ PackedStringArray class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const;
+ bool class_has_integer_constant(const StringName &p_class, const StringName &p_name) const;
+ int64_t class_get_integer_constant(const StringName &p_class, const StringName &p_name) const;
- bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const;
- PackedStringArray get_enum_list(const StringName &p_class, bool p_no_inheritance = false) const;
- PackedStringArray get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const;
- StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const;
+ bool class_has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const;
+ PackedStringArray class_get_enum_list(const StringName &p_class, bool p_no_inheritance = false) const;
+ PackedStringArray class_get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const;
+ StringName class_get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const;
bool is_class_enabled(StringName p_class) const;
diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp
index 79b0ebc641..0bcc8a44e8 100644
--- a/core/extension/extension_api_dump.cpp
+++ b/core/extension/extension_api_dump.cpp
@@ -880,6 +880,15 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d2["is_virtual"] = false;
d2["hash"] = method->get_hash();
+ Vector<uint32_t> compat_hashes = ClassDB::get_method_compatibility_hashes(class_name, method_name);
+ if (compat_hashes.size()) {
+ Array compatibility;
+ for (int i = 0; i < compat_hashes.size(); i++) {
+ compatibility.push_back(compat_hashes[i]);
+ }
+ d2["hash_compatibility"] = compatibility;
+ }
+
Vector<Variant> default_args = method->get_default_arguments();
Array arguments;
@@ -1056,4 +1065,275 @@ void GDExtensionAPIDump::generate_extension_json_file(const String &p_path) {
fa->store_string(text);
}
+static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_new_api, const String &p_base_array, const String &p_name_field, const Vector<String> &p_fields_to_compare, bool p_compare_hashes, const String &p_outer_class = String(), bool p_compare_operators = false) {
+ String base_array = p_outer_class + p_base_array;
+ if (!p_old_api.has(p_base_array)) {
+ return true; // May just not have this array and its still good. Probably added recently.
+ }
+ bool failed = false;
+ ERR_FAIL_COND_V_MSG(!p_new_api.has(p_base_array), false, "New API lacks base array: " + p_base_array);
+ Array new_api = p_new_api[p_base_array];
+ HashMap<String, Dictionary> new_api_assoc;
+
+ for (int i = 0; i < new_api.size(); i++) {
+ Dictionary elem = new_api[i];
+ ERR_FAIL_COND_V_MSG(!elem.has(p_name_field), false, "Validate extension JSON: Element of base_array '" + base_array + "' is missing field '" + p_name_field + "'. This is a bug.");
+ String name = elem[p_name_field];
+ if (p_compare_operators && elem.has("right_type")) {
+ name += " " + String(elem["right_type"]);
+ }
+ new_api_assoc.insert(name, elem);
+ }
+
+ Array old_api = p_old_api[p_base_array];
+ for (int i = 0; i < old_api.size(); i++) {
+ Dictionary old_elem = old_api[i];
+ if (!old_elem.has(p_name_field)) {
+ failed = true;
+ print_error("Validate extension JSON: JSON file: element of base array '" + base_array + "' is missing the field: '" + p_name_field + "'.");
+ continue;
+ }
+ String name = old_elem[p_name_field];
+ if (p_compare_operators && old_elem.has("right_type")) {
+ name += " " + String(old_elem["right_type"]);
+ }
+ if (!new_api_assoc.has(name)) {
+ failed = true;
+ print_error("Validate extension JSON: API was removed: " + base_array + "/" + name);
+ continue;
+ }
+
+ Dictionary new_elem = new_api_assoc[name];
+
+ for (int j = 0; j < p_fields_to_compare.size(); j++) {
+ String field = p_fields_to_compare[j];
+ bool optional = field.begins_with("*");
+ if (optional) {
+ // This is an optional field, but if exists it has to exist in both.
+ field = field.substr(1, field.length());
+ }
+
+ bool added = field.begins_with("+");
+ if (added) {
+ // Meaning this field must either exist or contents may not exist.
+ field = field.substr(1, field.length());
+ }
+
+ Variant old_value;
+
+ if (!old_elem.has(field)) {
+ if (optional) {
+ if (new_elem.has(field)) {
+ failed = true;
+ print_error("Validate extension JSON: JSON file: Field was added in a way that breaks compatibility '" + base_array + "/" + name + "': " + field);
+ }
+ } else if (added && new_elem.has(field)) {
+ // Should be ok, field now exists, should not be verified in prior versions where it does not.
+ } else {
+ failed = true;
+ print_error("Validate extension JSON: JSON file: Missing filed in '" + base_array + "/" + name + "': " + field);
+ }
+ continue;
+ } else {
+ old_value = old_elem[field];
+ }
+
+ if (!new_elem.has(field)) {
+ failed = true;
+ ERR_PRINT("Validate extension JSON: Missing filed in current API '" + base_array + "/" + name + "': " + field + ". This is a bug.");
+ continue;
+ }
+
+ Variant new_value = new_elem[field];
+
+ bool equal = Variant::evaluate(Variant::OP_EQUAL, old_value, new_value);
+ if (!equal) {
+ failed = true;
+ print_error("Validate extension JSON: Error: Field '" + base_array + "/" + name + "': " + field + " changed value in new API, from " + old_value.get_construct_string() + " to " + new_value.get_construct_string() + ".");
+ continue;
+ }
+ }
+
+ if (p_compare_hashes) {
+ if (!old_elem.has("hash")) {
+ if (old_elem.has("is_virtual") && bool(old_elem["is_virtual"]) && !new_elem.has("hash")) {
+ continue; // No hash for virtual methods, go on.
+ }
+
+ failed = true;
+ print_error("Validate extension JSON: JSON file: element of base array '" + base_array + "' is missing the field: 'hash'.");
+ continue;
+ }
+
+ uint64_t old_hash = old_elem["hash"];
+
+ if (!new_elem.has("hash")) {
+ failed = true;
+ print_error("Validate extension JSON: Error: Field '" + base_array + "' is missing the field: 'hash'.");
+ continue;
+ }
+
+ uint64_t new_hash = new_elem["hash"];
+ bool hash_found = false;
+ if (old_hash == new_hash) {
+ hash_found = true;
+ } else if (new_elem.has("hash_compatibility")) {
+ Array compatibility = new_elem["hash_compatibility"];
+ for (int j = 0; j < compatibility.size(); j++) {
+ new_hash = compatibility[j];
+ if (new_hash == old_hash) {
+ hash_found = true;
+ break;
+ }
+ }
+ }
+
+ if (!hash_found) {
+ failed = true;
+ print_error("Validate extension JSON: Error: Hash mismatch for '" + base_array + "/" + name + "'. This means that the function has changed and no compatibility function was provided.");
+ continue;
+ }
+ }
+ }
+
+ return !failed;
+}
+
+static bool compare_sub_dict_array(HashSet<String> &r_removed_classes_registered, const String &p_outer, const String &p_outer_name, const Dictionary &p_old_api, const Dictionary &p_new_api, const String &p_base_array, const String &p_name_field, const Vector<String> &p_fields_to_compare, bool p_compare_hashes, bool p_compare_operators = false) {
+ if (!p_old_api.has(p_outer)) {
+ return true; // May just not have this array and its still good. Probably added recently or optional.
+ }
+ bool failed = false;
+ ERR_FAIL_COND_V_MSG(!p_new_api.has(p_outer), false, "New API lacks base array: " + p_outer);
+ Array new_api = p_new_api[p_outer];
+ HashMap<String, Dictionary> new_api_assoc;
+
+ for (int i = 0; i < new_api.size(); i++) {
+ Dictionary elem = new_api[i];
+ ERR_FAIL_COND_V_MSG(!elem.has(p_outer_name), false, "Validate extension JSON: Element of base_array '" + p_outer + "' is missing field '" + p_outer_name + "'. This is a bug.");
+ new_api_assoc.insert(elem[p_outer_name], elem);
+ }
+
+ Array old_api = p_old_api[p_outer];
+
+ for (int i = 0; i < old_api.size(); i++) {
+ Dictionary old_elem = old_api[i];
+ if (!old_elem.has(p_outer_name)) {
+ failed = true;
+ print_error("Validate extension JSON: JSON file: element of base array '" + p_outer + "' is missing the field: '" + p_outer_name + "'.");
+ continue;
+ }
+ String name = old_elem[p_outer_name];
+ if (!new_api_assoc.has(name)) {
+ failed = true;
+ if (!r_removed_classes_registered.has(name)) {
+ print_error("Validate extension JSON: API was removed: " + p_outer + "/" + name);
+ r_removed_classes_registered.insert(name);
+ }
+ continue;
+ }
+
+ Dictionary new_elem = new_api_assoc[name];
+
+ if (!compare_dict_array(old_elem, new_elem, p_base_array, p_name_field, p_fields_to_compare, p_compare_hashes, p_outer + "/" + name + "/", p_compare_operators)) {
+ failed = true;
+ }
+ }
+
+ return !failed;
+}
+
+Error GDExtensionAPIDump::validate_extension_json_file(const String &p_path) {
+ Error error;
+ String text = FileAccess::get_file_as_string(p_path, &error);
+ if (error != OK) {
+ ERR_PRINT("Validate extension JSON: Could not open file '" + p_path + "'.");
+ return error;
+ }
+
+ Ref<JSON> json;
+ json.instantiate();
+ error = json->parse(text);
+ if (error != OK) {
+ ERR_PRINT("Validate extension JSON: Error parsing '" + p_path + "' at line " + itos(json->get_error_line()) + ": " + json->get_error_message());
+ return error;
+ }
+
+ Dictionary old_api = json->get_data();
+ Dictionary new_api = generate_extension_api();
+
+ { // Validate header:
+ Dictionary header = old_api["header"];
+ ERR_FAIL_COND_V(!header.has("version_major"), ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(!header.has("version_minor"), ERR_INVALID_DATA);
+ int major = header["version_major"];
+ int minor = header["version_minor"];
+
+ ERR_FAIL_COND_V_MSG(major != VERSION_MAJOR, ERR_INVALID_DATA, "JSON API dump is for a different engine version (" + itos(major) + ") than this one (" + itos(major) + ")");
+ ERR_FAIL_COND_V_MSG(minor > VERSION_MINOR, ERR_INVALID_DATA, "JSON API dump is for a newer version of the engine: " + itos(major) + "." + itos(minor));
+ }
+
+ bool failed = false;
+
+ HashSet<String> removed_classes_registered;
+
+ if (!compare_dict_array(old_api, new_api, "constants", "name", Vector<String>({ "value", "is_bitfield" }), false)) {
+ failed = true;
+ }
+
+ if (!compare_dict_array(old_api, new_api, "utility_functions", "name", Vector<String>({ "category" }), true)) {
+ failed = true;
+ }
+
+ if (!compare_sub_dict_array(removed_classes_registered, "builtin_classes", "name", old_api, new_api, "members", "name", { "type" }, false)) {
+ failed = true;
+ }
+
+ if (!compare_sub_dict_array(removed_classes_registered, "builtin_classes", "name", old_api, new_api, "constants", "name", { "type", "value" }, false)) {
+ failed = true;
+ }
+
+ if (!compare_sub_dict_array(removed_classes_registered, "builtin_classes", "name", old_api, new_api, "operators", "name", { "return_type" }, false, true)) {
+ failed = true;
+ }
+
+ if (!compare_sub_dict_array(removed_classes_registered, "builtin_classes", "name", old_api, new_api, "methods", "name", {}, true)) {
+ failed = true;
+ }
+
+ if (!compare_sub_dict_array(removed_classes_registered, "builtin_classes", "name", old_api, new_api, "constructors", "index", { "*arguments" }, false)) {
+ failed = true;
+ }
+
+ if (!compare_sub_dict_array(removed_classes_registered, "classes", "name", old_api, new_api, "constants", "name", { "value" }, false)) {
+ failed = true;
+ }
+
+ if (!compare_sub_dict_array(removed_classes_registered, "classes", "name", old_api, new_api, "methods", "name", { "is_virtual", "is_vararg", "is_static" }, true)) { // is_const sometimes can change because they are added if someone forgot, but should not be a problem for the extensions.
+ failed = true;
+ }
+
+ if (!compare_sub_dict_array(removed_classes_registered, "classes", "name", old_api, new_api, "signals", "name", { "*arguments" }, false)) {
+ failed = true;
+ }
+
+ if (!compare_sub_dict_array(removed_classes_registered, "classes", "name", old_api, new_api, "properties", "name", { "type", "*setter", "*getter", "*index" }, false)) {
+ failed = true;
+ }
+
+ if (!compare_dict_array(old_api, new_api, "singletons", "name", Vector<String>({ "type" }), false)) {
+ failed = true;
+ }
+
+ if (!compare_dict_array(old_api, new_api, "native_structures", "name", Vector<String>({ "format" }), false)) {
+ failed = true;
+ }
+
+ if (failed) {
+ return ERR_INVALID_DATA;
+ } else {
+ return OK;
+ }
+}
+
#endif // TOOLS_ENABLED
diff --git a/core/extension/extension_api_dump.h b/core/extension/extension_api_dump.h
index 7e588c9446..11ea2cf923 100644
--- a/core/extension/extension_api_dump.h
+++ b/core/extension/extension_api_dump.h
@@ -39,6 +39,7 @@ class GDExtensionAPIDump {
public:
static Dictionary generate_extension_api();
static void generate_extension_json_file(const String &p_path);
+ static Error validate_extension_json_file(const String &p_path);
};
#endif
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 91038b9bdf..0cbcf58882 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -34,6 +34,13 @@
#include "core/object/class_db.h"
#include "core/object/method_bind.h"
#include "core/os/os.h"
+#include "core/version.h"
+
+extern void gdextension_setup_interface();
+extern void *gdextension_get_legacy_interface();
+extern GDExtensionInterfaceFunctionPtr gdextension_get_proc_address(const char *p_name);
+
+typedef GDExtensionBool (*GDExtensionLegacyInitializationFunction)(void *p_interface, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization);
String GDExtension::get_extension_list_config_file() {
return ProjectSettings::get_singleton()->get_project_data_path().path_join("extension_list.cfg");
@@ -275,8 +282,6 @@ public:
}
};
-static GDExtensionInterface gdextension_interface;
-
void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs) {
GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
@@ -309,6 +314,7 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library
parent_extension->gdextension.children.push_back(&extension->gdextension);
}
+ extension->gdextension.library = self;
extension->gdextension.parent_class_name = parent_class_name;
extension->gdextension.class_name = class_name;
extension->gdextension.editor_class = self->level_initialized == INITIALIZATION_LEVEL_EDITOR;
@@ -425,13 +431,26 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra
self->extension_classes.erase(class_name);
}
-void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionStringPtr r_path) {
+void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringPtr r_path) {
GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
- *(String *)r_path = self->library_path;
+ memnew_placement(r_path, String(self->library_path));
+}
+
+HashMap<StringName, GDExtensionInterfaceFunctionPtr> gdextension_interface_functions;
+
+void GDExtension::register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) {
+ ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), "Attempt to register interface function '" + p_function_name + "', which appears to be already registered.");
+ gdextension_interface_functions.insert(p_function_name, p_function_pointer);
+}
+
+GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(StringName p_function_name) {
+ GDExtensionInterfaceFunctionPtr *function = gdextension_interface_functions.getptr(p_function_name);
+ ERR_FAIL_COND_V_MSG(function == nullptr, nullptr, "Attempt to get non-existent interface function: " + p_function_name);
+ return *function;
}
-Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol) {
+Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol, bool p_use_legacy_interface) {
Error err = OS::get_singleton()->open_dynamic_library(p_path, library, true, &library_path);
if (err != OK) {
ERR_PRINT("GDExtension dynamic library not found: " + p_path);
@@ -448,9 +467,17 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb
return err;
}
- GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr;
+ GDExtensionBool ret = 0;
+ if (p_use_legacy_interface) {
+ GDExtensionLegacyInitializationFunction initialization_function = (GDExtensionLegacyInitializationFunction)entry_funcptr;
+ ret = initialization_function(gdextension_get_legacy_interface(), this, &initialization);
+
+ } else {
+ GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr;
+ ret = initialization_function(&gdextension_get_proc_address, this, &initialization);
+ }
- if (initialization_function(&gdextension_interface, this, &initialization)) {
+ if (ret) {
level_initialized = -1;
return OK;
} else {
@@ -459,6 +486,10 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb
}
}
+Error GDExtension::open_library_compat_76406(const String &p_path, const String &p_entry_symbol) {
+ return open_library(p_path, p_entry_symbol, true);
+}
+
void GDExtension::close_library() {
ERR_FAIL_COND(library == nullptr);
OS::get_singleton()->close_dynamic_library(library);
@@ -494,7 +525,8 @@ void GDExtension::deinitialize_library(InitializationLevel p_level) {
}
void GDExtension::_bind_methods() {
- ClassDB::bind_method(D_METHOD("open_library", "path", "entry_symbol"), &GDExtension::open_library);
+ ClassDB::bind_method(D_METHOD("open_library", "path", "entry_symbol", "use_legacy_interface"), &GDExtension::open_library, DEFVAL(false));
+ ClassDB::bind_compatibility_method(D_METHOD("open_library", "path", "entry_symbol"), &GDExtension::open_library_compat_76406);
ClassDB::bind_method(D_METHOD("close_library"), &GDExtension::close_library);
ClassDB::bind_method(D_METHOD("is_library_open"), &GDExtension::is_library_open);
@@ -516,20 +548,18 @@ GDExtension::~GDExtension() {
}
}
-extern void gdextension_setup_interface(GDExtensionInterface *p_interface);
-
void GDExtension::initialize_gdextensions() {
- gdextension_setup_interface(&gdextension_interface);
-
- gdextension_interface.classdb_register_extension_class = _register_extension_class;
- gdextension_interface.classdb_register_extension_class_method = _register_extension_class_method;
- gdextension_interface.classdb_register_extension_class_integer_constant = _register_extension_class_integer_constant;
- gdextension_interface.classdb_register_extension_class_property = _register_extension_class_property;
- gdextension_interface.classdb_register_extension_class_property_group = _register_extension_class_property_group;
- gdextension_interface.classdb_register_extension_class_property_subgroup = _register_extension_class_property_subgroup;
- gdextension_interface.classdb_register_extension_class_signal = _register_extension_class_signal;
- gdextension_interface.classdb_unregister_extension_class = _unregister_extension_class;
- gdextension_interface.get_library_path = _get_library_path;
+ gdextension_setup_interface();
+
+ register_interface_function("classdb_register_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class);
+ register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method);
+ register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant);
+ register_interface_function("classdb_register_extension_class_property", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property);
+ register_interface_function("classdb_register_extension_class_property_group", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_group);
+ register_interface_function("classdb_register_extension_class_property_subgroup", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_subgroup);
+ register_interface_function("classdb_register_extension_class_signal", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_signal);
+ register_interface_function("classdb_unregister_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_unregister_extension_class);
+ register_interface_function("get_library_path", (GDExtensionInterfaceFunctionPtr)&GDExtension::_get_library_path);
}
Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
@@ -557,6 +587,39 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String
String entry_symbol = config->get_value("configuration", "entry_symbol");
+ uint32_t compatibility_minimum[3] = { 0, 0, 0 };
+ if (config->has_section_key("configuration", "compatibility_minimum")) {
+ String compat_string = config->get_value("configuration", "compatibility_minimum");
+ Vector<int> parts = compat_string.split_ints(".");
+ for (int i = 0; i < parts.size(); i++) {
+ if (i >= 3) {
+ break;
+ }
+ if (parts[i] >= 0) {
+ compatibility_minimum[i] = parts[i];
+ }
+ }
+ }
+ if (compatibility_minimum[0] < 4) {
+ compatibility_minimum[0] = 4;
+ }
+
+ bool compatible = true;
+ if (VERSION_MAJOR < compatibility_minimum[0]) {
+ compatible = false;
+ } else if (VERSION_MINOR < compatibility_minimum[1]) {
+ compatible = false;
+ } else if (VERSION_PATCH < compatibility_minimum[2]) {
+ compatible = false;
+ }
+ if (!compatible) {
+ if (r_error) {
+ *r_error = ERR_INVALID_DATA;
+ }
+ ERR_PRINT(vformat("GDExtension only compatible with Godot version %d.%d.%d or later: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
+ return Ref<Resource>();
+ }
+
String library_path = GDExtension::find_extension_library(p_path, config, [](String p_feature) { return OS::get_singleton()->has_feature(p_feature); });
if (library_path.is_empty()) {
@@ -572,10 +635,12 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String
library_path = p_path.get_base_dir().path_join(library_path);
}
+ bool use_legacy_interface = compatibility_minimum[0] == 4 && compatibility_minimum[1] == 0;
+
Ref<GDExtension> lib;
lib.instantiate();
String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path);
- err = lib->open_library(abs_path, entry_symbol);
+ err = lib->open_library(abs_path, entry_symbol, use_legacy_interface);
if (r_error) {
*r_error = err;
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index 9d946ca7ba..95811820cf 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -72,7 +72,8 @@ public:
static String get_extension_list_config_file();
static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr);
- Error open_library(const String &p_path, const String &p_entry_symbol);
+ Error open_library(const String &p_path, const String &p_entry_symbol, bool p_use_legacy_interface = false);
+ Error open_library_compat_76406(const String &p_path, const String &p_entry_symbol);
void close_library();
enum InitializationLevel {
@@ -88,7 +89,10 @@ public:
void initialize_library(InitializationLevel p_level);
void deinitialize_library(InitializationLevel p_level);
+ static void register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer);
+ static GDExtensionInterfaceFunctionPtr get_interface_function(StringName p_function_name);
static void initialize_gdextensions();
+
GDExtension();
~GDExtension();
};
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 2bedb623e4..12ef1772e3 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -31,6 +31,7 @@
#include "gdextension_interface.h"
#include "core/config/engine.h"
+#include "core/extension/gdextension.h"
#include "core/io/file_access.h"
#include "core/io/xml_parser.h"
#include "core/object/class_db.h"
@@ -40,16 +41,28 @@
#include "core/variant/variant.h"
#include "core/version.h"
+// Core interface functions.
+GDExtensionInterfaceFunctionPtr gdextension_get_proc_address(const char *p_name) {
+ return GDExtension::get_interface_function(p_name);
+}
+
+static void gdextension_get_godot_version(GDExtensionGodotVersion *r_godot_version) {
+ r_godot_version->major = VERSION_MAJOR;
+ r_godot_version->minor = VERSION_MINOR;
+ r_godot_version->patch = VERSION_PATCH;
+ r_godot_version->string = VERSION_FULL_NAME;
+}
+
// Memory Functions
-static void *gdextension_alloc(size_t p_size) {
+static void *gdextension_mem_alloc(size_t p_size) {
return memalloc(p_size);
}
-static void *gdextension_realloc(void *p_mem, size_t p_size) {
+static void *gdextension_mem_realloc(void *p_mem, size_t p_size) {
return memrealloc(p_mem, p_size);
}
-static void gdextension_free(void *p_mem) {
+static void gdextension_mem_free(void *p_mem) {
memfree(p_mem);
}
@@ -80,10 +93,10 @@ uint64_t gdextension_get_native_struct_size(GDExtensionConstStringNamePtr p_name
// Variant functions
-static void gdextension_variant_new_copy(GDExtensionVariantPtr r_dest, GDExtensionConstVariantPtr p_src) {
+static void gdextension_variant_new_copy(GDExtensionUninitializedVariantPtr r_dest, GDExtensionConstVariantPtr p_src) {
memnew_placement(reinterpret_cast<Variant *>(r_dest), Variant(*reinterpret_cast<const Variant *>(p_src)));
}
-static void gdextension_variant_new_nil(GDExtensionVariantPtr r_dest) {
+static void gdextension_variant_new_nil(GDExtensionUninitializedVariantPtr r_dest) {
memnew_placement(reinterpret_cast<Variant *>(r_dest), Variant);
}
static void gdextension_variant_destroy(GDExtensionVariantPtr p_self) {
@@ -92,14 +105,14 @@ static void gdextension_variant_destroy(GDExtensionVariantPtr p_self) {
// variant type
-static void gdextension_variant_call(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) {
+static void gdextension_variant_call(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error) {
Variant *self = (Variant *)p_self;
const StringName method = *reinterpret_cast<const StringName *>(p_method);
const Variant **args = (const Variant **)p_args;
- Variant ret;
Callable::CallError error;
- self->callp(method, args, p_argcount, ret, error);
- memnew_placement(r_return, Variant(ret));
+ memnew_placement(r_return, Variant);
+ Variant *ret = reinterpret_cast<Variant *>(r_return);
+ self->callp(method, args, p_argcount, *ret, error);
if (r_error) {
r_error->error = (GDExtensionCallErrorType)(error.error);
@@ -108,14 +121,14 @@ static void gdextension_variant_call(GDExtensionVariantPtr p_self, GDExtensionCo
}
}
-static void gdextension_variant_call_static(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) {
+static void gdextension_variant_call_static(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error) {
Variant::Type type = (Variant::Type)p_type;
const StringName method = *reinterpret_cast<const StringName *>(p_method);
const Variant **args = (const Variant **)p_args;
- Variant ret;
Callable::CallError error;
- Variant::call_static(type, method, args, p_argcount, ret, error);
- memnew_placement(r_return, Variant(ret));
+ memnew_placement(r_return, Variant);
+ Variant *ret = reinterpret_cast<Variant *>(r_return);
+ Variant::call_static(type, method, args, p_argcount, *ret, error);
if (r_error) {
r_error->error = (GDExtensionCallErrorType)error.error;
@@ -124,12 +137,13 @@ static void gdextension_variant_call_static(GDExtensionVariantType p_type, GDExt
}
}
-static void gdextension_variant_evaluate(GDExtensionVariantOperator p_op, GDExtensionConstVariantPtr p_a, GDExtensionConstVariantPtr p_b, GDExtensionVariantPtr r_return, GDExtensionBool *r_valid) {
+static void gdextension_variant_evaluate(GDExtensionVariantOperator p_op, GDExtensionConstVariantPtr p_a, GDExtensionConstVariantPtr p_b, GDExtensionUninitializedVariantPtr r_return, GDExtensionBool *r_valid) {
Variant::Operator op = (Variant::Operator)p_op;
const Variant *a = (const Variant *)p_a;
const Variant *b = (const Variant *)p_b;
- Variant *ret = (Variant *)r_return;
bool valid;
+ memnew_placement(r_return, Variant);
+ Variant *ret = reinterpret_cast<Variant *>(r_return);
Variant::evaluate(op, *a, *b, *ret, valid);
*r_valid = valid;
}
@@ -175,7 +189,7 @@ static void gdextension_variant_set_indexed(GDExtensionVariantPtr p_self, GDExte
*r_oob = oob;
}
-static void gdextension_variant_get(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid) {
+static void gdextension_variant_get(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid) {
const Variant *self = (const Variant *)p_self;
const Variant *key = (const Variant *)p_key;
@@ -184,7 +198,7 @@ static void gdextension_variant_get(GDExtensionConstVariantPtr p_self, GDExtensi
*r_valid = valid;
}
-static void gdextension_variant_get_named(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid) {
+static void gdextension_variant_get_named(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid) {
const Variant *self = (const Variant *)p_self;
const StringName *key = (const StringName *)p_key;
@@ -193,7 +207,7 @@ static void gdextension_variant_get_named(GDExtensionConstVariantPtr p_self, GDE
*r_valid = valid;
}
-static void gdextension_variant_get_keyed(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid) {
+static void gdextension_variant_get_keyed(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid) {
const Variant *self = (const Variant *)p_self;
const Variant *key = (const Variant *)p_key;
@@ -202,7 +216,7 @@ static void gdextension_variant_get_keyed(GDExtensionConstVariantPtr p_self, GDE
*r_valid = valid;
}
-static void gdextension_variant_get_indexed(GDExtensionConstVariantPtr p_self, GDExtensionInt p_index, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid, GDExtensionBool *r_oob) {
+static void gdextension_variant_get_indexed(GDExtensionConstVariantPtr p_self, GDExtensionInt p_index, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid, GDExtensionBool *r_oob) {
const Variant *self = (const Variant *)p_self;
bool valid;
@@ -213,9 +227,10 @@ static void gdextension_variant_get_indexed(GDExtensionConstVariantPtr p_self, G
}
/// Iteration.
-static GDExtensionBool gdextension_variant_iter_init(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionBool *r_valid) {
+static GDExtensionBool gdextension_variant_iter_init(GDExtensionConstVariantPtr p_self, GDExtensionUninitializedVariantPtr r_iter, GDExtensionBool *r_valid) {
const Variant *self = (const Variant *)p_self;
- Variant *iter = (Variant *)r_iter;
+ memnew_placement(r_iter, Variant);
+ Variant *iter = reinterpret_cast<Variant *>(r_iter);
bool valid;
bool ret = self->iter_init(*iter, valid);
@@ -233,7 +248,7 @@ static GDExtensionBool gdextension_variant_iter_next(GDExtensionConstVariantPtr
return ret;
}
-static void gdextension_variant_iter_get(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid) {
+static void gdextension_variant_iter_get(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid) {
const Variant *self = (const Variant *)p_self;
Variant *iter = (Variant *)r_iter;
@@ -264,12 +279,12 @@ static GDExtensionBool gdextension_variant_booleanize(GDExtensionConstVariantPtr
return self->booleanize();
}
-static void gdextension_variant_duplicate(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_ret, GDExtensionBool p_deep) {
+static void gdextension_variant_duplicate(GDExtensionConstVariantPtr p_self, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool p_deep) {
const Variant *self = (const Variant *)p_self;
memnew_placement(r_ret, Variant(self->duplicate(p_deep)));
}
-static void gdextension_variant_stringify(GDExtensionConstVariantPtr p_self, GDExtensionStringPtr r_ret) {
+static void gdextension_variant_stringify(GDExtensionConstVariantPtr p_self, GDExtensionUninitializedVariantPtr r_ret) {
const Variant *self = (const Variant *)p_self;
memnew_placement(r_ret, String(*self));
}
@@ -298,7 +313,7 @@ static GDExtensionBool gdextension_variant_has_key(GDExtensionConstVariantPtr p_
return ret;
}
-static void gdextension_variant_get_type_name(GDExtensionVariantType p_type, GDExtensionStringPtr r_ret) {
+static void gdextension_variant_get_type_name(GDExtensionVariantType p_type, GDExtensionUninitializedVariantPtr r_ret) {
String name = Variant::get_type_name((Variant::Type)p_type);
memnew_placement(r_ret, String(name));
}
@@ -395,7 +410,7 @@ static GDExtensionVariantFromTypeConstructorFunc gdextension_get_variant_from_ty
ERR_FAIL_V_MSG(nullptr, "Getting Variant conversion function with invalid type");
}
-static GDExtensionTypeFromVariantConstructorFunc gdextension_get_type_from_variant_constructor(GDExtensionVariantType p_type) {
+static GDExtensionTypeFromVariantConstructorFunc gdextension_get_variant_to_type_constructor(GDExtensionVariantType p_type) {
switch (p_type) {
case GDEXTENSION_VARIANT_TYPE_BOOL:
return VariantTypeConstructor<bool>::type_from_variant;
@@ -498,11 +513,12 @@ static GDExtensionPtrConstructor gdextension_variant_get_ptr_constructor(GDExten
static GDExtensionPtrDestructor gdextension_variant_get_ptr_destructor(GDExtensionVariantType p_type) {
return (GDExtensionPtrDestructor)Variant::get_ptr_destructor(Variant::Type(p_type));
}
-static void gdextension_variant_construct(GDExtensionVariantType p_type, GDExtensionVariantPtr p_base, const GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error) {
- memnew_placement(p_base, Variant);
+static void gdextension_variant_construct(GDExtensionVariantType p_type, GDExtensionUninitializedVariantPtr r_base, const GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error) {
+ memnew_placement(r_base, Variant);
+ Variant *base = reinterpret_cast<Variant *>(r_base);
Callable::CallError error;
- Variant::construct(Variant::Type(p_type), *(Variant *)p_base, (const Variant **)p_args, p_argument_count, error);
+ Variant::construct(Variant::Type(p_type), *base, (const Variant **)p_args, p_argument_count, error);
if (r_error) {
r_error->error = (GDExtensionCallErrorType)(error.error);
@@ -533,7 +549,7 @@ static GDExtensionPtrKeyedGetter gdextension_variant_get_ptr_keyed_getter(GDExte
static GDExtensionPtrKeyedChecker gdextension_variant_get_ptr_keyed_checker(GDExtensionVariantType p_type) {
return (GDExtensionPtrKeyedChecker)Variant::get_member_ptr_keyed_checker(Variant::Type(p_type));
}
-static void gdextension_variant_get_constant_value(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_constant, GDExtensionVariantPtr r_ret) {
+static void gdextension_variant_get_constant_value(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_constant, GDExtensionUninitializedVariantPtr r_ret) {
StringName constant = *reinterpret_cast<const StringName *>(p_constant);
memnew_placement(r_ret, Variant(Variant::get_constant_value(Variant::Type(p_type), constant)));
}
@@ -549,77 +565,67 @@ static GDExtensionPtrUtilityFunction gdextension_variant_get_ptr_utility_functio
//string helpers
-static void gdextension_string_new_with_latin1_chars(GDExtensionStringPtr r_dest, const char *p_contents) {
- String *dest = (String *)r_dest;
- memnew_placement(dest, String);
- *dest = String(p_contents);
+static void gdextension_string_new_with_latin1_chars(GDExtensionUninitializedStringPtr r_dest, const char *p_contents) {
+ memnew_placement(r_dest, String(p_contents));
}
-static void gdextension_string_new_with_utf8_chars(GDExtensionStringPtr r_dest, const char *p_contents) {
- String *dest = (String *)r_dest;
- memnew_placement(dest, String);
+static void gdextension_string_new_with_utf8_chars(GDExtensionUninitializedStringPtr r_dest, const char *p_contents) {
+ memnew_placement(r_dest, String);
+ String *dest = reinterpret_cast<String *>(r_dest);
dest->parse_utf8(p_contents);
}
-static void gdextension_string_new_with_utf16_chars(GDExtensionStringPtr r_dest, const char16_t *p_contents) {
- String *dest = (String *)r_dest;
- memnew_placement(dest, String);
+static void gdextension_string_new_with_utf16_chars(GDExtensionUninitializedStringPtr r_dest, const char16_t *p_contents) {
+ memnew_placement(r_dest, String);
+ String *dest = reinterpret_cast<String *>(r_dest);
dest->parse_utf16(p_contents);
}
-static void gdextension_string_new_with_utf32_chars(GDExtensionStringPtr r_dest, const char32_t *p_contents) {
- String *dest = (String *)r_dest;
- memnew_placement(dest, String);
- *dest = String((const char32_t *)p_contents);
+static void gdextension_string_new_with_utf32_chars(GDExtensionUninitializedStringPtr r_dest, const char32_t *p_contents) {
+ memnew_placement(r_dest, String((const char32_t *)p_contents));
}
-static void gdextension_string_new_with_wide_chars(GDExtensionStringPtr r_dest, const wchar_t *p_contents) {
- String *dest = (String *)r_dest;
+static void gdextension_string_new_with_wide_chars(GDExtensionUninitializedStringPtr r_dest, const wchar_t *p_contents) {
if constexpr (sizeof(wchar_t) == 2) {
// wchar_t is 16 bit, parse.
- memnew_placement(dest, String);
+ memnew_placement(r_dest, String);
+ String *dest = reinterpret_cast<String *>(r_dest);
dest->parse_utf16((const char16_t *)p_contents);
} else {
// wchar_t is 32 bit, copy.
- memnew_placement(dest, String);
- *dest = String((const char32_t *)p_contents);
+ memnew_placement(r_dest, String((const char32_t *)p_contents));
}
}
-static void gdextension_string_new_with_latin1_chars_and_len(GDExtensionStringPtr r_dest, const char *p_contents, GDExtensionInt p_size) {
- String *dest = (String *)r_dest;
- memnew_placement(dest, String);
- *dest = String(p_contents, p_size);
+static void gdextension_string_new_with_latin1_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char *p_contents, GDExtensionInt p_size) {
+ memnew_placement(r_dest, String(p_contents, p_size));
}
-static void gdextension_string_new_with_utf8_chars_and_len(GDExtensionStringPtr r_dest, const char *p_contents, GDExtensionInt p_size) {
- String *dest = (String *)r_dest;
- memnew_placement(dest, String);
+static void gdextension_string_new_with_utf8_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char *p_contents, GDExtensionInt p_size) {
+ memnew_placement(r_dest, String);
+ String *dest = reinterpret_cast<String *>(r_dest);
dest->parse_utf8(p_contents, p_size);
}
-static void gdextension_string_new_with_utf16_chars_and_len(GDExtensionStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_size) {
- String *dest = (String *)r_dest;
- memnew_placement(dest, String);
+static void gdextension_string_new_with_utf16_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_size) {
+ memnew_placement(r_dest, String);
+ String *dest = reinterpret_cast<String *>(r_dest);
dest->parse_utf16(p_contents, p_size);
}
-static void gdextension_string_new_with_utf32_chars_and_len(GDExtensionStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_size) {
- String *dest = (String *)r_dest;
- memnew_placement(dest, String);
- *dest = String((const char32_t *)p_contents, p_size);
+static void gdextension_string_new_with_utf32_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_size) {
+ memnew_placement(r_dest, String((const char32_t *)p_contents, p_size));
}
-static void gdextension_string_new_with_wide_chars_and_len(GDExtensionStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_size) {
- String *dest = (String *)r_dest;
+static void gdextension_string_new_with_wide_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_size) {
if constexpr (sizeof(wchar_t) == 2) {
// wchar_t is 16 bit, parse.
- memnew_placement(dest, String);
+ memnew_placement(r_dest, String);
+ String *dest = reinterpret_cast<String *>(r_dest);
dest->parse_utf16((const char16_t *)p_contents, p_size);
} else {
// wchar_t is 32 bit, copy.
- memnew_placement(dest, String);
- *dest = String((const char32_t *)p_contents, p_size);
+ memnew_placement(r_dest, String((const char32_t *)p_contents, p_size));
}
}
@@ -680,13 +686,17 @@ static GDExtensionInt gdextension_string_to_wide_chars(GDExtensionConstStringPtr
static char32_t *gdextension_string_operator_index(GDExtensionStringPtr p_self, GDExtensionInt p_index) {
String *self = (String *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->length() + 1, nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->length() + 1)) {
+ return nullptr;
+ }
return &self->ptrw()[p_index];
}
static const char32_t *gdextension_string_operator_index_const(GDExtensionConstStringPtr p_self, GDExtensionInt p_index) {
const String *self = (const String *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->length() + 1, nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->length() + 1)) {
+ return nullptr;
+ }
return &self->ptr()[p_index];
}
@@ -747,121 +757,161 @@ static int64_t gdextension_worker_thread_pool_add_native_task(GDExtensionObjectP
static uint8_t *gdextension_packed_byte_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedByteArray *self = (PackedByteArray *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptrw()[p_index];
}
static const uint8_t *gdextension_packed_byte_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedByteArray *self = (const PackedByteArray *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptr()[p_index];
}
static GDExtensionTypePtr gdextension_packed_color_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedColorArray *self = (PackedColorArray *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionTypePtr)&self->ptrw()[p_index];
}
static GDExtensionTypePtr gdextension_packed_color_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedColorArray *self = (const PackedColorArray *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionTypePtr)&self->ptr()[p_index];
}
static float *gdextension_packed_float32_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedFloat32Array *self = (PackedFloat32Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptrw()[p_index];
}
static const float *gdextension_packed_float32_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedFloat32Array *self = (const PackedFloat32Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptr()[p_index];
}
static double *gdextension_packed_float64_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedFloat64Array *self = (PackedFloat64Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptrw()[p_index];
}
static const double *gdextension_packed_float64_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedFloat64Array *self = (const PackedFloat64Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptr()[p_index];
}
static int32_t *gdextension_packed_int32_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedInt32Array *self = (PackedInt32Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptrw()[p_index];
}
static const int32_t *gdextension_packed_int32_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedInt32Array *self = (const PackedInt32Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptr()[p_index];
}
static int64_t *gdextension_packed_int64_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedInt64Array *self = (PackedInt64Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptrw()[p_index];
}
static const int64_t *gdextension_packed_int64_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedInt64Array *self = (const PackedInt64Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptr()[p_index];
}
static GDExtensionStringPtr gdextension_packed_string_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedStringArray *self = (PackedStringArray *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionStringPtr)&self->ptrw()[p_index];
}
static GDExtensionStringPtr gdextension_packed_string_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedStringArray *self = (const PackedStringArray *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionStringPtr)&self->ptr()[p_index];
}
static GDExtensionTypePtr gdextension_packed_vector2_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedVector2Array *self = (PackedVector2Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionTypePtr)&self->ptrw()[p_index];
}
static GDExtensionTypePtr gdextension_packed_vector2_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedVector2Array *self = (const PackedVector2Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionTypePtr)&self->ptr()[p_index];
}
static GDExtensionTypePtr gdextension_packed_vector3_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedVector3Array *self = (PackedVector3Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionTypePtr)&self->ptrw()[p_index];
}
static GDExtensionTypePtr gdextension_packed_vector3_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedVector3Array *self = (const PackedVector3Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionTypePtr)&self->ptr()[p_index];
}
static GDExtensionVariantPtr gdextension_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
Array *self = (Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionVariantPtr)&self->operator[](p_index);
}
static GDExtensionVariantPtr gdextension_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const Array *self = (const Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionVariantPtr)&self->operator[](p_index);
}
@@ -892,14 +942,13 @@ static GDExtensionVariantPtr gdextension_dictionary_operator_index_const(GDExten
/* OBJECT API */
-static void gdextension_object_method_bind_call(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) {
+static void gdextension_object_method_bind_call(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error) {
const MethodBind *mb = reinterpret_cast<const MethodBind *>(p_method_bind);
Object *o = (Object *)p_instance;
const Variant **args = (const Variant **)p_args;
Callable::CallError error;
- Variant ret = mb->call(o, args, p_arg_count, error);
- memnew_placement(r_return, Variant(ret));
+ memnew_placement(r_return, Variant(mb->call(o, args, p_arg_count, error)));
if (r_error) {
r_error->error = (GDExtensionCallErrorType)(error.error);
@@ -943,6 +992,19 @@ static GDExtensionObjectPtr gdextension_object_get_instance_from_id(GDObjectInst
return (GDExtensionObjectPtr)ObjectDB::get_instance(ObjectID(p_instance_id));
}
+static GDExtensionBool gdextension_object_get_class_name(GDExtensionConstObjectPtr p_object, GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringNamePtr r_class_name) {
+ if (!p_object) {
+ return false;
+ }
+ const Object *o = (const Object *)p_object;
+
+ memnew_placement(r_class_name, StringName);
+ StringName *class_name = reinterpret_cast<StringName *>(r_class_name);
+ *class_name = o->get_class_name_for_extension((GDExtension *)p_library);
+
+ return true;
+}
+
static GDExtensionObjectPtr gdextension_object_cast_to(GDExtensionConstObjectPtr p_object, void *p_class_tag) {
if (!p_object) {
return nullptr;
@@ -984,7 +1046,12 @@ static GDExtensionScriptInstancePtr gdextension_script_instance_create(const GDE
static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionConstStringNamePtr p_classname, GDExtensionConstStringNamePtr p_methodname, GDExtensionInt p_hash) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
const StringName methodname = *reinterpret_cast<const StringName *>(p_methodname);
- MethodBind *mb = ClassDB::get_method(classname, methodname);
+ bool exists = false;
+ MethodBind *mb = ClassDB::get_method_with_compatibility(classname, methodname, p_hash, &exists);
+ if (!mb && exists) {
+ ERR_PRINT("Method '" + classname + "." + methodname + "' has changed and no compatibility fallback has been provided. Please open an issue.");
+ return nullptr;
+ }
ERR_FAIL_COND_V(!mb, nullptr);
if (mb->get_hash() != p_hash) {
ERR_PRINT("Hash mismatch for method '" + classname + "." + methodname + "'.");
@@ -1004,204 +1071,453 @@ static void *gdextension_classdb_get_class_tag(GDExtensionConstStringNamePtr p_c
return class_info ? class_info->class_ptr : nullptr;
}
-void gdextension_setup_interface(GDExtensionInterface *p_interface) {
- GDExtensionInterface &gde_interface = *p_interface;
-
- gde_interface.version_major = VERSION_MAJOR;
- gde_interface.version_minor = VERSION_MINOR;
-#if VERSION_PATCH
- gde_interface.version_patch = VERSION_PATCH;
-#else
- gde_interface.version_patch = 0;
-#endif
- gde_interface.version_string = VERSION_FULL_NAME;
-
- /* GODOT CORE */
-
- gde_interface.mem_alloc = gdextension_alloc;
- gde_interface.mem_realloc = gdextension_realloc;
- gde_interface.mem_free = gdextension_free;
-
- gde_interface.print_error = gdextension_print_error;
- gde_interface.print_error_with_message = gdextension_print_error_with_message;
- gde_interface.print_warning = gdextension_print_warning;
- gde_interface.print_warning_with_message = gdextension_print_warning_with_message;
- gde_interface.print_script_error = gdextension_print_script_error;
- gde_interface.print_script_error_with_message = gdextension_print_script_error_with_message;
-
- gde_interface.get_native_struct_size = gdextension_get_native_struct_size;
-
- /* GODOT VARIANT */
-
- // variant general
- gde_interface.variant_new_copy = gdextension_variant_new_copy;
- gde_interface.variant_new_nil = gdextension_variant_new_nil;
- gde_interface.variant_destroy = gdextension_variant_destroy;
-
- gde_interface.variant_call = gdextension_variant_call;
- gde_interface.variant_call_static = gdextension_variant_call_static;
- gde_interface.variant_evaluate = gdextension_variant_evaluate;
- gde_interface.variant_set = gdextension_variant_set;
- gde_interface.variant_set_named = gdextension_variant_set_named;
- gde_interface.variant_set_keyed = gdextension_variant_set_keyed;
- gde_interface.variant_set_indexed = gdextension_variant_set_indexed;
- gde_interface.variant_get = gdextension_variant_get;
- gde_interface.variant_get_named = gdextension_variant_get_named;
- gde_interface.variant_get_keyed = gdextension_variant_get_keyed;
- gde_interface.variant_get_indexed = gdextension_variant_get_indexed;
- gde_interface.variant_iter_init = gdextension_variant_iter_init;
- gde_interface.variant_iter_next = gdextension_variant_iter_next;
- gde_interface.variant_iter_get = gdextension_variant_iter_get;
- gde_interface.variant_hash = gdextension_variant_hash;
- gde_interface.variant_recursive_hash = gdextension_variant_recursive_hash;
- gde_interface.variant_hash_compare = gdextension_variant_hash_compare;
- gde_interface.variant_booleanize = gdextension_variant_booleanize;
- gde_interface.variant_duplicate = gdextension_variant_duplicate;
- gde_interface.variant_stringify = gdextension_variant_stringify;
-
- gde_interface.variant_get_type = gdextension_variant_get_type;
- gde_interface.variant_has_method = gdextension_variant_has_method;
- gde_interface.variant_has_member = gdextension_variant_has_member;
- gde_interface.variant_has_key = gdextension_variant_has_key;
- gde_interface.variant_get_type_name = gdextension_variant_get_type_name;
- gde_interface.variant_can_convert = gdextension_variant_can_convert;
- gde_interface.variant_can_convert_strict = gdextension_variant_can_convert_strict;
-
- gde_interface.get_variant_from_type_constructor = gdextension_get_variant_from_type_constructor;
- gde_interface.get_variant_to_type_constructor = gdextension_get_type_from_variant_constructor;
-
- // ptrcalls.
-
- gde_interface.variant_get_ptr_operator_evaluator = gdextension_variant_get_ptr_operator_evaluator;
- gde_interface.variant_get_ptr_builtin_method = gdextension_variant_get_ptr_builtin_method;
- gde_interface.variant_get_ptr_constructor = gdextension_variant_get_ptr_constructor;
- gde_interface.variant_get_ptr_destructor = gdextension_variant_get_ptr_destructor;
- gde_interface.variant_construct = gdextension_variant_construct;
- gde_interface.variant_get_ptr_setter = gdextension_variant_get_ptr_setter;
- gde_interface.variant_get_ptr_getter = gdextension_variant_get_ptr_getter;
- gde_interface.variant_get_ptr_indexed_setter = gdextension_variant_get_ptr_indexed_setter;
- gde_interface.variant_get_ptr_indexed_getter = gdextension_variant_get_ptr_indexed_getter;
- gde_interface.variant_get_ptr_keyed_setter = gdextension_variant_get_ptr_keyed_setter;
- gde_interface.variant_get_ptr_keyed_getter = gdextension_variant_get_ptr_keyed_getter;
- gde_interface.variant_get_ptr_keyed_checker = gdextension_variant_get_ptr_keyed_checker;
- gde_interface.variant_get_constant_value = gdextension_variant_get_constant_value;
- gde_interface.variant_get_ptr_utility_function = gdextension_variant_get_ptr_utility_function;
-
- // extra utilities
-
- gde_interface.string_new_with_latin1_chars = gdextension_string_new_with_latin1_chars;
- gde_interface.string_new_with_utf8_chars = gdextension_string_new_with_utf8_chars;
- gde_interface.string_new_with_utf16_chars = gdextension_string_new_with_utf16_chars;
- gde_interface.string_new_with_utf32_chars = gdextension_string_new_with_utf32_chars;
- gde_interface.string_new_with_wide_chars = gdextension_string_new_with_wide_chars;
- gde_interface.string_new_with_latin1_chars_and_len = gdextension_string_new_with_latin1_chars_and_len;
- gde_interface.string_new_with_utf8_chars_and_len = gdextension_string_new_with_utf8_chars_and_len;
- gde_interface.string_new_with_utf16_chars_and_len = gdextension_string_new_with_utf16_chars_and_len;
- gde_interface.string_new_with_utf32_chars_and_len = gdextension_string_new_with_utf32_chars_and_len;
- gde_interface.string_new_with_wide_chars_and_len = gdextension_string_new_with_wide_chars_and_len;
- gde_interface.string_to_latin1_chars = gdextension_string_to_latin1_chars;
- gde_interface.string_to_utf8_chars = gdextension_string_to_utf8_chars;
- gde_interface.string_to_utf16_chars = gdextension_string_to_utf16_chars;
- gde_interface.string_to_utf32_chars = gdextension_string_to_utf32_chars;
- gde_interface.string_to_wide_chars = gdextension_string_to_wide_chars;
- gde_interface.string_operator_index = gdextension_string_operator_index;
- gde_interface.string_operator_index_const = gdextension_string_operator_index_const;
- gde_interface.string_operator_plus_eq_string = gdextension_string_operator_plus_eq_string;
- gde_interface.string_operator_plus_eq_char = gdextension_string_operator_plus_eq_char;
- gde_interface.string_operator_plus_eq_cstr = gdextension_string_operator_plus_eq_cstr;
- gde_interface.string_operator_plus_eq_wcstr = gdextension_string_operator_plus_eq_wcstr;
- gde_interface.string_operator_plus_eq_c32str = gdextension_string_operator_plus_eq_c32str;
-
- /* XMLParser extra utilities */
-
- gde_interface.xml_parser_open_buffer = gdextension_xml_parser_open_buffer;
-
- /* FileAccess extra utilities */
-
- gde_interface.file_access_store_buffer = gdextension_file_access_store_buffer;
- gde_interface.file_access_get_buffer = gdextension_file_access_get_buffer;
-
- /* WorkerThreadPool extra utilities */
-
- gde_interface.worker_thread_pool_add_native_group_task = gdextension_worker_thread_pool_add_native_group_task;
- gde_interface.worker_thread_pool_add_native_task = gdextension_worker_thread_pool_add_native_task;
-
- /* Packed array functions */
-
- gde_interface.packed_byte_array_operator_index = gdextension_packed_byte_array_operator_index;
- gde_interface.packed_byte_array_operator_index_const = gdextension_packed_byte_array_operator_index_const;
-
- gde_interface.packed_color_array_operator_index = gdextension_packed_color_array_operator_index;
- gde_interface.packed_color_array_operator_index_const = gdextension_packed_color_array_operator_index_const;
-
- gde_interface.packed_float32_array_operator_index = gdextension_packed_float32_array_operator_index;
- gde_interface.packed_float32_array_operator_index_const = gdextension_packed_float32_array_operator_index_const;
- gde_interface.packed_float64_array_operator_index = gdextension_packed_float64_array_operator_index;
- gde_interface.packed_float64_array_operator_index_const = gdextension_packed_float64_array_operator_index_const;
-
- gde_interface.packed_int32_array_operator_index = gdextension_packed_int32_array_operator_index;
- gde_interface.packed_int32_array_operator_index_const = gdextension_packed_int32_array_operator_index_const;
- gde_interface.packed_int64_array_operator_index = gdextension_packed_int64_array_operator_index;
- gde_interface.packed_int64_array_operator_index_const = gdextension_packed_int64_array_operator_index_const;
-
- gde_interface.packed_string_array_operator_index = gdextension_packed_string_array_operator_index;
- gde_interface.packed_string_array_operator_index_const = gdextension_packed_string_array_operator_index_const;
-
- gde_interface.packed_vector2_array_operator_index = gdextension_packed_vector2_array_operator_index;
- gde_interface.packed_vector2_array_operator_index_const = gdextension_packed_vector2_array_operator_index_const;
- gde_interface.packed_vector3_array_operator_index = gdextension_packed_vector3_array_operator_index;
- gde_interface.packed_vector3_array_operator_index_const = gdextension_packed_vector3_array_operator_index_const;
-
- gde_interface.array_operator_index = gdextension_array_operator_index;
- gde_interface.array_operator_index_const = gdextension_array_operator_index_const;
- gde_interface.array_ref = gdextension_array_ref;
- gde_interface.array_set_typed = gdextension_array_set_typed;
-
- /* Dictionary functions */
-
- gde_interface.dictionary_operator_index = gdextension_dictionary_operator_index;
- gde_interface.dictionary_operator_index_const = gdextension_dictionary_operator_index_const;
-
- /* OBJECT */
-
- gde_interface.object_method_bind_call = gdextension_object_method_bind_call;
- gde_interface.object_method_bind_ptrcall = gdextension_object_method_bind_ptrcall;
- gde_interface.object_destroy = gdextension_object_destroy;
- gde_interface.global_get_singleton = gdextension_global_get_singleton;
- gde_interface.object_get_instance_binding = gdextension_object_get_instance_binding;
- gde_interface.object_set_instance_binding = gdextension_object_set_instance_binding;
- gde_interface.object_set_instance = gdextension_object_set_instance;
-
- gde_interface.object_cast_to = gdextension_object_cast_to;
- gde_interface.object_get_instance_from_id = gdextension_object_get_instance_from_id;
- gde_interface.object_get_instance_id = gdextension_object_get_instance_id;
-
- /* REFERENCE */
-
- gde_interface.ref_get_object = gdextension_ref_get_object;
- gde_interface.ref_set_object = gdextension_ref_set_object;
-
- /* SCRIPT INSTANCE */
-
- gde_interface.script_instance_create = gdextension_script_instance_create;
-
- /* CLASSDB */
-
- gde_interface.classdb_construct_object = gdextension_classdb_construct_object;
- gde_interface.classdb_get_method_bind = gdextension_classdb_get_method_bind;
- gde_interface.classdb_get_class_tag = gdextension_classdb_get_class_tag;
-
- /* CLASSDB EXTENSION */
-
- //these are filled by implementation, since it will want to keep track of registered classes
- gde_interface.classdb_register_extension_class = nullptr;
- gde_interface.classdb_register_extension_class_method = nullptr;
- gde_interface.classdb_register_extension_class_integer_constant = nullptr;
- gde_interface.classdb_register_extension_class_property = nullptr;
- gde_interface.classdb_register_extension_class_property_group = nullptr;
- gde_interface.classdb_register_extension_class_property_subgroup = nullptr;
- gde_interface.classdb_register_extension_class_signal = nullptr;
- gde_interface.classdb_unregister_extension_class = nullptr;
+#define REGISTER_INTERFACE_FUNC(m_name) GDExtension::register_interface_function(#m_name, (GDExtensionInterfaceFunctionPtr)&gdextension_##m_name)
+
+void gdextension_setup_interface() {
+ REGISTER_INTERFACE_FUNC(get_godot_version);
+ REGISTER_INTERFACE_FUNC(mem_alloc);
+ REGISTER_INTERFACE_FUNC(mem_realloc);
+ REGISTER_INTERFACE_FUNC(mem_free);
+ REGISTER_INTERFACE_FUNC(print_error);
+ REGISTER_INTERFACE_FUNC(print_error_with_message);
+ REGISTER_INTERFACE_FUNC(print_warning);
+ REGISTER_INTERFACE_FUNC(print_warning_with_message);
+ REGISTER_INTERFACE_FUNC(print_script_error);
+ REGISTER_INTERFACE_FUNC(print_script_error_with_message);
+ REGISTER_INTERFACE_FUNC(get_native_struct_size);
+ REGISTER_INTERFACE_FUNC(variant_new_copy);
+ REGISTER_INTERFACE_FUNC(variant_new_nil);
+ REGISTER_INTERFACE_FUNC(variant_destroy);
+ REGISTER_INTERFACE_FUNC(variant_call);
+ REGISTER_INTERFACE_FUNC(variant_call_static);
+ REGISTER_INTERFACE_FUNC(variant_evaluate);
+ REGISTER_INTERFACE_FUNC(variant_set);
+ REGISTER_INTERFACE_FUNC(variant_set_named);
+ REGISTER_INTERFACE_FUNC(variant_set_keyed);
+ REGISTER_INTERFACE_FUNC(variant_set_indexed);
+ REGISTER_INTERFACE_FUNC(variant_get);
+ REGISTER_INTERFACE_FUNC(variant_get_named);
+ REGISTER_INTERFACE_FUNC(variant_get_keyed);
+ REGISTER_INTERFACE_FUNC(variant_get_indexed);
+ REGISTER_INTERFACE_FUNC(variant_iter_init);
+ REGISTER_INTERFACE_FUNC(variant_iter_next);
+ REGISTER_INTERFACE_FUNC(variant_iter_get);
+ REGISTER_INTERFACE_FUNC(variant_hash);
+ REGISTER_INTERFACE_FUNC(variant_recursive_hash);
+ REGISTER_INTERFACE_FUNC(variant_hash_compare);
+ REGISTER_INTERFACE_FUNC(variant_booleanize);
+ REGISTER_INTERFACE_FUNC(variant_duplicate);
+ REGISTER_INTERFACE_FUNC(variant_stringify);
+ REGISTER_INTERFACE_FUNC(variant_get_type);
+ REGISTER_INTERFACE_FUNC(variant_has_method);
+ REGISTER_INTERFACE_FUNC(variant_has_member);
+ REGISTER_INTERFACE_FUNC(variant_has_key);
+ REGISTER_INTERFACE_FUNC(variant_get_type_name);
+ REGISTER_INTERFACE_FUNC(variant_can_convert);
+ REGISTER_INTERFACE_FUNC(variant_can_convert_strict);
+ REGISTER_INTERFACE_FUNC(get_variant_from_type_constructor);
+ REGISTER_INTERFACE_FUNC(get_variant_to_type_constructor);
+ REGISTER_INTERFACE_FUNC(variant_get_ptr_operator_evaluator);
+ REGISTER_INTERFACE_FUNC(variant_get_ptr_builtin_method);
+ REGISTER_INTERFACE_FUNC(variant_get_ptr_constructor);
+ REGISTER_INTERFACE_FUNC(variant_get_ptr_destructor);
+ REGISTER_INTERFACE_FUNC(variant_construct);
+ REGISTER_INTERFACE_FUNC(variant_get_ptr_setter);
+ REGISTER_INTERFACE_FUNC(variant_get_ptr_getter);
+ REGISTER_INTERFACE_FUNC(variant_get_ptr_indexed_setter);
+ REGISTER_INTERFACE_FUNC(variant_get_ptr_indexed_getter);
+ REGISTER_INTERFACE_FUNC(variant_get_ptr_keyed_setter);
+ REGISTER_INTERFACE_FUNC(variant_get_ptr_keyed_getter);
+ REGISTER_INTERFACE_FUNC(variant_get_ptr_keyed_checker);
+ REGISTER_INTERFACE_FUNC(variant_get_constant_value);
+ REGISTER_INTERFACE_FUNC(variant_get_ptr_utility_function);
+ REGISTER_INTERFACE_FUNC(string_new_with_latin1_chars);
+ REGISTER_INTERFACE_FUNC(string_new_with_utf8_chars);
+ REGISTER_INTERFACE_FUNC(string_new_with_utf16_chars);
+ REGISTER_INTERFACE_FUNC(string_new_with_utf32_chars);
+ REGISTER_INTERFACE_FUNC(string_new_with_wide_chars);
+ REGISTER_INTERFACE_FUNC(string_new_with_latin1_chars_and_len);
+ REGISTER_INTERFACE_FUNC(string_new_with_utf8_chars_and_len);
+ REGISTER_INTERFACE_FUNC(string_new_with_utf16_chars_and_len);
+ REGISTER_INTERFACE_FUNC(string_new_with_utf32_chars_and_len);
+ REGISTER_INTERFACE_FUNC(string_new_with_wide_chars_and_len);
+ REGISTER_INTERFACE_FUNC(string_to_latin1_chars);
+ REGISTER_INTERFACE_FUNC(string_to_utf8_chars);
+ REGISTER_INTERFACE_FUNC(string_to_utf16_chars);
+ REGISTER_INTERFACE_FUNC(string_to_utf32_chars);
+ REGISTER_INTERFACE_FUNC(string_to_wide_chars);
+ REGISTER_INTERFACE_FUNC(string_operator_index);
+ REGISTER_INTERFACE_FUNC(string_operator_index_const);
+ REGISTER_INTERFACE_FUNC(string_operator_plus_eq_string);
+ REGISTER_INTERFACE_FUNC(string_operator_plus_eq_char);
+ REGISTER_INTERFACE_FUNC(string_operator_plus_eq_cstr);
+ REGISTER_INTERFACE_FUNC(string_operator_plus_eq_wcstr);
+ REGISTER_INTERFACE_FUNC(string_operator_plus_eq_c32str);
+ REGISTER_INTERFACE_FUNC(xml_parser_open_buffer);
+ REGISTER_INTERFACE_FUNC(file_access_store_buffer);
+ REGISTER_INTERFACE_FUNC(file_access_get_buffer);
+ REGISTER_INTERFACE_FUNC(worker_thread_pool_add_native_group_task);
+ REGISTER_INTERFACE_FUNC(worker_thread_pool_add_native_task);
+ REGISTER_INTERFACE_FUNC(packed_byte_array_operator_index);
+ REGISTER_INTERFACE_FUNC(packed_byte_array_operator_index_const);
+ REGISTER_INTERFACE_FUNC(packed_color_array_operator_index);
+ REGISTER_INTERFACE_FUNC(packed_color_array_operator_index_const);
+ REGISTER_INTERFACE_FUNC(packed_float32_array_operator_index);
+ REGISTER_INTERFACE_FUNC(packed_float32_array_operator_index_const);
+ REGISTER_INTERFACE_FUNC(packed_float64_array_operator_index);
+ REGISTER_INTERFACE_FUNC(packed_float64_array_operator_index_const);
+ REGISTER_INTERFACE_FUNC(packed_int32_array_operator_index);
+ REGISTER_INTERFACE_FUNC(packed_int32_array_operator_index_const);
+ REGISTER_INTERFACE_FUNC(packed_int64_array_operator_index);
+ REGISTER_INTERFACE_FUNC(packed_int64_array_operator_index_const);
+ REGISTER_INTERFACE_FUNC(packed_string_array_operator_index);
+ REGISTER_INTERFACE_FUNC(packed_string_array_operator_index_const);
+ REGISTER_INTERFACE_FUNC(packed_vector2_array_operator_index);
+ REGISTER_INTERFACE_FUNC(packed_vector2_array_operator_index_const);
+ REGISTER_INTERFACE_FUNC(packed_vector3_array_operator_index);
+ REGISTER_INTERFACE_FUNC(packed_vector3_array_operator_index_const);
+ REGISTER_INTERFACE_FUNC(array_operator_index);
+ REGISTER_INTERFACE_FUNC(array_operator_index_const);
+ REGISTER_INTERFACE_FUNC(array_ref);
+ REGISTER_INTERFACE_FUNC(array_set_typed);
+ REGISTER_INTERFACE_FUNC(dictionary_operator_index);
+ REGISTER_INTERFACE_FUNC(dictionary_operator_index_const);
+ REGISTER_INTERFACE_FUNC(object_method_bind_call);
+ REGISTER_INTERFACE_FUNC(object_method_bind_ptrcall);
+ REGISTER_INTERFACE_FUNC(object_destroy);
+ REGISTER_INTERFACE_FUNC(global_get_singleton);
+ REGISTER_INTERFACE_FUNC(object_get_instance_binding);
+ REGISTER_INTERFACE_FUNC(object_set_instance_binding);
+ REGISTER_INTERFACE_FUNC(object_set_instance);
+ REGISTER_INTERFACE_FUNC(object_get_class_name);
+ REGISTER_INTERFACE_FUNC(object_cast_to);
+ REGISTER_INTERFACE_FUNC(object_get_instance_from_id);
+ REGISTER_INTERFACE_FUNC(object_get_instance_id);
+ REGISTER_INTERFACE_FUNC(ref_get_object);
+ REGISTER_INTERFACE_FUNC(ref_set_object);
+ REGISTER_INTERFACE_FUNC(script_instance_create);
+ REGISTER_INTERFACE_FUNC(classdb_construct_object);
+ REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
+ REGISTER_INTERFACE_FUNC(classdb_get_class_tag);
+}
+
+#undef REGISTER_INTERFACE_FUNCTION
+
+/*
+ * Handle legacy GDExtension interface from Godot 4.0.
+ */
+
+typedef struct {
+ uint32_t version_major;
+ uint32_t version_minor;
+ uint32_t version_patch;
+ const char *version_string;
+
+ GDExtensionInterfaceMemAlloc mem_alloc;
+ GDExtensionInterfaceMemRealloc mem_realloc;
+ GDExtensionInterfaceMemFree mem_free;
+
+ GDExtensionInterfacePrintError print_error;
+ GDExtensionInterfacePrintErrorWithMessage print_error_with_message;
+ GDExtensionInterfacePrintWarning print_warning;
+ GDExtensionInterfacePrintWarningWithMessage print_warning_with_message;
+ GDExtensionInterfacePrintScriptError print_script_error;
+ GDExtensionInterfacePrintScriptErrorWithMessage print_script_error_with_message;
+
+ GDExtensionInterfaceGetNativeStructSize get_native_struct_size;
+
+ GDExtensionInterfaceVariantNewCopy variant_new_copy;
+ GDExtensionInterfaceVariantNewNil variant_new_nil;
+ GDExtensionInterfaceVariantDestroy variant_destroy;
+
+ GDExtensionInterfaceVariantCall variant_call;
+ GDExtensionInterfaceVariantCallStatic variant_call_static;
+ GDExtensionInterfaceVariantEvaluate variant_evaluate;
+ GDExtensionInterfaceVariantSet variant_set;
+ GDExtensionInterfaceVariantSetNamed variant_set_named;
+ GDExtensionInterfaceVariantSetKeyed variant_set_keyed;
+ GDExtensionInterfaceVariantSetIndexed variant_set_indexed;
+ GDExtensionInterfaceVariantGet variant_get;
+ GDExtensionInterfaceVariantGetNamed variant_get_named;
+ GDExtensionInterfaceVariantGetKeyed variant_get_keyed;
+ GDExtensionInterfaceVariantGetIndexed variant_get_indexed;
+ GDExtensionInterfaceVariantIterInit variant_iter_init;
+ GDExtensionInterfaceVariantIterNext variant_iter_next;
+ GDExtensionInterfaceVariantIterGet variant_iter_get;
+ GDExtensionInterfaceVariantHash variant_hash;
+ GDExtensionInterfaceVariantRecursiveHash variant_recursive_hash;
+ GDExtensionInterfaceVariantHashCompare variant_hash_compare;
+ GDExtensionInterfaceVariantBooleanize variant_booleanize;
+ GDExtensionInterfaceVariantDuplicate variant_duplicate;
+ GDExtensionInterfaceVariantStringify variant_stringify;
+
+ GDExtensionInterfaceVariantGetType variant_get_type;
+ GDExtensionInterfaceVariantHasMethod variant_has_method;
+ GDExtensionInterfaceVariantHasMember variant_has_member;
+ GDExtensionInterfaceVariantHasKey variant_has_key;
+ GDExtensionInterfaceVariantGetTypeName variant_get_type_name;
+ GDExtensionInterfaceVariantCanConvert variant_can_convert;
+ GDExtensionInterfaceVariantCanConvertStrict variant_can_convert_strict;
+
+ GDExtensionInterfaceGetVariantFromTypeConstructor get_variant_from_type_constructor;
+ GDExtensionInterfaceGetVariantToTypeConstructor get_variant_to_type_constructor;
+ GDExtensionInterfaceVariantGetPtrOperatorEvaluator variant_get_ptr_operator_evaluator;
+ GDExtensionInterfaceVariantGetPtrBuiltinMethod variant_get_ptr_builtin_method;
+ GDExtensionInterfaceVariantGetPtrConstructor variant_get_ptr_constructor;
+ GDExtensionInterfaceVariantGetPtrDestructor variant_get_ptr_destructor;
+ GDExtensionInterfaceVariantConstruct variant_construct;
+ GDExtensionInterfaceVariantGetPtrSetter variant_get_ptr_setter;
+ GDExtensionInterfaceVariantGetPtrGetter variant_get_ptr_getter;
+ GDExtensionInterfaceVariantGetPtrIndexedSetter variant_get_ptr_indexed_setter;
+ GDExtensionInterfaceVariantGetPtrIndexedGetter variant_get_ptr_indexed_getter;
+ GDExtensionInterfaceVariantGetPtrKeyedSetter variant_get_ptr_keyed_setter;
+ GDExtensionInterfaceVariantGetPtrKeyedGetter variant_get_ptr_keyed_getter;
+ GDExtensionInterfaceVariantGetPtrKeyedChecker variant_get_ptr_keyed_checker;
+ GDExtensionInterfaceVariantGetConstantValue variant_get_constant_value;
+ GDExtensionInterfaceVariantGetPtrUtilityFunction variant_get_ptr_utility_function;
+
+ GDExtensionInterfaceStringNewWithLatin1Chars string_new_with_latin1_chars;
+ GDExtensionInterfaceStringNewWithUtf8Chars string_new_with_utf8_chars;
+ GDExtensionInterfaceStringNewWithUtf16Chars string_new_with_utf16_chars;
+ GDExtensionInterfaceStringNewWithUtf32Chars string_new_with_utf32_chars;
+ GDExtensionInterfaceStringNewWithWideChars string_new_with_wide_chars;
+ GDExtensionInterfaceStringNewWithLatin1CharsAndLen string_new_with_latin1_chars_and_len;
+ GDExtensionInterfaceStringNewWithUtf8CharsAndLen string_new_with_utf8_chars_and_len;
+ GDExtensionInterfaceStringNewWithUtf16CharsAndLen string_new_with_utf16_chars_and_len;
+ GDExtensionInterfaceStringNewWithUtf32CharsAndLen string_new_with_utf32_chars_and_len;
+ GDExtensionInterfaceStringNewWithWideCharsAndLen string_new_with_wide_chars_and_len;
+ GDExtensionInterfaceStringToLatin1Chars string_to_latin1_chars;
+ GDExtensionInterfaceStringToUtf8Chars string_to_utf8_chars;
+ GDExtensionInterfaceStringToUtf16Chars string_to_utf16_chars;
+ GDExtensionInterfaceStringToUtf32Chars string_to_utf32_chars;
+ GDExtensionInterfaceStringToWideChars string_to_wide_chars;
+ GDExtensionInterfaceStringOperatorIndex string_operator_index;
+ GDExtensionInterfaceStringOperatorIndexConst string_operator_index_const;
+
+ GDExtensionInterfaceStringOperatorPlusEqString string_operator_plus_eq_string;
+ GDExtensionInterfaceStringOperatorPlusEqChar string_operator_plus_eq_char;
+ GDExtensionInterfaceStringOperatorPlusEqCstr string_operator_plus_eq_cstr;
+ GDExtensionInterfaceStringOperatorPlusEqWcstr string_operator_plus_eq_wcstr;
+ GDExtensionInterfaceStringOperatorPlusEqC32str string_operator_plus_eq_c32str;
+
+ GDExtensionInterfaceXmlParserOpenBuffer xml_parser_open_buffer;
+
+ GDExtensionInterfaceFileAccessStoreBuffer file_access_store_buffer;
+ GDExtensionInterfaceFileAccessGetBuffer file_access_get_buffer;
+
+ GDExtensionInterfaceWorkerThreadPoolAddNativeGroupTask worker_thread_pool_add_native_group_task;
+ GDExtensionInterfaceWorkerThreadPoolAddNativeTask worker_thread_pool_add_native_task;
+
+ GDExtensionInterfacePackedByteArrayOperatorIndex packed_byte_array_operator_index;
+ GDExtensionInterfacePackedByteArrayOperatorIndexConst packed_byte_array_operator_index_const;
+ GDExtensionInterfacePackedColorArrayOperatorIndex packed_color_array_operator_index;
+ GDExtensionInterfacePackedColorArrayOperatorIndexConst packed_color_array_operator_index_const;
+ GDExtensionInterfacePackedFloat32ArrayOperatorIndex packed_float32_array_operator_index;
+ GDExtensionInterfacePackedFloat32ArrayOperatorIndexConst packed_float32_array_operator_index_const;
+ GDExtensionInterfacePackedFloat64ArrayOperatorIndex packed_float64_array_operator_index;
+ GDExtensionInterfacePackedFloat64ArrayOperatorIndexConst packed_float64_array_operator_index_const;
+ GDExtensionInterfacePackedInt32ArrayOperatorIndex packed_int32_array_operator_index;
+ GDExtensionInterfacePackedInt32ArrayOperatorIndexConst packed_int32_array_operator_index_const;
+ GDExtensionInterfacePackedInt64ArrayOperatorIndex packed_int64_array_operator_index;
+ GDExtensionInterfacePackedInt64ArrayOperatorIndexConst packed_int64_array_operator_index_const;
+ GDExtensionInterfacePackedStringArrayOperatorIndex packed_string_array_operator_index;
+ GDExtensionInterfacePackedStringArrayOperatorIndexConst packed_string_array_operator_index_const;
+ GDExtensionInterfacePackedVector2ArrayOperatorIndex packed_vector2_array_operator_index;
+ GDExtensionInterfacePackedVector2ArrayOperatorIndexConst packed_vector2_array_operator_index_const;
+ GDExtensionInterfacePackedVector3ArrayOperatorIndex packed_vector3_array_operator_index;
+ GDExtensionInterfacePackedVector3ArrayOperatorIndexConst packed_vector3_array_operator_index_const;
+ GDExtensionInterfaceArrayOperatorIndex array_operator_index;
+ GDExtensionInterfaceArrayOperatorIndexConst array_operator_index_const;
+ GDExtensionInterfaceArrayRef array_ref;
+ GDExtensionInterfaceArraySetTyped array_set_typed;
+
+ GDExtensionInterfaceDictionaryOperatorIndex dictionary_operator_index;
+ GDExtensionInterfaceDictionaryOperatorIndexConst dictionary_operator_index_const;
+
+ GDExtensionInterfaceObjectMethodBindCall object_method_bind_call;
+ GDExtensionInterfaceObjectMethodBindPtrcall object_method_bind_ptrcall;
+ GDExtensionInterfaceObjectDestroy object_destroy;
+ GDExtensionInterfaceGlobalGetSingleton global_get_singleton;
+ GDExtensionInterfaceObjectGetInstanceBinding object_get_instance_binding;
+ GDExtensionInterfaceObjectSetInstanceBinding object_set_instance_binding;
+ GDExtensionInterfaceObjectSetInstance object_set_instance;
+ GDExtensionInterfaceObjectCastTo object_cast_to;
+ GDExtensionInterfaceObjectGetInstanceFromId object_get_instance_from_id;
+ GDExtensionInterfaceObjectGetInstanceId object_get_instance_id;
+
+ GDExtensionInterfaceRefGetObject ref_get_object;
+ GDExtensionInterfaceRefSetObject ref_set_object;
+
+ GDExtensionInterfaceScriptInstanceCreate script_instance_create;
+
+ GDExtensionInterfaceClassdbConstructObject classdb_construct_object;
+ GDExtensionInterfaceClassdbGetMethodBind classdb_get_method_bind;
+ GDExtensionInterfaceClassdbGetClassTag classdb_get_class_tag;
+
+ GDExtensionInterfaceClassdbRegisterExtensionClass classdb_register_extension_class;
+ GDExtensionInterfaceClassdbRegisterExtensionClassMethod classdb_register_extension_class_method;
+ GDExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant classdb_register_extension_class_integer_constant;
+ GDExtensionInterfaceClassdbRegisterExtensionClassProperty classdb_register_extension_class_property;
+ GDExtensionInterfaceClassdbRegisterExtensionClassPropertyGroup classdb_register_extension_class_property_group;
+ GDExtensionInterfaceClassdbRegisterExtensionClassPropertySubgroup classdb_register_extension_class_property_subgroup;
+ GDExtensionInterfaceClassdbRegisterExtensionClassSignal classdb_register_extension_class_signal;
+ GDExtensionInterfaceClassdbUnregisterExtensionClass classdb_unregister_extension_class;
+
+ GDExtensionInterfaceGetLibraryPath get_library_path;
+
+} LegacyGDExtensionInterface;
+
+static LegacyGDExtensionInterface *legacy_gdextension_interface = nullptr;
+
+#define SETUP_LEGACY_FUNC(m_name, m_type) legacy_gdextension_interface->m_name = (m_type)GDExtension::get_interface_function(#m_name)
+
+void *gdextension_get_legacy_interface() {
+ if (legacy_gdextension_interface != nullptr) {
+ return legacy_gdextension_interface;
+ }
- gde_interface.get_library_path = nullptr;
-}
+ legacy_gdextension_interface = memnew(LegacyGDExtensionInterface);
+
+ // Force to 4.0.999 to make it easier to detect this structure.
+ legacy_gdextension_interface->version_major = 4;
+ legacy_gdextension_interface->version_minor = 0;
+ legacy_gdextension_interface->version_patch = 999;
+ legacy_gdextension_interface->version_string = "Godot Engine v4.0.999.stable.official [000000000]";
+
+ SETUP_LEGACY_FUNC(mem_alloc, GDExtensionInterfaceMemAlloc);
+ SETUP_LEGACY_FUNC(mem_realloc, GDExtensionInterfaceMemRealloc);
+ SETUP_LEGACY_FUNC(mem_free, GDExtensionInterfaceMemFree);
+ SETUP_LEGACY_FUNC(print_error, GDExtensionInterfacePrintError);
+ SETUP_LEGACY_FUNC(print_error_with_message, GDExtensionInterfacePrintErrorWithMessage);
+ SETUP_LEGACY_FUNC(print_warning, GDExtensionInterfacePrintWarning);
+ SETUP_LEGACY_FUNC(print_warning_with_message, GDExtensionInterfacePrintWarningWithMessage);
+ SETUP_LEGACY_FUNC(print_script_error, GDExtensionInterfacePrintScriptError);
+ SETUP_LEGACY_FUNC(print_script_error_with_message, GDExtensionInterfacePrintScriptErrorWithMessage);
+ SETUP_LEGACY_FUNC(get_native_struct_size, GDExtensionInterfaceGetNativeStructSize);
+ SETUP_LEGACY_FUNC(variant_new_copy, GDExtensionInterfaceVariantNewCopy);
+ SETUP_LEGACY_FUNC(variant_new_nil, GDExtensionInterfaceVariantNewNil);
+ SETUP_LEGACY_FUNC(variant_destroy, GDExtensionInterfaceVariantDestroy);
+ SETUP_LEGACY_FUNC(variant_call, GDExtensionInterfaceVariantCall);
+ SETUP_LEGACY_FUNC(variant_call_static, GDExtensionInterfaceVariantCallStatic);
+ SETUP_LEGACY_FUNC(variant_evaluate, GDExtensionInterfaceVariantEvaluate);
+ SETUP_LEGACY_FUNC(variant_set, GDExtensionInterfaceVariantSet);
+ SETUP_LEGACY_FUNC(variant_set_named, GDExtensionInterfaceVariantSetNamed);
+ SETUP_LEGACY_FUNC(variant_set_keyed, GDExtensionInterfaceVariantSetKeyed);
+ SETUP_LEGACY_FUNC(variant_set_indexed, GDExtensionInterfaceVariantSetIndexed);
+ SETUP_LEGACY_FUNC(variant_get, GDExtensionInterfaceVariantGet);
+ SETUP_LEGACY_FUNC(variant_get_named, GDExtensionInterfaceVariantGetNamed);
+ SETUP_LEGACY_FUNC(variant_get_keyed, GDExtensionInterfaceVariantGetKeyed);
+ SETUP_LEGACY_FUNC(variant_get_indexed, GDExtensionInterfaceVariantGetIndexed);
+ SETUP_LEGACY_FUNC(variant_iter_init, GDExtensionInterfaceVariantIterInit);
+ SETUP_LEGACY_FUNC(variant_iter_next, GDExtensionInterfaceVariantIterNext);
+ SETUP_LEGACY_FUNC(variant_iter_get, GDExtensionInterfaceVariantIterGet);
+ SETUP_LEGACY_FUNC(variant_hash, GDExtensionInterfaceVariantHash);
+ SETUP_LEGACY_FUNC(variant_recursive_hash, GDExtensionInterfaceVariantRecursiveHash);
+ SETUP_LEGACY_FUNC(variant_hash_compare, GDExtensionInterfaceVariantHashCompare);
+ SETUP_LEGACY_FUNC(variant_booleanize, GDExtensionInterfaceVariantBooleanize);
+ SETUP_LEGACY_FUNC(variant_duplicate, GDExtensionInterfaceVariantDuplicate);
+ SETUP_LEGACY_FUNC(variant_stringify, GDExtensionInterfaceVariantStringify);
+ SETUP_LEGACY_FUNC(variant_get_type, GDExtensionInterfaceVariantGetType);
+ SETUP_LEGACY_FUNC(variant_has_method, GDExtensionInterfaceVariantHasMethod);
+ SETUP_LEGACY_FUNC(variant_has_member, GDExtensionInterfaceVariantHasMember);
+ SETUP_LEGACY_FUNC(variant_has_key, GDExtensionInterfaceVariantHasKey);
+ SETUP_LEGACY_FUNC(variant_get_type_name, GDExtensionInterfaceVariantGetTypeName);
+ SETUP_LEGACY_FUNC(variant_can_convert, GDExtensionInterfaceVariantCanConvert);
+ SETUP_LEGACY_FUNC(variant_can_convert_strict, GDExtensionInterfaceVariantCanConvertStrict);
+ SETUP_LEGACY_FUNC(get_variant_from_type_constructor, GDExtensionInterfaceGetVariantFromTypeConstructor);
+ SETUP_LEGACY_FUNC(get_variant_to_type_constructor, GDExtensionInterfaceGetVariantToTypeConstructor);
+ SETUP_LEGACY_FUNC(variant_get_ptr_operator_evaluator, GDExtensionInterfaceVariantGetPtrOperatorEvaluator);
+ SETUP_LEGACY_FUNC(variant_get_ptr_builtin_method, GDExtensionInterfaceVariantGetPtrBuiltinMethod);
+ SETUP_LEGACY_FUNC(variant_get_ptr_constructor, GDExtensionInterfaceVariantGetPtrConstructor);
+ SETUP_LEGACY_FUNC(variant_get_ptr_destructor, GDExtensionInterfaceVariantGetPtrDestructor);
+ SETUP_LEGACY_FUNC(variant_construct, GDExtensionInterfaceVariantConstruct);
+ SETUP_LEGACY_FUNC(variant_get_ptr_setter, GDExtensionInterfaceVariantGetPtrSetter);
+ SETUP_LEGACY_FUNC(variant_get_ptr_getter, GDExtensionInterfaceVariantGetPtrGetter);
+ SETUP_LEGACY_FUNC(variant_get_ptr_indexed_setter, GDExtensionInterfaceVariantGetPtrIndexedSetter);
+ SETUP_LEGACY_FUNC(variant_get_ptr_indexed_getter, GDExtensionInterfaceVariantGetPtrIndexedGetter);
+ SETUP_LEGACY_FUNC(variant_get_ptr_keyed_setter, GDExtensionInterfaceVariantGetPtrKeyedSetter);
+ SETUP_LEGACY_FUNC(variant_get_ptr_keyed_getter, GDExtensionInterfaceVariantGetPtrKeyedGetter);
+ SETUP_LEGACY_FUNC(variant_get_ptr_keyed_checker, GDExtensionInterfaceVariantGetPtrKeyedChecker);
+ SETUP_LEGACY_FUNC(variant_get_constant_value, GDExtensionInterfaceVariantGetConstantValue);
+ SETUP_LEGACY_FUNC(variant_get_ptr_utility_function, GDExtensionInterfaceVariantGetPtrUtilityFunction);
+ SETUP_LEGACY_FUNC(string_new_with_latin1_chars, GDExtensionInterfaceStringNewWithLatin1Chars);
+ SETUP_LEGACY_FUNC(string_new_with_utf8_chars, GDExtensionInterfaceStringNewWithUtf8Chars);
+ SETUP_LEGACY_FUNC(string_new_with_utf16_chars, GDExtensionInterfaceStringNewWithUtf16Chars);
+ SETUP_LEGACY_FUNC(string_new_with_utf32_chars, GDExtensionInterfaceStringNewWithUtf32Chars);
+ SETUP_LEGACY_FUNC(string_new_with_wide_chars, GDExtensionInterfaceStringNewWithWideChars);
+ SETUP_LEGACY_FUNC(string_new_with_latin1_chars_and_len, GDExtensionInterfaceStringNewWithLatin1CharsAndLen);
+ SETUP_LEGACY_FUNC(string_new_with_utf8_chars_and_len, GDExtensionInterfaceStringNewWithUtf8CharsAndLen);
+ SETUP_LEGACY_FUNC(string_new_with_utf16_chars_and_len, GDExtensionInterfaceStringNewWithUtf16CharsAndLen);
+ SETUP_LEGACY_FUNC(string_new_with_utf32_chars_and_len, GDExtensionInterfaceStringNewWithUtf32CharsAndLen);
+ SETUP_LEGACY_FUNC(string_new_with_wide_chars_and_len, GDExtensionInterfaceStringNewWithWideCharsAndLen);
+ SETUP_LEGACY_FUNC(string_to_latin1_chars, GDExtensionInterfaceStringToLatin1Chars);
+ SETUP_LEGACY_FUNC(string_to_utf8_chars, GDExtensionInterfaceStringToUtf8Chars);
+ SETUP_LEGACY_FUNC(string_to_utf16_chars, GDExtensionInterfaceStringToUtf16Chars);
+ SETUP_LEGACY_FUNC(string_to_utf32_chars, GDExtensionInterfaceStringToUtf32Chars);
+ SETUP_LEGACY_FUNC(string_to_wide_chars, GDExtensionInterfaceStringToWideChars);
+ SETUP_LEGACY_FUNC(string_operator_index, GDExtensionInterfaceStringOperatorIndex);
+ SETUP_LEGACY_FUNC(string_operator_index_const, GDExtensionInterfaceStringOperatorIndexConst);
+ SETUP_LEGACY_FUNC(string_operator_plus_eq_string, GDExtensionInterfaceStringOperatorPlusEqString);
+ SETUP_LEGACY_FUNC(string_operator_plus_eq_char, GDExtensionInterfaceStringOperatorPlusEqChar);
+ SETUP_LEGACY_FUNC(string_operator_plus_eq_cstr, GDExtensionInterfaceStringOperatorPlusEqCstr);
+ SETUP_LEGACY_FUNC(string_operator_plus_eq_wcstr, GDExtensionInterfaceStringOperatorPlusEqWcstr);
+ SETUP_LEGACY_FUNC(string_operator_plus_eq_c32str, GDExtensionInterfaceStringOperatorPlusEqC32str);
+ SETUP_LEGACY_FUNC(xml_parser_open_buffer, GDExtensionInterfaceXmlParserOpenBuffer);
+ SETUP_LEGACY_FUNC(file_access_store_buffer, GDExtensionInterfaceFileAccessStoreBuffer);
+ SETUP_LEGACY_FUNC(file_access_get_buffer, GDExtensionInterfaceFileAccessGetBuffer);
+ SETUP_LEGACY_FUNC(worker_thread_pool_add_native_group_task, GDExtensionInterfaceWorkerThreadPoolAddNativeGroupTask);
+ SETUP_LEGACY_FUNC(worker_thread_pool_add_native_task, GDExtensionInterfaceWorkerThreadPoolAddNativeTask);
+ SETUP_LEGACY_FUNC(packed_byte_array_operator_index, GDExtensionInterfacePackedByteArrayOperatorIndex);
+ SETUP_LEGACY_FUNC(packed_byte_array_operator_index_const, GDExtensionInterfacePackedByteArrayOperatorIndexConst);
+ SETUP_LEGACY_FUNC(packed_color_array_operator_index, GDExtensionInterfacePackedColorArrayOperatorIndex);
+ SETUP_LEGACY_FUNC(packed_color_array_operator_index_const, GDExtensionInterfacePackedColorArrayOperatorIndexConst);
+ SETUP_LEGACY_FUNC(packed_float32_array_operator_index, GDExtensionInterfacePackedFloat32ArrayOperatorIndex);
+ SETUP_LEGACY_FUNC(packed_float32_array_operator_index_const, GDExtensionInterfacePackedFloat32ArrayOperatorIndexConst);
+ SETUP_LEGACY_FUNC(packed_float64_array_operator_index, GDExtensionInterfacePackedFloat64ArrayOperatorIndex);
+ SETUP_LEGACY_FUNC(packed_float64_array_operator_index_const, GDExtensionInterfacePackedFloat64ArrayOperatorIndexConst);
+ SETUP_LEGACY_FUNC(packed_int32_array_operator_index, GDExtensionInterfacePackedInt32ArrayOperatorIndex);
+ SETUP_LEGACY_FUNC(packed_int32_array_operator_index_const, GDExtensionInterfacePackedInt32ArrayOperatorIndexConst);
+ SETUP_LEGACY_FUNC(packed_int64_array_operator_index, GDExtensionInterfacePackedInt64ArrayOperatorIndex);
+ SETUP_LEGACY_FUNC(packed_int64_array_operator_index_const, GDExtensionInterfacePackedInt64ArrayOperatorIndexConst);
+ SETUP_LEGACY_FUNC(packed_string_array_operator_index, GDExtensionInterfacePackedStringArrayOperatorIndex);
+ SETUP_LEGACY_FUNC(packed_string_array_operator_index_const, GDExtensionInterfacePackedStringArrayOperatorIndexConst);
+ SETUP_LEGACY_FUNC(packed_vector2_array_operator_index, GDExtensionInterfacePackedVector2ArrayOperatorIndex);
+ SETUP_LEGACY_FUNC(packed_vector2_array_operator_index_const, GDExtensionInterfacePackedVector2ArrayOperatorIndexConst);
+ SETUP_LEGACY_FUNC(packed_vector3_array_operator_index, GDExtensionInterfacePackedVector3ArrayOperatorIndex);
+ SETUP_LEGACY_FUNC(packed_vector3_array_operator_index_const, GDExtensionInterfacePackedVector3ArrayOperatorIndexConst);
+ SETUP_LEGACY_FUNC(array_operator_index, GDExtensionInterfaceArrayOperatorIndex);
+ SETUP_LEGACY_FUNC(array_operator_index_const, GDExtensionInterfaceArrayOperatorIndexConst);
+ SETUP_LEGACY_FUNC(array_ref, GDExtensionInterfaceArrayRef);
+ SETUP_LEGACY_FUNC(array_set_typed, GDExtensionInterfaceArraySetTyped);
+ SETUP_LEGACY_FUNC(dictionary_operator_index, GDExtensionInterfaceDictionaryOperatorIndex);
+ SETUP_LEGACY_FUNC(dictionary_operator_index_const, GDExtensionInterfaceDictionaryOperatorIndexConst);
+ SETUP_LEGACY_FUNC(object_method_bind_call, GDExtensionInterfaceObjectMethodBindCall);
+ SETUP_LEGACY_FUNC(object_method_bind_ptrcall, GDExtensionInterfaceObjectMethodBindPtrcall);
+ SETUP_LEGACY_FUNC(object_destroy, GDExtensionInterfaceObjectDestroy);
+ SETUP_LEGACY_FUNC(global_get_singleton, GDExtensionInterfaceGlobalGetSingleton);
+ SETUP_LEGACY_FUNC(object_get_instance_binding, GDExtensionInterfaceObjectGetInstanceBinding);
+ SETUP_LEGACY_FUNC(object_set_instance_binding, GDExtensionInterfaceObjectSetInstanceBinding);
+ SETUP_LEGACY_FUNC(object_set_instance, GDExtensionInterfaceObjectSetInstance);
+ SETUP_LEGACY_FUNC(object_cast_to, GDExtensionInterfaceObjectCastTo);
+ SETUP_LEGACY_FUNC(object_get_instance_from_id, GDExtensionInterfaceObjectGetInstanceFromId);
+ SETUP_LEGACY_FUNC(object_get_instance_id, GDExtensionInterfaceObjectGetInstanceId);
+ SETUP_LEGACY_FUNC(ref_get_object, GDExtensionInterfaceRefGetObject);
+ SETUP_LEGACY_FUNC(ref_set_object, GDExtensionInterfaceRefSetObject);
+ SETUP_LEGACY_FUNC(script_instance_create, GDExtensionInterfaceScriptInstanceCreate);
+ SETUP_LEGACY_FUNC(classdb_construct_object, GDExtensionInterfaceClassdbConstructObject);
+ SETUP_LEGACY_FUNC(classdb_get_method_bind, GDExtensionInterfaceClassdbGetMethodBind);
+ SETUP_LEGACY_FUNC(classdb_get_class_tag, GDExtensionInterfaceClassdbGetClassTag);
+ SETUP_LEGACY_FUNC(classdb_register_extension_class, GDExtensionInterfaceClassdbRegisterExtensionClass);
+ SETUP_LEGACY_FUNC(classdb_register_extension_class_method, GDExtensionInterfaceClassdbRegisterExtensionClassMethod);
+ SETUP_LEGACY_FUNC(classdb_register_extension_class_integer_constant, GDExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant);
+ SETUP_LEGACY_FUNC(classdb_register_extension_class_property, GDExtensionInterfaceClassdbRegisterExtensionClassProperty);
+ SETUP_LEGACY_FUNC(classdb_register_extension_class_property_group, GDExtensionInterfaceClassdbRegisterExtensionClassPropertyGroup);
+ SETUP_LEGACY_FUNC(classdb_register_extension_class_property_subgroup, GDExtensionInterfaceClassdbRegisterExtensionClassPropertySubgroup);
+ SETUP_LEGACY_FUNC(classdb_register_extension_class_signal, GDExtensionInterfaceClassdbRegisterExtensionClassSignal);
+ SETUP_LEGACY_FUNC(classdb_unregister_extension_class, GDExtensionInterfaceClassdbUnregisterExtensionClass);
+ SETUP_LEGACY_FUNC(get_library_path, GDExtensionInterfaceGetLibraryPath);
+
+ return legacy_gdextension_interface;
+}
+
+#undef SETUP_LEGACY_FUNC
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index f1412d667f..a5ea3918df 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -139,16 +139,37 @@ typedef enum {
} GDExtensionVariantOperator;
+// In this API there are multiple functions which expect the caller to pass a pointer
+// on return value as parameter.
+// In order to make it clear if the caller should initialize the return value or not
+// we have two flavor of types:
+// - `GDExtensionXXXPtr` for pointer on an initialized value
+// - `GDExtensionUninitializedXXXPtr` for pointer on uninitialized value
+//
+// Notes:
+// - Not respecting those requirements can seems harmless, but will lead to unexpected
+// segfault or memory leak (for instance with a specific compiler/OS, or when two
+// native extensions start doing ptrcall on each other).
+// - Initialization must be done with the function pointer returned by `variant_get_ptr_constructor`,
+// zero-initializing the variable should not be considered a valid initialization method here !
+// - Some types have no destructor (see `extension_api.json`'s `has_destructor` field), for
+// them it is always safe to skip the constructor for the return value if you are in a hurry ;-)
+
typedef void *GDExtensionVariantPtr;
typedef const void *GDExtensionConstVariantPtr;
+typedef void *GDExtensionUninitializedVariantPtr;
typedef void *GDExtensionStringNamePtr;
typedef const void *GDExtensionConstStringNamePtr;
+typedef void *GDExtensionUninitializedStringNamePtr;
typedef void *GDExtensionStringPtr;
typedef const void *GDExtensionConstStringPtr;
+typedef void *GDExtensionUninitializedStringPtr;
typedef void *GDExtensionObjectPtr;
typedef const void *GDExtensionConstObjectPtr;
+typedef void *GDExtensionUninitializedObjectPtr;
typedef void *GDExtensionTypePtr;
typedef const void *GDExtensionConstTypePtr;
+typedef void *GDExtensionUninitializedTypePtr;
typedef const void *GDExtensionMethodBindPtr;
typedef int64_t GDExtensionInt;
typedef uint8_t GDExtensionBool;
@@ -401,214 +422,6 @@ typedef struct {
} GDExtensionScriptInstanceInfo;
-/* INTERFACE */
-
-typedef struct {
- uint32_t version_major;
- uint32_t version_minor;
- uint32_t version_patch;
- const char *version_string;
-
- /* GODOT CORE */
-
- void *(*mem_alloc)(size_t p_bytes);
- void *(*mem_realloc)(void *p_ptr, size_t p_bytes);
- void (*mem_free)(void *p_ptr);
-
- void (*print_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify);
- void (*print_error_with_message)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify);
- void (*print_warning)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify);
- void (*print_warning_with_message)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify);
- void (*print_script_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify);
- void (*print_script_error_with_message)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify);
-
- uint64_t (*get_native_struct_size)(GDExtensionConstStringNamePtr p_name);
-
- /* GODOT VARIANT */
-
- /* variant general */
- void (*variant_new_copy)(GDExtensionVariantPtr r_dest, GDExtensionConstVariantPtr p_src);
- void (*variant_new_nil)(GDExtensionVariantPtr r_dest);
- void (*variant_destroy)(GDExtensionVariantPtr p_self);
-
- /* variant type */
- void (*variant_call)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
- void (*variant_call_static)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
- void (*variant_evaluate)(GDExtensionVariantOperator p_op, GDExtensionConstVariantPtr p_a, GDExtensionConstVariantPtr p_b, GDExtensionVariantPtr r_return, GDExtensionBool *r_valid);
- void (*variant_set)(GDExtensionVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid);
- void (*variant_set_named)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid);
- void (*variant_set_keyed)(GDExtensionVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid);
- void (*variant_set_indexed)(GDExtensionVariantPtr p_self, GDExtensionInt p_index, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid, GDExtensionBool *r_oob);
- void (*variant_get)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid);
- void (*variant_get_named)(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid);
- void (*variant_get_keyed)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid);
- void (*variant_get_indexed)(GDExtensionConstVariantPtr p_self, GDExtensionInt p_index, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid, GDExtensionBool *r_oob);
- GDExtensionBool (*variant_iter_init)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionBool *r_valid);
- GDExtensionBool (*variant_iter_next)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionBool *r_valid);
- void (*variant_iter_get)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid);
- GDExtensionInt (*variant_hash)(GDExtensionConstVariantPtr p_self);
- GDExtensionInt (*variant_recursive_hash)(GDExtensionConstVariantPtr p_self, GDExtensionInt p_recursion_count);
- GDExtensionBool (*variant_hash_compare)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_other);
- GDExtensionBool (*variant_booleanize)(GDExtensionConstVariantPtr p_self);
- void (*variant_duplicate)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_ret, GDExtensionBool p_deep);
- void (*variant_stringify)(GDExtensionConstVariantPtr p_self, GDExtensionStringPtr r_ret);
-
- GDExtensionVariantType (*variant_get_type)(GDExtensionConstVariantPtr p_self);
- GDExtensionBool (*variant_has_method)(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_method);
- GDExtensionBool (*variant_has_member)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member);
- GDExtensionBool (*variant_has_key)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionBool *r_valid);
- void (*variant_get_type_name)(GDExtensionVariantType p_type, GDExtensionStringPtr r_name);
- GDExtensionBool (*variant_can_convert)(GDExtensionVariantType p_from, GDExtensionVariantType p_to);
- GDExtensionBool (*variant_can_convert_strict)(GDExtensionVariantType p_from, GDExtensionVariantType p_to);
-
- /* ptrcalls */
- GDExtensionVariantFromTypeConstructorFunc (*get_variant_from_type_constructor)(GDExtensionVariantType p_type);
- GDExtensionTypeFromVariantConstructorFunc (*get_variant_to_type_constructor)(GDExtensionVariantType p_type);
- GDExtensionPtrOperatorEvaluator (*variant_get_ptr_operator_evaluator)(GDExtensionVariantOperator p_operator, GDExtensionVariantType p_type_a, GDExtensionVariantType p_type_b);
- GDExtensionPtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, GDExtensionInt p_hash);
- GDExtensionPtrConstructor (*variant_get_ptr_constructor)(GDExtensionVariantType p_type, int32_t p_constructor);
- GDExtensionPtrDestructor (*variant_get_ptr_destructor)(GDExtensionVariantType p_type);
- void (*variant_construct)(GDExtensionVariantType p_type, GDExtensionVariantPtr p_base, const GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error);
- GDExtensionPtrSetter (*variant_get_ptr_setter)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member);
- GDExtensionPtrGetter (*variant_get_ptr_getter)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member);
- GDExtensionPtrIndexedSetter (*variant_get_ptr_indexed_setter)(GDExtensionVariantType p_type);
- GDExtensionPtrIndexedGetter (*variant_get_ptr_indexed_getter)(GDExtensionVariantType p_type);
- GDExtensionPtrKeyedSetter (*variant_get_ptr_keyed_setter)(GDExtensionVariantType p_type);
- GDExtensionPtrKeyedGetter (*variant_get_ptr_keyed_getter)(GDExtensionVariantType p_type);
- GDExtensionPtrKeyedChecker (*variant_get_ptr_keyed_checker)(GDExtensionVariantType p_type);
- void (*variant_get_constant_value)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_constant, GDExtensionVariantPtr r_ret);
- GDExtensionPtrUtilityFunction (*variant_get_ptr_utility_function)(GDExtensionConstStringNamePtr p_function, GDExtensionInt p_hash);
-
- /* extra utilities */
- void (*string_new_with_latin1_chars)(GDExtensionStringPtr r_dest, const char *p_contents);
- void (*string_new_with_utf8_chars)(GDExtensionStringPtr r_dest, const char *p_contents);
- void (*string_new_with_utf16_chars)(GDExtensionStringPtr r_dest, const char16_t *p_contents);
- void (*string_new_with_utf32_chars)(GDExtensionStringPtr r_dest, const char32_t *p_contents);
- void (*string_new_with_wide_chars)(GDExtensionStringPtr r_dest, const wchar_t *p_contents);
- void (*string_new_with_latin1_chars_and_len)(GDExtensionStringPtr r_dest, const char *p_contents, GDExtensionInt p_size);
- void (*string_new_with_utf8_chars_and_len)(GDExtensionStringPtr r_dest, const char *p_contents, GDExtensionInt p_size);
- void (*string_new_with_utf16_chars_and_len)(GDExtensionStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_size);
- void (*string_new_with_utf32_chars_and_len)(GDExtensionStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_size);
- void (*string_new_with_wide_chars_and_len)(GDExtensionStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_size);
- /* Information about the following functions:
- * - The return value is the resulting encoded string length.
- * - The length returned is in characters, not in bytes. It also does not include a trailing zero.
- * - These functions also do not write trailing zero, If you need it, write it yourself at the position indicated by the length (and make sure to allocate it).
- * - Passing NULL in r_text means only the length is computed (again, without including trailing zero).
- * - p_max_write_length argument is in characters, not bytes. It will be ignored if r_text is NULL.
- * - p_max_write_length argument does not affect the return value, it's only to cap write length.
- */
- GDExtensionInt (*string_to_latin1_chars)(GDExtensionConstStringPtr p_self, char *r_text, GDExtensionInt p_max_write_length);
- GDExtensionInt (*string_to_utf8_chars)(GDExtensionConstStringPtr p_self, char *r_text, GDExtensionInt p_max_write_length);
- GDExtensionInt (*string_to_utf16_chars)(GDExtensionConstStringPtr p_self, char16_t *r_text, GDExtensionInt p_max_write_length);
- GDExtensionInt (*string_to_utf32_chars)(GDExtensionConstStringPtr p_self, char32_t *r_text, GDExtensionInt p_max_write_length);
- GDExtensionInt (*string_to_wide_chars)(GDExtensionConstStringPtr p_self, wchar_t *r_text, GDExtensionInt p_max_write_length);
- char32_t *(*string_operator_index)(GDExtensionStringPtr p_self, GDExtensionInt p_index);
- const char32_t *(*string_operator_index_const)(GDExtensionConstStringPtr p_self, GDExtensionInt p_index);
-
- void (*string_operator_plus_eq_string)(GDExtensionStringPtr p_self, GDExtensionConstStringPtr p_b);
- void (*string_operator_plus_eq_char)(GDExtensionStringPtr p_self, char32_t p_b);
- void (*string_operator_plus_eq_cstr)(GDExtensionStringPtr p_self, const char *p_b);
- void (*string_operator_plus_eq_wcstr)(GDExtensionStringPtr p_self, const wchar_t *p_b);
- void (*string_operator_plus_eq_c32str)(GDExtensionStringPtr p_self, const char32_t *p_b);
-
- /* XMLParser extra utilities */
-
- GDExtensionInt (*xml_parser_open_buffer)(GDExtensionObjectPtr p_instance, const uint8_t *p_buffer, size_t p_size);
-
- /* FileAccess extra utilities */
-
- void (*file_access_store_buffer)(GDExtensionObjectPtr p_instance, const uint8_t *p_src, uint64_t p_length);
- uint64_t (*file_access_get_buffer)(GDExtensionConstObjectPtr p_instance, uint8_t *p_dst, uint64_t p_length);
-
- /* WorkerThreadPool extra utilities */
-
- int64_t (*worker_thread_pool_add_native_group_task)(GDExtensionObjectPtr p_instance, void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks, GDExtensionBool p_high_priority, GDExtensionConstStringPtr p_description);
- int64_t (*worker_thread_pool_add_native_task)(GDExtensionObjectPtr p_instance, void (*p_func)(void *), void *p_userdata, GDExtensionBool p_high_priority, GDExtensionConstStringPtr p_description);
-
- /* Packed array functions */
-
- uint8_t *(*packed_byte_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedByteArray
- const uint8_t *(*packed_byte_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedByteArray
-
- GDExtensionTypePtr (*packed_color_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedColorArray, returns Color ptr
- GDExtensionTypePtr (*packed_color_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedColorArray, returns Color ptr
-
- float *(*packed_float32_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedFloat32Array
- const float *(*packed_float32_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedFloat32Array
- double *(*packed_float64_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedFloat64Array
- const double *(*packed_float64_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedFloat64Array
-
- int32_t *(*packed_int32_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedInt32Array
- const int32_t *(*packed_int32_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedInt32Array
- int64_t *(*packed_int64_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedInt32Array
- const int64_t *(*packed_int64_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedInt32Array
-
- GDExtensionStringPtr (*packed_string_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedStringArray
- GDExtensionStringPtr (*packed_string_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedStringArray
-
- GDExtensionTypePtr (*packed_vector2_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr
- GDExtensionTypePtr (*packed_vector2_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr
- GDExtensionTypePtr (*packed_vector3_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr
- GDExtensionTypePtr (*packed_vector3_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr
-
- GDExtensionVariantPtr (*array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr
- GDExtensionVariantPtr (*array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr
- void (*array_ref)(GDExtensionTypePtr p_self, GDExtensionConstTypePtr p_from); // p_self should be an Array ptr
- void (*array_set_typed)(GDExtensionTypePtr p_self, GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstVariantPtr p_script); // p_self should be an Array ptr
-
- /* Dictionary functions */
-
- GDExtensionVariantPtr (*dictionary_operator_index)(GDExtensionTypePtr p_self, GDExtensionConstVariantPtr p_key); // p_self should be an Dictionary ptr
- GDExtensionVariantPtr (*dictionary_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionConstVariantPtr p_key); // p_self should be an Dictionary ptr
-
- /* OBJECT */
-
- void (*object_method_bind_call)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionVariantPtr r_ret, GDExtensionCallError *r_error);
- void (*object_method_bind_ptrcall)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
- void (*object_destroy)(GDExtensionObjectPtr p_o);
- GDExtensionObjectPtr (*global_get_singleton)(GDExtensionConstStringNamePtr p_name);
-
- void *(*object_get_instance_binding)(GDExtensionObjectPtr p_o, void *p_token, const GDExtensionInstanceBindingCallbacks *p_callbacks);
- void (*object_set_instance_binding)(GDExtensionObjectPtr p_o, void *p_token, void *p_binding, const GDExtensionInstanceBindingCallbacks *p_callbacks);
-
- void (*object_set_instance)(GDExtensionObjectPtr p_o, GDExtensionConstStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance); /* p_classname should be a registered extension class and should extend the p_o object's class. */
-
- GDExtensionObjectPtr (*object_cast_to)(GDExtensionConstObjectPtr p_object, void *p_class_tag);
- GDExtensionObjectPtr (*object_get_instance_from_id)(GDObjectInstanceID p_instance_id);
- GDObjectInstanceID (*object_get_instance_id)(GDExtensionConstObjectPtr p_object);
-
- /* REFERENCE */
-
- GDExtensionObjectPtr (*ref_get_object)(GDExtensionConstRefPtr p_ref);
- void (*ref_set_object)(GDExtensionRefPtr p_ref, GDExtensionObjectPtr p_object);
-
- /* SCRIPT INSTANCE */
-
- GDExtensionScriptInstancePtr (*script_instance_create)(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data);
-
- /* CLASSDB */
-
- GDExtensionObjectPtr (*classdb_construct_object)(GDExtensionConstStringNamePtr p_classname); /* The passed class must be a built-in godot class, or an already-registered extension class. In both case, object_set_instance should be called to fully initialize the object. */
- GDExtensionMethodBindPtr (*classdb_get_method_bind)(GDExtensionConstStringNamePtr p_classname, GDExtensionConstStringNamePtr p_methodname, GDExtensionInt p_hash);
- void *(*classdb_get_class_tag)(GDExtensionConstStringNamePtr p_classname);
-
- /* CLASSDB EXTENSION */
-
- /* Provided parameters for `classdb_register_extension_*` can be safely freed once the function returns. */
- void (*classdb_register_extension_class)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs);
- void (*classdb_register_extension_class_method)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info);
- void (*classdb_register_extension_class_integer_constant)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield);
- void (*classdb_register_extension_class_property)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter);
- void (*classdb_register_extension_class_property_group)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringPtr p_group_name, GDExtensionConstStringPtr p_prefix);
- void (*classdb_register_extension_class_property_subgroup)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringPtr p_subgroup_name, GDExtensionConstStringPtr p_prefix);
- void (*classdb_register_extension_class_signal)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_signal_name, const GDExtensionPropertyInfo *p_argument_info, GDExtensionInt p_argument_count);
- void (*classdb_unregister_extension_class)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */
-
- void (*get_library_path)(GDExtensionClassLibraryPtr p_library, GDExtensionStringPtr r_path);
-
-} GDExtensionInterface;
-
/* INITIALIZATION */
typedef enum {
@@ -630,12 +443,1703 @@ typedef struct {
void (*deinitialize)(void *userdata, GDExtensionInitializationLevel p_level);
} GDExtensionInitialization;
-/* Define a C function prototype that implements the function below and expose it to dlopen() (or similar).
- * This is the entry point of the GDExtension library and will be called on initialization.
- * It can be used to set up different init levels, which are called during various stages of initialization/shutdown.
- * The function name must be a unique one specified in the .gdextension config file.
+typedef void (*GDExtensionInterfaceFunctionPtr)();
+typedef GDExtensionInterfaceFunctionPtr (*GDExtensionInterfaceGetProcAddress)(const char *p_function_name);
+
+/*
+ * Each GDExtension should define a C function that matches the signature of GDExtensionInitializationFunction,
+ * and export it so that it can be loaded via dlopen() or equivalent for the given platform.
+ *
+ * For example:
+ *
+ * GDExtensionBool my_extension_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization);
+ *
+ * This function's name must be specified as the 'entry_symbol' in the .gdextension file.
+ *
+ * This makes it the entry point of the GDExtension and will be called on initialization.
+ *
+ * The GDExtension can then modify the r_initialization structure, setting the minimum initialization level,
+ * and providing pointers to functions that will be called at various stages of initialization/shutdown.
+ *
+ * The rest of the GDExtension's interface to Godot consists of function pointers that can be loaded
+ * by calling p_get_proc_address("...") with the name of the function.
+ *
+ * For example:
+ *
+ * GDExtensionInterfaceGetGodotVersion *get_godot_version = (GDExtensionInterfaceGetGodotVersion)p_get_proc_address("get_godot_version");
+ *
+ * You can then call it like a normal function:
+ *
+ * GDExtensionGodotVersion godot_version;
+ * get_godot_version(&godot_version);
+ * printf("Godot v%d.%d.%d\n", godot_version.major, godot_version.minor, godot_version.patch);
+ *
+ * All of these interface functions are described below, together with the name that's used to load it,
+ * and the function pointer typedef that shows its signature.
+ */
+typedef GDExtensionBool (*GDExtensionInitializationFunction)(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization);
+
+/* INTERFACE */
+
+typedef struct {
+ uint32_t major;
+ uint32_t minor;
+ uint32_t patch;
+ const char *string;
+} GDExtensionGodotVersion;
+
+/**
+ * @name get_godot_version
+ *
+ * Gets the Godot version that the GDExtension was loaded into.
+ *
+ * @param r_godot_version A pointer to the structure to write the version information into.
+ */
+typedef void (*GDExtensionInterfaceGetGodotVersion)(GDExtensionGodotVersion *r_godot_version);
+
+/* INTERFACE: Memory */
+
+/**
+ * @name mem_alloc
+ *
+ * Allocates memory.
+ *
+ * @param p_bytes The amount of memory to allocate in bytes.
+ *
+ * @return A pointer to the allocated memory, or NULL if unsuccessful.
+ */
+typedef void *(*GDExtensionInterfaceMemAlloc)(size_t p_bytes);
+
+/**
+ * @name mem_realloc
+ *
+ * Reallocates memory.
+ *
+ * @param p_ptr A pointer to the previously allocated memory.
+ * @param p_bytes The number of bytes to resize the memory block to.
+ *
+ * @return A pointer to the allocated memory, or NULL if unsuccessful.
+ */
+typedef void *(*GDExtensionInterfaceMemRealloc)(void *p_ptr, size_t p_bytes);
+
+/**
+ * @name mem_free
+ *
+ * Frees memory.
+ *
+ * @param p_ptr A pointer to the previously allocated memory.
+ */
+typedef void (*GDExtensionInterfaceMemFree)(void *p_ptr);
+
+/* INTERFACE: Godot Core */
+
+/**
+ * @name print_error
+ *
+ * Logs an error to Godot's built-in debugger and to the OS terminal.
+ *
+ * @param p_description The code trigging the error.
+ * @param p_function The function name where the error occurred.
+ * @param p_file The file where the error occurred.
+ * @param p_line The line where the error occurred.
+ * @param p_editor_notify Whether or not to notify the editor.
+ */
+typedef void (*GDExtensionInterfacePrintError)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify);
+
+/**
+ * @name print_error_with_message
+ *
+ * Logs an error with a message to Godot's built-in debugger and to the OS terminal.
+ *
+ * @param p_description The code trigging the error.
+ * @param p_message The message to show along with the error.
+ * @param p_function The function name where the error occurred.
+ * @param p_file The file where the error occurred.
+ * @param p_line The line where the error occurred.
+ * @param p_editor_notify Whether or not to notify the editor.
+ */
+typedef void (*GDExtensionInterfacePrintErrorWithMessage)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify);
+
+/**
+ * @name print_warning
+ *
+ * Logs a warning to Godot's built-in debugger and to the OS terminal.
+ *
+ * @param p_description The code trigging the warning.
+ * @param p_function The function name where the warning occurred.
+ * @param p_file The file where the warning occurred.
+ * @param p_line The line where the warning occurred.
+ * @param p_editor_notify Whether or not to notify the editor.
+ */
+typedef void (*GDExtensionInterfacePrintWarning)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify);
+
+/**
+ * @name print_warning_with_message
+ *
+ * Logs a warning with a message to Godot's built-in debugger and to the OS terminal.
+ *
+ * @param p_description The code trigging the warning.
+ * @param p_message The message to show along with the warning.
+ * @param p_function The function name where the warning occurred.
+ * @param p_file The file where the warning occurred.
+ * @param p_line The line where the warning occurred.
+ * @param p_editor_notify Whether or not to notify the editor.
+ */
+typedef void (*GDExtensionInterfacePrintWarningWithMessage)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify);
+
+/**
+ * @name print_script_error
+ *
+ * Logs a script error to Godot's built-in debugger and to the OS terminal.
+ *
+ * @param p_description The code trigging the error.
+ * @param p_function The function name where the error occurred.
+ * @param p_file The file where the error occurred.
+ * @param p_line The line where the error occurred.
+ * @param p_editor_notify Whether or not to notify the editor.
+ */
+typedef void (*GDExtensionInterfacePrintScriptError)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify);
+
+/**
+ * @name print_script_error_with_message
+ *
+ * Logs a script error with a message to Godot's built-in debugger and to the OS terminal.
+ *
+ * @param p_description The code trigging the error.
+ * @param p_message The message to show along with the error.
+ * @param p_function The function name where the error occurred.
+ * @param p_file The file where the error occurred.
+ * @param p_line The line where the error occurred.
+ * @param p_editor_notify Whether or not to notify the editor.
+ */
+typedef void (*GDExtensionInterfacePrintScriptErrorWithMessage)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify);
+
+/**
+ * @name get_native_struct_size
+ *
+ * Gets the size of a native struct (ex. ObjectID) in bytes.
+ *
+ * @param p_name A pointer to a StringName identifying the struct name.
+ *
+ * @return The size in bytes.
+ */
+typedef uint64_t (*GDExtensionInterfaceGetNativeStructSize)(GDExtensionConstStringNamePtr p_name);
+
+/* INTERFACE: Variant */
+
+/**
+ * @name variant_new_copy
+ *
+ * Copies one Variant into a another.
+ *
+ * @param r_dest A pointer to the destination Variant.
+ * @param p_src A pointer to the source Variant.
+ */
+typedef void (*GDExtensionInterfaceVariantNewCopy)(GDExtensionUninitializedVariantPtr r_dest, GDExtensionConstVariantPtr p_src);
+
+/**
+ * @name variant_new_nil
+ *
+ * Creates a new Variant containing nil.
+ *
+ * @param r_dest A pointer to the destination Variant.
+ */
+typedef void (*GDExtensionInterfaceVariantNewNil)(GDExtensionUninitializedVariantPtr r_dest);
+
+/**
+ * @name variant_destroy
+ *
+ * Destroys a Variant.
+ *
+ * @param p_self A pointer to the Variant to destroy.
+ */
+typedef void (*GDExtensionInterfaceVariantDestroy)(GDExtensionVariantPtr p_self);
+
+/**
+ * @name variant_call
+ *
+ * Calls a method on a Variant.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param p_method A pointer to a StringName identifying the method.
+ * @param p_args A pointer to a C array of Variant.
+ * @param p_argument_count The number of arguments.
+ * @param r_return A pointer a Variant which will be assigned the return value.
+ * @param r_error A pointer the structure which will hold error information.
+ *
+ * @see Variant::callp()
+ */
+typedef void (*GDExtensionInterfaceVariantCall)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error);
+
+/**
+ * @name variant_call_static
+ *
+ * Calls a static method on a Variant.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param p_method A pointer to a StringName identifying the method.
+ * @param p_args A pointer to a C array of Variant.
+ * @param p_argument_count The number of arguments.
+ * @param r_return A pointer a Variant which will be assigned the return value.
+ * @param r_error A pointer the structure which will be updated with error information.
+ *
+ * @see Variant::call_static()
+ */
+typedef void (*GDExtensionInterfaceVariantCallStatic)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error);
+
+/**
+ * @name variant_evaluate
+ *
+ * Evaluate an operator on two Variants.
+ *
+ * @param p_op The operator to evaluate.
+ * @param p_a The first Variant.
+ * @param p_b The second Variant.
+ * @param r_return A pointer a Variant which will be assigned the return value.
+ * @param r_valid A pointer to a boolean which will be set to false if the operation is invalid.
+ *
+ * @see Variant::evaluate()
+ */
+typedef void (*GDExtensionInterfaceVariantEvaluate)(GDExtensionVariantOperator p_op, GDExtensionConstVariantPtr p_a, GDExtensionConstVariantPtr p_b, GDExtensionUninitializedVariantPtr r_return, GDExtensionBool *r_valid);
+
+/**
+ * @name variant_set
+ *
+ * Sets a key on a Variant to a value.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param p_key A pointer to a Variant representing the key.
+ * @param p_value A pointer to a Variant representing the value.
+ * @param r_valid A pointer to a boolean which will be set to false if the operation is invalid.
+ *
+ * @see Variant::set()
+ */
+typedef void (*GDExtensionInterfaceVariantSet)(GDExtensionVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid);
+
+/**
+ * @name variant_set_named
+ *
+ * Sets a named key on a Variant to a value.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param p_key A pointer to a StringName representing the key.
+ * @param p_value A pointer to a Variant representing the value.
+ * @param r_valid A pointer to a boolean which will be set to false if the operation is invalid.
+ *
+ * @see Variant::set_named()
+ */
+typedef void (*GDExtensionInterfaceVariantSetNamed)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid);
+
+/**
+ * @name variant_set_keyed
+ *
+ * Sets a keyed property on a Variant to a value.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param p_key A pointer to a Variant representing the key.
+ * @param p_value A pointer to a Variant representing the value.
+ * @param r_valid A pointer to a boolean which will be set to false if the operation is invalid.
+ *
+ * @see Variant::set_keyed()
+ */
+typedef void (*GDExtensionInterfaceVariantSetKeyed)(GDExtensionVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid);
+
+/**
+ * @name variant_set_indexed
+ *
+ * Sets an index on a Variant to a value.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param p_index The index.
+ * @param p_value A pointer to a Variant representing the value.
+ * @param r_valid A pointer to a boolean which will be set to false if the operation is invalid.
+ * @param r_oob A pointer to a boolean which will be set to true if the index is out of bounds.
+ */
+typedef void (*GDExtensionInterfaceVariantSetIndexed)(GDExtensionVariantPtr p_self, GDExtensionInt p_index, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid, GDExtensionBool *r_oob);
+
+/**
+ * @name variant_get
+ *
+ * Gets the value of a key from a Variant.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param p_key A pointer to a Variant representing the key.
+ * @param r_ret A pointer to a Variant which will be assigned the value.
+ * @param r_valid A pointer to a boolean which will be set to false if the operation is invalid.
+ */
+typedef void (*GDExtensionInterfaceVariantGet)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid);
+
+/**
+ * @name variant_get_named
+ *
+ * Gets the value of a named key from a Variant.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param p_key A pointer to a StringName representing the key.
+ * @param r_ret A pointer to a Variant which will be assigned the value.
+ * @param r_valid A pointer to a boolean which will be set to false if the operation is invalid.
+ */
+typedef void (*GDExtensionInterfaceVariantGetNamed)(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid);
+
+/**
+ * @name variant_get_keyed
+ *
+ * Gets the value of a keyed property from a Variant.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param p_key A pointer to a Variant representing the key.
+ * @param r_ret A pointer to a Variant which will be assigned the value.
+ * @param r_valid A pointer to a boolean which will be set to false if the operation is invalid.
+ */
+typedef void (*GDExtensionInterfaceVariantGetKeyed)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid);
+
+/**
+ * @name variant_get_indexed
+ *
+ * Gets the value of an index from a Variant.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param p_index The index.
+ * @param r_ret A pointer to a Variant which will be assigned the value.
+ * @param r_valid A pointer to a boolean which will be set to false if the operation is invalid.
+ * @param r_oob A pointer to a boolean which will be set to true if the index is out of bounds.
+ */
+typedef void (*GDExtensionInterfaceVariantGetIndexed)(GDExtensionConstVariantPtr p_self, GDExtensionInt p_index, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid, GDExtensionBool *r_oob);
+
+/**
+ * @name variant_iter_init
+ *
+ * Initializes an iterator over a Variant.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param r_iter A pointer to a Variant which will be assigned the iterator.
+ * @param r_valid A pointer to a boolean which will be set to false if the operation is invalid.
+ *
+ * @return true if the operation is valid; otherwise false.
+ *
+ * @see Variant::iter_init()
+ */
+typedef GDExtensionBool (*GDExtensionInterfaceVariantIterInit)(GDExtensionConstVariantPtr p_self, GDExtensionUninitializedVariantPtr r_iter, GDExtensionBool *r_valid);
+
+/**
+ * @name variant_iter_next
+ *
+ * Gets the next value for an iterator over a Variant.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param r_iter A pointer to a Variant which will be assigned the iterator.
+ * @param r_valid A pointer to a boolean which will be set to false if the operation is invalid.
+ *
+ * @return true if the operation is valid; otherwise false.
+ *
+ * @see Variant::iter_next()
+ */
+typedef GDExtensionBool (*GDExtensionInterfaceVariantIterNext)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionBool *r_valid);
+
+/**
+ * @name variant_iter_get
+ *
+ * Gets the next value for an iterator over a Variant.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param r_iter A pointer to a Variant which will be assigned the iterator.
+ * @param r_ret A pointer to a Variant which will be assigned false if the operation is invalid.
+ * @param r_valid A pointer to a boolean which will be set to false if the operation is invalid.
+ *
+ * @see Variant::iter_get()
+ */
+typedef void (*GDExtensionInterfaceVariantIterGet)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid);
+
+/**
+ * @name variant_hash
+ *
+ * Gets the hash of a Variant.
+ *
+ * @param p_self A pointer to the Variant.
+ *
+ * @return The hash value.
+ *
+ * @see Variant::hash()
+ */
+typedef GDExtensionInt (*GDExtensionInterfaceVariantHash)(GDExtensionConstVariantPtr p_self);
+
+/**
+ * @name variant_recursive_hash
+ *
+ * Gets the recursive hash of a Variant.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param p_recursion_count The number of recursive loops so far.
+ *
+ * @return The hash value.
+ *
+ * @see Variant::recursive_hash()
+ */
+typedef GDExtensionInt (*GDExtensionInterfaceVariantRecursiveHash)(GDExtensionConstVariantPtr p_self, GDExtensionInt p_recursion_count);
+
+/**
+ * @name variant_hash_compare
+ *
+ * Compares two Variants by their hash.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param p_other A pointer to the other Variant to compare it to.
+ *
+ * @return The hash value.
+ *
+ * @see Variant::hash_compare()
+ */
+typedef GDExtensionBool (*GDExtensionInterfaceVariantHashCompare)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_other);
+
+/**
+ * @name variant_booleanize
+ *
+ * Converts a Variant to a boolean.
+ *
+ * @param p_self A pointer to the Variant.
+ *
+ * @return The boolean value of the Variant.
+ */
+typedef GDExtensionBool (*GDExtensionInterfaceVariantBooleanize)(GDExtensionConstVariantPtr p_self);
+
+/**
+ * @name variant_duplicate
+ *
+ * Duplicates a Variant.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param r_ret A pointer to a Variant to store the duplicated value.
+ * @param p_deep Whether or not to duplicate deeply (when supported by the Variant type).
+ */
+typedef void (*GDExtensionInterfaceVariantDuplicate)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_ret, GDExtensionBool p_deep);
+
+/**
+ * @name variant_stringify
+ *
+ * Converts a Variant to a string.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param r_ret A pointer to a String to store the resulting value.
+ */
+typedef void (*GDExtensionInterfaceVariantStringify)(GDExtensionConstVariantPtr p_self, GDExtensionStringPtr r_ret);
+
+/**
+ * @name variant_get_type
+ *
+ * Gets the type of a Variant.
+ *
+ * @param p_self A pointer to the Variant.
+ *
+ * @return The variant type.
+ */
+typedef GDExtensionVariantType (*GDExtensionInterfaceVariantGetType)(GDExtensionConstVariantPtr p_self);
+
+/**
+ * @name variant_has_method
+ *
+ * Checks if a Variant has the given method.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param p_method A pointer to a StringName with the method name.
+ *
+ * @return
+ */
+typedef GDExtensionBool (*GDExtensionInterfaceVariantHasMethod)(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_method);
+
+/**
+ * @name variant_has_member
+ *
+ * Checks if a type of Variant has the given member.
+ *
+ * @param p_type The Variant type.
+ * @param p_member A pointer to a StringName with the member name.
+ *
+ * @return
+ */
+typedef GDExtensionBool (*GDExtensionInterfaceVariantHasMember)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member);
+
+/**
+ * @name variant_has_key
+ *
+ * Checks if a Variant has a key.
+ *
+ * @param p_self A pointer to the Variant.
+ * @param p_key A pointer to a Variant representing the key.
+ * @param r_valid A pointer to a boolean which will be set to false if the key doesn't exist.
+ *
+ * @return true if the key exists; otherwise false.
+ */
+typedef GDExtensionBool (*GDExtensionInterfaceVariantHasKey)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionBool *r_valid);
+
+/**
+ * @name variant_get_type_name
+ *
+ * Gets the name of a Variant type.
+ *
+ * @param p_type The Variant type.
+ * @param r_name A pointer to a String to store the Variant type name.
+ */
+typedef void (*GDExtensionInterfaceVariantGetTypeName)(GDExtensionVariantType p_type, GDExtensionUninitializedStringPtr r_name);
+
+/**
+ * @name variant_can_convert
+ *
+ * Checks if Variants can be converted from one type to another.
+ *
+ * @param p_from The Variant type to convert from.
+ * @param p_to The Variant type to convert to.
+ *
+ * @return true if the conversion is possible; otherwise false.
+ */
+typedef GDExtensionBool (*GDExtensionInterfaceVariantCanConvert)(GDExtensionVariantType p_from, GDExtensionVariantType p_to);
+
+/**
+ * @name variant_can_convert_strict
+ *
+ * Checks if Variant can be converted from one type to another using stricter rules.
+ *
+ * @param p_from The Variant type to convert from.
+ * @param p_to The Variant type to convert to.
+ *
+ * @return true if the conversion is possible; otherwise false.
+ */
+typedef GDExtensionBool (*GDExtensionInterfaceVariantCanConvertStrict)(GDExtensionVariantType p_from, GDExtensionVariantType p_to);
+
+/**
+ * @name get_variant_from_type_constructor
+ *
+ * Gets a pointer to a function that can create a Variant of the given type from a raw value.
+ *
+ * @param p_type The Variant type.
+ *
+ * @return A pointer to a function that can create a Variant of the given type from a raw value.
+ */
+typedef GDExtensionVariantFromTypeConstructorFunc (*GDExtensionInterfaceGetVariantFromTypeConstructor)(GDExtensionVariantType p_type);
+
+/**
+ * @name get_variant_to_type_constructor
+ *
+ * Gets a pointer to a function that can get the raw value from a Variant of the given type.
+ *
+ * @param p_type The Variant type.
+ *
+ * @return A pointer to a function that can get the raw value from a Variant of the given type.
+ */
+typedef GDExtensionTypeFromVariantConstructorFunc (*GDExtensionInterfaceGetVariantToTypeConstructor)(GDExtensionVariantType p_type);
+
+/**
+ * @name variant_get_ptr_operator_evaluator
+ *
+ * Gets a pointer to a function that can evaluate the given Variant operator on the given Variant types.
+ *
+ * @param p_operator The variant operator.
+ * @param p_type_a The type of the first Variant.
+ * @param p_type_b The type of the second Variant.
+ *
+ * @return A pointer to a function that can evaluate the given Variant operator on the given Variant types.
+ */
+typedef GDExtensionPtrOperatorEvaluator (*GDExtensionInterfaceVariantGetPtrOperatorEvaluator)(GDExtensionVariantOperator p_operator, GDExtensionVariantType p_type_a, GDExtensionVariantType p_type_b);
+
+/**
+ * @name variant_get_ptr_builtin_method
+ *
+ * Gets a pointer to a function that can call a builtin method on a type of Variant.
+ *
+ * @param p_type The Variant type.
+ * @param p_method A pointer to a StringName with the method name.
+ * @param p_hash A hash representing the method signature.
+ *
+ * @return A pointer to a function that can call a builtin method on a type of Variant.
+ */
+typedef GDExtensionPtrBuiltInMethod (*GDExtensionInterfaceVariantGetPtrBuiltinMethod)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, GDExtensionInt p_hash);
+
+/**
+ * @name variant_get_ptr_constructor
+ *
+ * Gets a pointer to a function that can call one of the constructors for a type of Variant.
+ *
+ * @param p_type The Variant type.
+ * @param p_constructor The index of the constructor.
+ *
+ * @return A pointer to a function that can call one of the constructors for a type of Variant.
+ */
+typedef GDExtensionPtrConstructor (*GDExtensionInterfaceVariantGetPtrConstructor)(GDExtensionVariantType p_type, int32_t p_constructor);
+
+/**
+ * @name variant_get_ptr_destructor
+ *
+ * Gets a pointer to a function than can call the destructor for a type of Variant.
+ *
+ * @param p_type The Variant type.
+ *
+ * @return A pointer to a function than can call the destructor for a type of Variant.
+ */
+typedef GDExtensionPtrDestructor (*GDExtensionInterfaceVariantGetPtrDestructor)(GDExtensionVariantType p_type);
+
+/**
+ * @name variant_construct
+ *
+ * Constructs a Variant of the given type, using the first constructor that matches the given arguments.
+ *
+ * @param p_type The Variant type.
+ * @param p_base A pointer to a Variant to store the constructed value.
+ * @param p_args A pointer to a C array of Variant pointers representing the arguments for the constructor.
+ * @param p_argument_count The number of arguments to pass to the constructor.
+ * @param r_error A pointer the structure which will be updated with error information.
+ */
+typedef void (*GDExtensionInterfaceVariantConstruct)(GDExtensionVariantType p_type, GDExtensionUninitializedVariantPtr r_base, const GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error);
+
+/**
+ * @name variant_get_ptr_setter
+ *
+ * Gets a pointer to a function that can call a member's setter on the given Variant type.
+ *
+ * @param p_type The Variant type.
+ * @param p_member A pointer to a StringName with the member name.
+ *
+ * @return A pointer to a function that can call a member's setter on the given Variant type.
+ */
+typedef GDExtensionPtrSetter (*GDExtensionInterfaceVariantGetPtrSetter)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member);
+
+/**
+ * @name variant_get_ptr_getter
+ *
+ * Gets a pointer to a function that can call a member's getter on the given Variant type.
+ *
+ * @param p_type The Variant type.
+ * @param p_member A pointer to a StringName with the member name.
+ *
+ * @return A pointer to a function that can call a member's getter on the given Variant type.
+ */
+typedef GDExtensionPtrGetter (*GDExtensionInterfaceVariantGetPtrGetter)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member);
+
+/**
+ * @name variant_get_ptr_indexed_setter
+ *
+ * Gets a pointer to a function that can set an index on the given Variant type.
+ *
+ * @param p_type The Variant type.
+ *
+ * @return A pointer to a function that can set an index on the given Variant type.
+ */
+typedef GDExtensionPtrIndexedSetter (*GDExtensionInterfaceVariantGetPtrIndexedSetter)(GDExtensionVariantType p_type);
+
+/**
+ * @name variant_get_ptr_indexed_getter
+ *
+ * Gets a pointer to a function that can get an index on the given Variant type.
+ *
+ * @param p_type The Variant type.
+ *
+ * @return A pointer to a function that can get an index on the given Variant type.
+ */
+typedef GDExtensionPtrIndexedGetter (*GDExtensionInterfaceVariantGetPtrIndexedGetter)(GDExtensionVariantType p_type);
+
+/**
+ * @name variant_get_ptr_keyed_setter
+ *
+ * Gets a pointer to a function that can set a key on the given Variant type.
+ *
+ * @param p_type The Variant type.
+ *
+ * @return A pointer to a function that can set a key on the given Variant type.
+ */
+typedef GDExtensionPtrKeyedSetter (*GDExtensionInterfaceVariantGetPtrKeyedSetter)(GDExtensionVariantType p_type);
+
+/**
+ * @name variant_get_ptr_keyed_getter
+ *
+ * Gets a pointer to a function that can get a key on the given Variant type.
+ *
+ * @param p_type The Variant type.
+ *
+ * @return A pointer to a function that can get a key on the given Variant type.
+ */
+typedef GDExtensionPtrKeyedGetter (*GDExtensionInterfaceVariantGetPtrKeyedGetter)(GDExtensionVariantType p_type);
+
+/**
+ * @name variant_get_ptr_keyed_checker
+ *
+ * Gets a pointer to a function that can check a key on the given Variant type.
+ *
+ * @param p_type The Variant type.
+ *
+ * @return A pointer to a function that can check a key on the given Variant type.
+ */
+typedef GDExtensionPtrKeyedChecker (*GDExtensionInterfaceVariantGetPtrKeyedChecker)(GDExtensionVariantType p_type);
+
+/**
+ * @name variant_get_constant_value
+ *
+ * Gets the value of a constant from the given Variant type.
+ *
+ * @param p_type The Variant type.
+ * @param p_constant A pointer to a StringName with the constant name.
+ * @param r_ret A pointer to a Variant to store the value.
+ */
+typedef void (*GDExtensionInterfaceVariantGetConstantValue)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_constant, GDExtensionUninitializedVariantPtr r_ret);
+
+/**
+ * @name variant_get_ptr_utility_function
+ *
+ * Gets a pointer to a function that can call a Variant utility function.
+ *
+ * @param p_function A pointer to a StringName with the function name.
+ * @param p_hash A hash representing the function signature.
+ *
+ * @return A pointer to a function that can call a Variant utility function.
+ */
+typedef GDExtensionPtrUtilityFunction (*GDExtensionInterfaceVariantGetPtrUtilityFunction)(GDExtensionConstStringNamePtr p_function, GDExtensionInt p_hash);
+
+/* INTERFACE: String Utilities */
+
+/**
+ * @name string_new_with_latin1_chars
+ *
+ * Creates a String from a Latin-1 encoded C string.
+ *
+ * @param r_dest A pointer to a Variant to hold the newly created String.
+ * @param p_contents A pointer to a Latin-1 encoded C string (null terminated).
+ */
+typedef void (*GDExtensionInterfaceStringNewWithLatin1Chars)(GDExtensionUninitializedStringPtr r_dest, const char *p_contents);
+
+/**
+ * @name string_new_with_utf8_chars
+ *
+ * Creates a String from a UTF-8 encoded C string.
+ *
+ * @param r_dest A pointer to a Variant to hold the newly created String.
+ * @param p_contents A pointer to a UTF-8 encoded C string (null terminated).
+ */
+typedef void (*GDExtensionInterfaceStringNewWithUtf8Chars)(GDExtensionUninitializedStringPtr r_dest, const char *p_contents);
+
+/**
+ * @name string_new_with_utf16_chars
+ *
+ * Creates a String from a UTF-16 encoded C string.
+ *
+ * @param r_dest A pointer to a Variant to hold the newly created String.
+ * @param p_contents A pointer to a UTF-16 encoded C string (null terminated).
+ */
+typedef void (*GDExtensionInterfaceStringNewWithUtf16Chars)(GDExtensionUninitializedStringPtr r_dest, const char16_t *p_contents);
+
+/**
+ * @name string_new_with_utf32_chars
+ *
+ * Creates a String from a UTF-32 encoded C string.
+ *
+ * @param r_dest A pointer to a Variant to hold the newly created String.
+ * @param p_contents A pointer to a UTF-32 encoded C string (null terminated).
+ */
+typedef void (*GDExtensionInterfaceStringNewWithUtf32Chars)(GDExtensionUninitializedStringPtr r_dest, const char32_t *p_contents);
+
+/**
+ * @name string_new_with_wide_chars
+ *
+ * Creates a String from a wide C string.
+ *
+ * @param r_dest A pointer to a Variant to hold the newly created String.
+ * @param p_contents A pointer to a wide C string (null terminated).
+ */
+typedef void (*GDExtensionInterfaceStringNewWithWideChars)(GDExtensionUninitializedStringPtr r_dest, const wchar_t *p_contents);
+
+/**
+ * @name string_new_with_latin1_chars_and_len
+ *
+ * Creates a String from a Latin-1 encoded C string with the given length.
+ *
+ * @param r_dest A pointer to a Variant to hold the newly created String.
+ * @param p_contents A pointer to a Latin-1 encoded C string.
+ * @param p_size The number of characters.
+ */
+typedef void (*GDExtensionInterfaceStringNewWithLatin1CharsAndLen)(GDExtensionUninitializedStringPtr r_dest, const char *p_contents, GDExtensionInt p_size);
+
+/**
+ * @name string_new_with_utf8_chars_and_len
+ *
+ * Creates a String from a UTF-8 encoded C string with the given length.
+ *
+ * @param r_dest A pointer to a Variant to hold the newly created String.
+ * @param p_contents A pointer to a UTF-8 encoded C string.
+ * @param p_size The number of characters.
+ */
+typedef void (*GDExtensionInterfaceStringNewWithUtf8CharsAndLen)(GDExtensionUninitializedStringPtr r_dest, const char *p_contents, GDExtensionInt p_size);
+
+/**
+ * @name string_new_with_utf16_chars_and_len
+ *
+ * Creates a String from a UTF-16 encoded C string with the given length.
+ *
+ * @param r_dest A pointer to a Variant to hold the newly created String.
+ * @param p_contents A pointer to a UTF-16 encoded C string.
+ * @param p_size The number of characters.
+ */
+typedef void (*GDExtensionInterfaceStringNewWithUtf16CharsAndLen)(GDExtensionUninitializedStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_size);
+
+/**
+ * @name string_new_with_utf32_chars_and_len
+ *
+ * Creates a String from a UTF-32 encoded C string with the given length.
+ *
+ * @param r_dest A pointer to a Variant to hold the newly created String.
+ * @param p_contents A pointer to a UTF-32 encoded C string.
+ * @param p_size The number of characters.
+ */
+typedef void (*GDExtensionInterfaceStringNewWithUtf32CharsAndLen)(GDExtensionUninitializedStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_size);
+
+/**
+ * @name string_new_with_wide_chars_and_len
+ *
+ * Creates a String from a wide C string with the given length.
+ *
+ * @param r_dest A pointer to a Variant to hold the newly created String.
+ * @param p_contents A pointer to a wide C string.
+ * @param p_size The number of characters.
+ */
+typedef void (*GDExtensionInterfaceStringNewWithWideCharsAndLen)(GDExtensionUninitializedStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_size);
+
+/**
+ * @name string_to_latin1_chars
+ *
+ * Converts a String to a Latin-1 encoded C string.
+ *
+ * It doesn't write a null terminator.
+ *
+ * @param p_self A pointer to the String.
+ * @param r_text A pointer to the buffer to hold the resulting data. If NULL is passed in, only the length will be computed.
+ * @param p_max_write_length The maximum number of characters that can be written to r_text. It has no affect on the return value.
+ *
+ * @return The resulting encoded string length in characters (not bytes), not including a null terminator.
+ */
+typedef GDExtensionInt (*GDExtensionInterfaceStringToLatin1Chars)(GDExtensionConstStringPtr p_self, char *r_text, GDExtensionInt p_max_write_length);
+
+/**
+ * @name string_to_utf8_chars
+ *
+ * Converts a String to a UTF-8 encoded C string.
+ *
+ * It doesn't write a null terminator.
+ *
+ * @param p_self A pointer to the String.
+ * @param r_text A pointer to the buffer to hold the resulting data. If NULL is passed in, only the length will be computed.
+ * @param p_max_write_length The maximum number of characters that can be written to r_text. It has no affect on the return value.
+ *
+ * @return The resulting encoded string length in characters (not bytes), not including a null terminator.
+ */
+typedef GDExtensionInt (*GDExtensionInterfaceStringToUtf8Chars)(GDExtensionConstStringPtr p_self, char *r_text, GDExtensionInt p_max_write_length);
+
+/**
+ * @name string_to_utf16_chars
+ *
+ * Converts a String to a UTF-16 encoded C string.
+ *
+ * It doesn't write a null terminator.
+ *
+ * @param p_self A pointer to the String.
+ * @param r_text A pointer to the buffer to hold the resulting data. If NULL is passed in, only the length will be computed.
+ * @param p_max_write_length The maximum number of characters that can be written to r_text. It has no affect on the return value.
+ *
+ * @return The resulting encoded string length in characters (not bytes), not including a null terminator.
+ */
+typedef GDExtensionInt (*GDExtensionInterfaceStringToUtf16Chars)(GDExtensionConstStringPtr p_self, char16_t *r_text, GDExtensionInt p_max_write_length);
+
+/**
+ * @name string_to_utf32_chars
+ *
+ * Converts a String to a UTF-32 encoded C string.
+ *
+ * It doesn't write a null terminator.
+ *
+ * @param p_self A pointer to the String.
+ * @param r_text A pointer to the buffer to hold the resulting data. If NULL is passed in, only the length will be computed.
+ * @param p_max_write_length The maximum number of characters that can be written to r_text. It has no affect on the return value.
+ *
+ * @return The resulting encoded string length in characters (not bytes), not including a null terminator.
+ */
+typedef GDExtensionInt (*GDExtensionInterfaceStringToUtf32Chars)(GDExtensionConstStringPtr p_self, char32_t *r_text, GDExtensionInt p_max_write_length);
+
+/**
+ * @name string_to_wide_chars
+ *
+ * Converts a String to a wide C string.
+ *
+ * It doesn't write a null terminator.
+ *
+ * @param p_self A pointer to the String.
+ * @param r_text A pointer to the buffer to hold the resulting data. If NULL is passed in, only the length will be computed.
+ * @param p_max_write_length The maximum number of characters that can be written to r_text. It has no affect on the return value.
+ *
+ * @return The resulting encoded string length in characters (not bytes), not including a null terminator.
+ */
+typedef GDExtensionInt (*GDExtensionInterfaceStringToWideChars)(GDExtensionConstStringPtr p_self, wchar_t *r_text, GDExtensionInt p_max_write_length);
+
+/**
+ * @name string_operator_index
+ *
+ * Gets a pointer to the character at the given index from a String.
+ *
+ * @param p_self A pointer to the String.
+ * @param p_index The index.
+ *
+ * @return A pointer to the requested character.
+ */
+typedef char32_t *(*GDExtensionInterfaceStringOperatorIndex)(GDExtensionStringPtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name string_operator_index_const
+ *
+ * Gets a const pointer to the character at the given index from a String.
+ *
+ * @param p_self A pointer to the String.
+ * @param p_index The index.
+ *
+ * @return A const pointer to the requested character.
+ */
+typedef const char32_t *(*GDExtensionInterfaceStringOperatorIndexConst)(GDExtensionConstStringPtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name string_operator_plus_eq_string
+ *
+ * Appends another String to a String.
+ *
+ * @param p_self A pointer to the String.
+ * @param p_b A pointer to the other String to append.
+ */
+typedef void (*GDExtensionInterfaceStringOperatorPlusEqString)(GDExtensionStringPtr p_self, GDExtensionConstStringPtr p_b);
+
+/**
+ * @name string_operator_plus_eq_char
+ *
+ * Appends a character to a String.
+ *
+ * @param p_self A pointer to the String.
+ * @param p_b A pointer to the character to append.
+ */
+typedef void (*GDExtensionInterfaceStringOperatorPlusEqChar)(GDExtensionStringPtr p_self, char32_t p_b);
+
+/**
+ * @name string_operator_plus_eq_cstr
+ *
+ * Appends a Latin-1 encoded C string to a String.
+ *
+ * @param p_self A pointer to the String.
+ * @param p_b A pointer to a Latin-1 encoded C string (null terminated).
+ */
+typedef void (*GDExtensionInterfaceStringOperatorPlusEqCstr)(GDExtensionStringPtr p_self, const char *p_b);
+
+/**
+ * @name string_operator_plus_eq_wcstr
+ *
+ * Appends a wide C string to a String.
+ *
+ * @param p_self A pointer to the String.
+ * @param p_b A pointer to a wide C string (null terminated).
+ */
+typedef void (*GDExtensionInterfaceStringOperatorPlusEqWcstr)(GDExtensionStringPtr p_self, const wchar_t *p_b);
+
+/**
+ * @name string_operator_plus_eq_c32str
+ *
+ * Appends a UTF-32 encoded C string to a String.
+ *
+ * @param p_self A pointer to the String.
+ * @param p_b A pointer to a UTF-32 encoded C string (null terminated).
+ */
+typedef void (*GDExtensionInterfaceStringOperatorPlusEqC32str)(GDExtensionStringPtr p_self, const char32_t *p_b);
+
+/* INTERFACE: XMLParser Utilities */
+
+/**
+ * @name xml_parser_open_buffer
+ *
+ * Opens a raw XML buffer on an XMLParser instance.
+ *
+ * @param p_instance A pointer to an XMLParser object.
+ * @param p_buffer A pointer to the buffer.
+ * @param p_size The size of the buffer.
+ *
+ * @return A Godot error code (ex. OK, ERR_INVALID_DATA, etc).
+ *
+ * @see XMLParser::open_buffer()
+ */
+typedef GDExtensionInt (*GDExtensionInterfaceXmlParserOpenBuffer)(GDExtensionObjectPtr p_instance, const uint8_t *p_buffer, size_t p_size);
+
+/* INTERFACE: FileAccess Utilities */
+
+/**
+ * @name file_access_store_buffer
+ *
+ * Stores the given buffer using an instance of FileAccess.
+ *
+ * @param p_instance A pointer to a FileAccess object.
+ * @param p_src A pointer to the buffer.
+ * @param p_length The size of the buffer.
+ *
+ * @see FileAccess::store_buffer()
+ */
+typedef void (*GDExtensionInterfaceFileAccessStoreBuffer)(GDExtensionObjectPtr p_instance, const uint8_t *p_src, uint64_t p_length);
+
+/**
+ * @name file_access_get_buffer
+ *
+ * Reads the next p_length bytes into the given buffer using an instance of FileAccess.
+ *
+ * @param p_instance A pointer to a FileAccess object.
+ * @param p_dst A pointer to the buffer to store the data.
+ * @param p_length The requested number of bytes to read.
+ *
+ * @return The actual number of bytes read (may be less than requested).
+ */
+typedef uint64_t (*GDExtensionInterfaceFileAccessGetBuffer)(GDExtensionConstObjectPtr p_instance, uint8_t *p_dst, uint64_t p_length);
+
+/* INTERFACE: WorkerThreadPool Utilities */
+
+/**
+ * @name worker_thread_pool_add_native_group_task
+ *
+ * Adds a group task to an instance of WorkerThreadPool.
+ *
+ * @param p_instance A pointer to a WorkerThreadPool object.
+ * @param p_func A pointer to a function to run in the thread pool.
+ * @param p_userdata A pointer to arbitrary data which will be passed to p_func.
+ * @param p_tasks The number of tasks needed in the group.
+ * @param p_high_priority Whether or not this is a high priority task.
+ * @param p_description A pointer to a String with the task description.
+ *
+ * @return The task group ID.
+ *
+ * @see WorkerThreadPool::add_group_task()
+ */
+typedef int64_t (*GDExtensionInterfaceWorkerThreadPoolAddNativeGroupTask)(GDExtensionObjectPtr p_instance, void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks, GDExtensionBool p_high_priority, GDExtensionConstStringPtr p_description);
+
+/**
+ * @name worker_thread_pool_add_native_task
+ *
+ * Adds a task to an instance of WorkerThreadPool.
+ *
+ * @param p_instance A pointer to a WorkerThreadPool object.
+ * @param p_func A pointer to a function to run in the thread pool.
+ * @param p_userdata A pointer to arbitrary data which will be passed to p_func.
+ * @param p_high_priority Whether or not this is a high priority task.
+ * @param p_description A pointer to a String with the task description.
+ *
+ * @return The task ID.
+ */
+typedef int64_t (*GDExtensionInterfaceWorkerThreadPoolAddNativeTask)(GDExtensionObjectPtr p_instance, void (*p_func)(void *), void *p_userdata, GDExtensionBool p_high_priority, GDExtensionConstStringPtr p_description);
+
+/* INTERFACE: Packed Array */
+
+/**
+ * @name packed_byte_array_operator_index
+ *
+ * Gets a pointer to a byte in a PackedByteArray.
+ *
+ * @param p_self A pointer to a PackedByteArray object.
+ * @param p_index The index of the byte to get.
+ *
+ * @return A pointer to the requested byte.
+ */
+typedef uint8_t *(*GDExtensionInterfacePackedByteArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_byte_array_operator_index_const
+ *
+ * Gets a const pointer to a byte in a PackedByteArray.
+ *
+ * @param p_self A const pointer to a PackedByteArray object.
+ * @param p_index The index of the byte to get.
+ *
+ * @return A const pointer to the requested byte.
+ */
+typedef const uint8_t *(*GDExtensionInterfacePackedByteArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_color_array_operator_index
+ *
+ * Gets a pointer to a color in a PackedColorArray.
+ *
+ * @param p_self A pointer to a PackedColorArray object.
+ * @param p_index The index of the Color to get.
+ *
+ * @return A pointer to the requested Color.
+ */
+typedef GDExtensionTypePtr (*GDExtensionInterfacePackedColorArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_color_array_operator_index_const
+ *
+ * Gets a const pointer to a color in a PackedColorArray.
+ *
+ * @param p_self A const pointer to a const PackedColorArray object.
+ * @param p_index The index of the Color to get.
+ *
+ * @return A const pointer to the requested Color.
+ */
+typedef GDExtensionTypePtr (*GDExtensionInterfacePackedColorArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_float32_array_operator_index
+ *
+ * Gets a pointer to a 32-bit float in a PackedFloat32Array.
+ *
+ * @param p_self A pointer to a PackedFloat32Array object.
+ * @param p_index The index of the float to get.
+ *
+ * @return A pointer to the requested 32-bit float.
+ */
+typedef float *(*GDExtensionInterfacePackedFloat32ArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_float32_array_operator_index_const
+ *
+ * Gets a const pointer to a 32-bit float in a PackedFloat32Array.
+ *
+ * @param p_self A const pointer to a PackedFloat32Array object.
+ * @param p_index The index of the float to get.
+ *
+ * @return A const pointer to the requested 32-bit float.
+ */
+typedef const float *(*GDExtensionInterfacePackedFloat32ArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_float64_array_operator_index
+ *
+ * Gets a pointer to a 64-bit float in a PackedFloat64Array.
+ *
+ * @param p_self A pointer to a PackedFloat64Array object.
+ * @param p_index The index of the float to get.
+ *
+ * @return A pointer to the requested 64-bit float.
+ */
+typedef double *(*GDExtensionInterfacePackedFloat64ArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_float64_array_operator_index_const
+ *
+ * Gets a const pointer to a 64-bit float in a PackedFloat64Array.
+ *
+ * @param p_self A const pointer to a PackedFloat64Array object.
+ * @param p_index The index of the float to get.
+ *
+ * @return A const pointer to the requested 64-bit float.
+ */
+typedef const double *(*GDExtensionInterfacePackedFloat64ArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_int32_array_operator_index
+ *
+ * Gets a pointer to a 32-bit integer in a PackedInt32Array.
+ *
+ * @param p_self A pointer to a PackedInt32Array object.
+ * @param p_index The index of the integer to get.
+ *
+ * @return A pointer to the requested 32-bit integer.
+ */
+typedef int32_t *(*GDExtensionInterfacePackedInt32ArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_int32_array_operator_index_const
+ *
+ * Gets a const pointer to a 32-bit integer in a PackedInt32Array.
+ *
+ * @param p_self A const pointer to a PackedInt32Array object.
+ * @param p_index The index of the integer to get.
+ *
+ * @return A const pointer to the requested 32-bit integer.
+ */
+typedef const int32_t *(*GDExtensionInterfacePackedInt32ArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_int64_array_operator_index
+ *
+ * Gets a pointer to a 64-bit integer in a PackedInt64Array.
+ *
+ * @param p_self A pointer to a PackedInt64Array object.
+ * @param p_index The index of the integer to get.
+ *
+ * @return A pointer to the requested 64-bit integer.
+ */
+typedef int64_t *(*GDExtensionInterfacePackedInt64ArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_int64_array_operator_index_const
+ *
+ * Gets a const pointer to a 64-bit integer in a PackedInt64Array.
+ *
+ * @param p_self A const pointer to a PackedInt64Array object.
+ * @param p_index The index of the integer to get.
+ *
+ * @return A const pointer to the requested 64-bit integer.
+ */
+typedef const int64_t *(*GDExtensionInterfacePackedInt64ArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_string_array_operator_index
+ *
+ * Gets a pointer to a string in a PackedStringArray.
+ *
+ * @param p_self A pointer to a PackedStringArray object.
+ * @param p_index The index of the String to get.
+ *
+ * @return A pointer to the requested String.
+ */
+typedef GDExtensionStringPtr (*GDExtensionInterfacePackedStringArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_string_array_operator_index_const
+ *
+ * Gets a const pointer to a string in a PackedStringArray.
+ *
+ * @param p_self A const pointer to a PackedStringArray object.
+ * @param p_index The index of the String to get.
+ *
+ * @return A const pointer to the requested String.
+ */
+typedef GDExtensionStringPtr (*GDExtensionInterfacePackedStringArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_vector2_array_operator_index
+ *
+ * Gets a pointer to a Vector2 in a PackedVector2Array.
+ *
+ * @param p_self A pointer to a PackedVector2Array object.
+ * @param p_index The index of the Vector2 to get.
+ *
+ * @return A pointer to the requested Vector2.
+ */
+typedef GDExtensionTypePtr (*GDExtensionInterfacePackedVector2ArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_vector2_array_operator_index_const
+ *
+ * Gets a const pointer to a Vector2 in a PackedVector2Array.
+ *
+ * @param p_self A const pointer to a PackedVector2Array object.
+ * @param p_index The index of the Vector2 to get.
+ *
+ * @return A const pointer to the requested Vector2.
+ */
+typedef GDExtensionTypePtr (*GDExtensionInterfacePackedVector2ArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_vector3_array_operator_index
+ *
+ * Gets a pointer to a Vector3 in a PackedVector3Array.
+ *
+ * @param p_self A pointer to a PackedVector3Array object.
+ * @param p_index The index of the Vector3 to get.
+ *
+ * @return A pointer to the requested Vector3.
+ */
+typedef GDExtensionTypePtr (*GDExtensionInterfacePackedVector3ArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_vector3_array_operator_index_const
+ *
+ * Gets a const pointer to a Vector3 in a PackedVector3Array.
+ *
+ * @param p_self A const pointer to a PackedVector3Array object.
+ * @param p_index The index of the Vector3 to get.
+ *
+ * @return A const pointer to the requested Vector3.
+ */
+typedef GDExtensionTypePtr (*GDExtensionInterfacePackedVector3ArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name array_operator_index
+ *
+ * Gets a pointer to a Variant in an Array.
+ *
+ * @param p_self A pointer to an Array object.
+ * @param p_index The index of the Variant to get.
+ *
+ * @return A pointer to the requested Variant.
+ */
+typedef GDExtensionVariantPtr (*GDExtensionInterfaceArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name array_operator_index_const
+ *
+ * Gets a const pointer to a Variant in an Array.
+ *
+ * @param p_self A const pointer to an Array object.
+ * @param p_index The index of the Variant to get.
+ *
+ * @return A const pointer to the requested Variant.
+ */
+typedef GDExtensionVariantPtr (*GDExtensionInterfaceArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name array_ref
+ *
+ * Sets an Array to be a reference to another Array object.
+ *
+ * @param p_self A pointer to the Array object to update.
+ * @param p_from A pointer to the Array object to reference.
+ */
+typedef void (*GDExtensionInterfaceArrayRef)(GDExtensionTypePtr p_self, GDExtensionConstTypePtr p_from);
+
+/**
+ * @name array_set_typed
+ *
+ * Makes an Array into a typed Array.
+ *
+ * @param p_self A pointer to the Array.
+ * @param p_type The type of Variant the Array will store.
+ * @param p_class_name A pointer to a StringName with the name of the object (if p_type is GDEXTENSION_VARIANT_TYPE_OBJECT).
+ * @param p_script A pointer to a Script object (if p_type is GDEXTENSION_VARIANT_TYPE_OBJECT and the base class is extended by a script).
+ */
+typedef void (*GDExtensionInterfaceArraySetTyped)(GDExtensionTypePtr p_self, GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstVariantPtr p_script);
+
+/* INTERFACE: Dictionary */
+
+/**
+ * @name dictionary_operator_index
+ *
+ * Gets a pointer to a Variant in a Dictionary with the given key.
+ *
+ * @param p_self A pointer to a Dictionary object.
+ * @param p_key A pointer to a Variant representing the key.
+ *
+ * @return A pointer to a Variant representing the value at the given key.
+ */
+typedef GDExtensionVariantPtr (*GDExtensionInterfaceDictionaryOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionConstVariantPtr p_key);
+
+/**
+ * @name dictionary_operator_index_const
+ *
+ * Gets a const pointer to a Variant in a Dictionary with the given key.
+ *
+ * @param p_self A const pointer to a Dictionary object.
+ * @param p_key A pointer to a Variant representing the key.
+ *
+ * @return A const pointer to a Variant representing the value at the given key.
+ */
+typedef GDExtensionVariantPtr (*GDExtensionInterfaceDictionaryOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionConstVariantPtr p_key);
+
+/* INTERFACE: Object */
+
+/**
+ * @name object_method_bind_call
+ *
+ * Calls a method on an Object.
+ *
+ * @param p_method_bind A pointer to the MethodBind representing the method on the Object's class.
+ * @param p_instance A pointer to the Object.
+ * @param p_args A pointer to a C array of Variants representing the arguments.
+ * @param p_arg_count The number of arguments.
+ * @param r_ret A pointer to Variant which will receive the return value.
+ * @param r_error A pointer to a GDExtensionCallError struct that will receive error information.
+ */
+typedef void (*GDExtensionInterfaceObjectMethodBindCall)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionUninitializedVariantPtr r_ret, GDExtensionCallError *r_error);
+
+/**
+ * @name object_method_bind_ptrcall
+ *
+ * Calls a method on an Object (using a "ptrcall").
+ *
+ * @param p_method_bind A pointer to the MethodBind representing the method on the Object's class.
+ * @param p_instance A pointer to the Object.
+ * @param p_args A pointer to a C array representing the arguments.
+ * @param r_ret A pointer to the Object that will receive the return value.
+ */
+typedef void (*GDExtensionInterfaceObjectMethodBindPtrcall)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
+
+/**
+ * @name object_destroy
+ *
+ * Destroys an Object.
+ *
+ * @param p_o A pointer to the Object.
+ */
+typedef void (*GDExtensionInterfaceObjectDestroy)(GDExtensionObjectPtr p_o);
+
+/**
+ * @name global_get_singleton
+ *
+ * Gets a global singleton by name.
+ *
+ * @param p_name A pointer to a StringName with the singleton name.
+ *
+ * @return A pointer to the singleton Object.
+ */
+typedef GDExtensionObjectPtr (*GDExtensionInterfaceGlobalGetSingleton)(GDExtensionConstStringNamePtr p_name);
+
+/**
+ * @name object_get_instance_binding
+ *
+ * Gets a pointer representing an Object's instance binding.
+ *
+ * @param p_o A pointer to the Object.
+ * @param p_library A token the library received by the GDExtension's entry point function.
+ * @param p_callbacks A pointer to a GDExtensionInstanceBindingCallbacks struct.
+ *
+ * @return
+ */
+typedef void *(*GDExtensionInterfaceObjectGetInstanceBinding)(GDExtensionObjectPtr p_o, void *p_token, const GDExtensionInstanceBindingCallbacks *p_callbacks);
+
+/**
+ * @name object_set_instance_binding
+ *
+ * Sets an Object's instance binding.
+ *
+ * @param p_o A pointer to the Object.
+ * @param p_library A token the library received by the GDExtension's entry point function.
+ * @param p_binding A pointer to the instance binding.
+ * @param p_callbacks A pointer to a GDExtensionInstanceBindingCallbacks struct.
+ */
+typedef void (*GDExtensionInterfaceObjectSetInstanceBinding)(GDExtensionObjectPtr p_o, void *p_token, void *p_binding, const GDExtensionInstanceBindingCallbacks *p_callbacks);
+
+/**
+ * @name object_set_instance
+ *
+ * Sets an extension class instance on a Object.
+ *
+ * @param p_o A pointer to the Object.
+ * @param p_classname A pointer to a StringName with the registered extension class's name.
+ * @param p_instance A pointer to the extension class instance.
+ */
+typedef void (*GDExtensionInterfaceObjectSetInstance)(GDExtensionObjectPtr p_o, GDExtensionConstStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance); /* p_classname should be a registered extension class and should extend the p_o object's class. */
+
+/**
+ * @name object_get_class_name
+ *
+ * Gets the class name of an Object.
+ *
+ * @param p_object A pointer to the Object.
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param r_class_name A pointer to a String to receive the class name.
+ *
+ * @return true if successful in getting the class name; otherwise false.
+ */
+typedef GDExtensionBool (*GDExtensionInterfaceObjectGetClassName)(GDExtensionConstObjectPtr p_object, GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringNamePtr r_class_name);
+
+/**
+ * @name object_cast_to
+ *
+ * Casts an Object to a different type.
+ *
+ * @param p_object A pointer to the Object.
+ * @param p_class_tag A pointer uniquely identifying a built-in class in the ClassDB.
+ *
+ * @return Returns a pointer to the Object, or NULL if it can't be cast to the requested type.
+ */
+typedef GDExtensionObjectPtr (*GDExtensionInterfaceObjectCastTo)(GDExtensionConstObjectPtr p_object, void *p_class_tag);
+
+/**
+ * @name object_get_instance_from_id
+ *
+ * Gets an Object by its instance ID.
+ *
+ * @param p_instance_id The instance ID.
+ *
+ * @return A pointer to the Object.
+ */
+typedef GDExtensionObjectPtr (*GDExtensionInterfaceObjectGetInstanceFromId)(GDObjectInstanceID p_instance_id);
+
+/**
+ * @name object_get_instance_id
+ *
+ * Gets the instance ID from an Object.
+ *
+ * @param p_object A pointer to the Object.
+ *
+ * @return The instance ID.
+ */
+typedef GDObjectInstanceID (*GDExtensionInterfaceObjectGetInstanceId)(GDExtensionConstObjectPtr p_object);
+
+/* INTERFACE: Reference */
+
+/**
+ * @name ref_get_object
+ *
+ * Gets the Object from a reference.
+ *
+ * @param p_ref A pointer to the reference.
+ *
+ * @return A pointer to the Object from the reference or NULL.
+ */
+typedef GDExtensionObjectPtr (*GDExtensionInterfaceRefGetObject)(GDExtensionConstRefPtr p_ref);
+
+/**
+ * @name ref_set_object
+ *
+ * Sets the Object referred to by a reference.
+ *
+ * @param p_ref A pointer to the reference.
+ * @param p_object A pointer to the Object to refer to.
+ */
+typedef void (*GDExtensionInterfaceRefSetObject)(GDExtensionRefPtr p_ref, GDExtensionObjectPtr p_object);
+
+/* INTERFACE: Script Instance */
+
+/**
+ * @name script_instance_create
+ *
+ * Creates a script instance that contains the given info and instance data.
+ *
+ * @param p_info A pointer to a GDExtensionScriptInstanceInfo struct.
+ * @param p_instance_data A pointer to a data representing the script instance in the GDExtension. This will be passed to all the function pointers on p_info.
+ *
+ * @return A pointer to a ScriptInstanceExtension object.
+ */
+typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate)(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data);
+
+/* INTERFACE: ClassDB */
+
+/**
+ * @name classdb_construct_object
+ *
+ * Constructs an Object of the requested class.
+ *
+ * The passed class must be a built-in godot class, or an already-registered extension class. In both cases, object_set_instance() should be called to fully initialize the object.
+ *
+ * @param p_classname A pointer to a StringName with the class name.
+ *
+ * @return A pointer to the newly created Object.
+ */
+typedef GDExtensionObjectPtr (*GDExtensionInterfaceClassdbConstructObject)(GDExtensionConstStringNamePtr p_classname);
+
+/**
+ * @name classdb_get_method_bind
+ *
+ * Gets a pointer to the MethodBind in ClassDB for the given class, method and hash.
+ *
+ * @param p_classname A pointer to a StringName with the class name.
+ * @param p_methodname A pointer to a StringName with the method name.
+ * @param p_hash A hash representing the function signature.
+ *
+ * @return A pointer to the MethodBind from ClassDB.
+ */
+typedef GDExtensionMethodBindPtr (*GDExtensionInterfaceClassdbGetMethodBind)(GDExtensionConstStringNamePtr p_classname, GDExtensionConstStringNamePtr p_methodname, GDExtensionInt p_hash);
+
+/**
+ * @name classdb_get_class_tag
+ *
+ * Gets a pointer uniquely identifying the given built-in class in the ClassDB.
+ *
+ * @param p_classname A pointer to a StringName with the class name.
+ *
+ * @return A pointer uniquely identifying the built-in class in the ClassDB.
+ */
+typedef void *(*GDExtensionInterfaceClassdbGetClassTag)(GDExtensionConstStringNamePtr p_classname);
+
+/* INTERFACE: ClassDB Extension */
+
+/**
+ * @name classdb_register_extension_class
+ *
+ * Registers an extension class in the ClassDB.
+ *
+ * Provided struct can be safely freed once the function returns.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param p_class_name A pointer to a StringName with the class name.
+ * @param p_parent_class_name A pointer to a StringName with the parent class name.
+ * @param p_extension_funcs A pointer to a GDExtensionClassCreationInfo struct.
+ */
+typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs);
+
+/**
+ * @name classdb_register_extension_class_method
+ *
+ * Registers a method on an extension class in the ClassDB.
+ *
+ * Provided struct can be safely freed once the function returns.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param p_class_name A pointer to a StringName with the class name.
+ * @param p_method_info A pointer to a GDExtensionClassMethodInfo struct.
+ */
+typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassMethod)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info);
+
+/**
+ * @name classdb_register_extension_class_integer_constant
+ *
+ * Registers an integer constant on an extension class in the ClassDB.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param p_class_name A pointer to a StringName with the class name.
+ * @param p_enum_name A pointer to a StringName with the enum name.
+ * @param p_constant_name A pointer to a StringName with the constant name.
+ * @param p_constant_value The constant value.
+ * @param p_is_bitfield Whether or not this is a bit field.
+ */
+typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield);
+
+/**
+ * @name classdb_register_extension_class_property
+ *
+ * Registers a property on an extension class in the ClassDB.
+ *
+ * Provided struct can be safely freed once the function returns.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param p_class_name A pointer to a StringName with the class name.
+ * @param p_info A pointer to a GDExtensionPropertyInfo struct.
+ * @param p_setter A pointer to a StringName with the name of the setter method.
+ * @param p_getter A pointer to a StringName with the name of the getter method.
+ */
+typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassProperty)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter);
+
+/**
+ * @name classdb_register_extension_class_property_group
+ *
+ * Registers a property group on an extension class in the ClassDB.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param p_class_name A pointer to a StringName with the class name.
+ * @param p_group_name A pointer to a String with the group name.
+ * @param p_prefix A pointer to a String with the prefix used by properties in this group.
+ */
+typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassPropertyGroup)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringPtr p_group_name, GDExtensionConstStringPtr p_prefix);
+
+/**
+ * @name classdb_register_extension_class_property_subgroup
+ *
+ * Registers a property subgroup on an extension class in the ClassDB.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param p_class_name A pointer to a StringName with the class name.
+ * @param p_subgroup_name A pointer to a String with the subgroup name.
+ * @param p_prefix A pointer to a String with the prefix used by properties in this subgroup.
+ */
+typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassPropertySubgroup)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringPtr p_subgroup_name, GDExtensionConstStringPtr p_prefix);
+
+/**
+ * @name classdb_register_extension_class_signal
+ *
+ * Registers a signal on an extension class in the ClassDB.
+ *
+ * Provided structs can be safely freed once the function returns.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param p_class_name A pointer to a StringName with the class name.
+ * @param p_signal_name A pointer to a StringName with the signal name.
+ * @param p_argument_info A pointer to a GDExtensionPropertyInfo struct.
+ * @param p_argument_count The number of arguments the signal receives.
+ */
+typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassSignal)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_signal_name, const GDExtensionPropertyInfo *p_argument_info, GDExtensionInt p_argument_count);
+
+/**
+ * @name classdb_unregister_extension_class
+ *
+ * Unregisters an extension class in the ClassDB.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param p_class_name A pointer to a StringName with the class name.
+ */
+typedef void (*GDExtensionInterfaceClassdbUnregisterExtensionClass)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */
+
+/**
+ * @name get_library_path
+ *
+ * Gets the path to the current GDExtension library.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param r_path A pointer to a String which will receive the path.
*/
-typedef GDExtensionBool (*GDExtensionInitializationFunction)(const GDExtensionInterface *p_interface, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization);
+typedef void (*GDExtensionInterfaceGetLibraryPath)(GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringPtr r_path);
#ifdef __cplusplus
}
diff --git a/core/extension/make_interface_dumper.py b/core/extension/make_interface_dumper.py
index a604112d13..a85d62eff3 100644
--- a/core/extension/make_interface_dumper.py
+++ b/core/extension/make_interface_dumper.py
@@ -1,9 +1,19 @@
+import zlib
+
+
def run(target, source, env):
src = source[0]
dst = target[0]
- f = open(src, "r", encoding="utf-8")
+ f = open(src, "rb")
g = open(dst, "w", encoding="utf-8")
+ buf = f.read()
+ decomp_size = len(buf)
+
+ # Use maximum zlib compression level to further reduce file size
+ # (at the cost of initial build times).
+ buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
+
g.write(
"""/* THIS FILE IS GENERATED DO NOT EDIT */
#ifndef GDEXTENSION_INTERFACE_DUMP_H
@@ -11,25 +21,32 @@ def run(target, source, env):
#ifdef TOOLS_ENABLED
+#include "core/io/compression.h"
#include "core/io/file_access.h"
#include "core/string/ustring.h"
-class GDExtensionInterfaceDump {
- private:
- static constexpr char const *gdextension_interface_dump ="""
+"""
)
- for line in f:
- g.write('"' + line.rstrip().replace('"', '\\"') + '\\n"\n')
- g.write(";\n")
+
+ g.write("static const int _gdextension_interface_data_compressed_size = " + str(len(buf)) + ";\n")
+ g.write("static const int _gdextension_interface_data_uncompressed_size = " + str(decomp_size) + ";\n")
+ g.write("static const unsigned char _gdextension_interface_data_compressed[] = {\n")
+ for i in range(len(buf)):
+ g.write("\t" + str(buf[i]) + ",\n")
+ g.write("};\n")
g.write(
"""
+class GDExtensionInterfaceDump {
public:
static void generate_gdextension_interface_file(const String &p_path) {
Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE);
ERR_FAIL_COND_MSG(fa.is_null(), vformat("Cannot open file '%s' for writing.", p_path));
- CharString cs(gdextension_interface_dump);
- fa->store_buffer((const uint8_t *)cs.ptr(), cs.length());
+ Vector<uint8_t> data;
+ data.resize(_gdextension_interface_data_uncompressed_size);
+ int ret = Compression::decompress(data.ptrw(), _gdextension_interface_data_uncompressed_size, _gdextension_interface_data_compressed, _gdextension_interface_data_compressed_size, Compression::MODE_DEFLATE);
+ ERR_FAIL_COND_MSG(ret == -1, "Compressed file is corrupt.");
+ fa->store_buffer(data.ptr(), data.size());
};
};
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 2b3e0b56e4..1348389481 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -542,6 +542,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
Ref<InputEventScreenTouch> touch_event;
touch_event.instantiate();
touch_event->set_pressed(mb->is_pressed());
+ touch_event->set_canceled(mb->is_canceled());
touch_event->set_position(mb->get_position());
touch_event->set_double_tap(mb->is_double_click());
touch_event->set_device(InputEvent::DEVICE_ID_EMULATION);
@@ -613,6 +614,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
button_event->set_position(st->get_position());
button_event->set_global_position(st->get_position());
button_event->set_pressed(st->is_pressed());
+ button_event->set_canceled(st->is_canceled());
button_event->set_button_index(MouseButton::LEFT);
button_event->set_double_click(st->is_double_tap());
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index 19e4d6182a..95be01bc65 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -52,15 +52,15 @@ bool InputEvent::is_action(const StringName &p_action, bool p_exact_match) const
}
bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo, bool p_exact_match) const {
- bool pressed;
- bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>(const_cast<InputEvent *>(this)), p_action, p_exact_match, &pressed, nullptr, nullptr);
- return valid && pressed && (p_allow_echo || !is_echo());
+ bool pressed_state;
+ bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>(const_cast<InputEvent *>(this)), p_action, p_exact_match, &pressed_state, nullptr, nullptr);
+ return valid && pressed_state && (p_allow_echo || !is_echo());
}
bool InputEvent::is_action_released(const StringName &p_action, bool p_exact_match) const {
- bool pressed;
- bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>(const_cast<InputEvent *>(this)), p_action, p_exact_match, &pressed, nullptr, nullptr);
- return valid && !pressed;
+ bool pressed_state;
+ bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>(const_cast<InputEvent *>(this)), p_action, p_exact_match, &pressed_state, nullptr, nullptr);
+ return valid && !pressed_state;
}
float InputEvent::get_action_strength(const StringName &p_action, bool p_exact_match) const {
@@ -75,8 +75,16 @@ float InputEvent::get_action_raw_strength(const StringName &p_action, bool p_exa
return valid ? raw_strength : 0.0f;
}
+bool InputEvent::is_canceled() const {
+ return canceled;
+}
+
bool InputEvent::is_pressed() const {
- return false;
+ return pressed && !canceled;
+}
+
+bool InputEvent::is_released() const {
+ return !pressed && !canceled;
}
bool InputEvent::is_echo() const {
@@ -108,7 +116,9 @@ void InputEvent::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_action_released", "action", "exact_match"), &InputEvent::is_action_released, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact_match"), &InputEvent::get_action_strength, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("is_canceled"), &InputEvent::is_canceled);
ClassDB::bind_method(D_METHOD("is_pressed"), &InputEvent::is_pressed);
+ ClassDB::bind_method(D_METHOD("is_released"), &InputEvent::is_released);
ClassDB::bind_method(D_METHOD("is_echo"), &InputEvent::is_echo);
ClassDB::bind_method(D_METHOD("as_text"), &InputEvent::as_text);
@@ -318,10 +328,6 @@ void InputEventKey::set_pressed(bool p_pressed) {
emit_changed();
}
-bool InputEventKey::is_pressed() const {
- return pressed;
-}
-
void InputEventKey::set_keycode(Key p_keycode) {
keycode = p_keycode;
emit_changed();
@@ -671,8 +677,8 @@ void InputEventMouseButton::set_pressed(bool p_pressed) {
pressed = p_pressed;
}
-bool InputEventMouseButton::is_pressed() const {
- return pressed;
+void InputEventMouseButton::set_canceled(bool p_canceled) {
+ canceled = p_canceled;
}
void InputEventMouseButton::set_double_click(bool p_double_click) {
@@ -699,6 +705,7 @@ Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, co
mb->set_button_mask(get_button_mask());
mb->set_pressed(pressed);
+ mb->set_canceled(canceled);
mb->set_double_click(double_click);
mb->set_factor(factor);
mb->set_button_index(button_index);
@@ -794,6 +801,7 @@ String InputEventMouseButton::as_text() const {
String InputEventMouseButton::to_string() {
String p = is_pressed() ? "true" : "false";
+ String canceled_state = is_canceled() ? "true" : "false";
String d = double_click ? "true" : "false";
MouseButton idx = get_button_index();
@@ -820,7 +828,7 @@ String InputEventMouseButton::to_string() {
// Work around the fact vformat can only take 5 substitutions but 6 need to be passed.
String index_and_mods = vformat("button_index=%s, mods=%s", button_index, mods);
- return vformat("InputEventMouseButton: %s, pressed=%s, position=(%s), button_mask=%d, double_click=%s", index_and_mods, p, String(get_position()), get_button_mask(), d);
+ return vformat("InputEventMouseButton: %s, pressed=%s, canceled=%s, position=(%s), button_mask=%d, double_click=%s", index_and_mods, p, canceled_state, String(get_position()), get_button_mask(), d);
}
void InputEventMouseButton::_bind_methods() {
@@ -831,13 +839,14 @@ void InputEventMouseButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_button_index"), &InputEventMouseButton::get_button_index);
ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &InputEventMouseButton::set_pressed);
- // ClassDB::bind_method(D_METHOD("is_pressed"), &InputEventMouseButton::is_pressed);
+ ClassDB::bind_method(D_METHOD("set_canceled", "canceled"), &InputEventMouseButton::set_canceled);
ClassDB::bind_method(D_METHOD("set_double_click", "double_click"), &InputEventMouseButton::set_double_click);
ClassDB::bind_method(D_METHOD("is_double_click"), &InputEventMouseButton::is_double_click);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "factor"), "set_factor", "get_factor");
ADD_PROPERTY(PropertyInfo(Variant::INT, "button_index"), "set_button_index", "get_button_index");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "canceled"), "set_canceled", "is_canceled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "double_click"), "set_double_click", "is_double_click");
}
@@ -945,6 +954,10 @@ bool InputEventMouseMotion::accumulate(const Ref<InputEvent> &p_event) {
return false;
}
+ if (is_canceled() != motion->is_canceled()) {
+ return false;
+ }
+
if (is_pressed() != motion->is_pressed()) {
return false;
}
@@ -1015,6 +1028,7 @@ JoyAxis InputEventJoypadMotion::get_axis() const {
void InputEventJoypadMotion::set_axis_value(float p_value) {
axis_value = p_value;
+ pressed = Math::abs(axis_value) >= 0.5f;
emit_changed();
}
@@ -1022,10 +1036,6 @@ float InputEventJoypadMotion::get_axis_value() const {
return axis_value;
}
-bool InputEventJoypadMotion::is_pressed() const {
- return Math::abs(axis_value) >= 0.5f;
-}
-
bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
Ref<InputEventJoypadMotion> jm = p_event;
if (jm.is_null()) {
@@ -1040,12 +1050,12 @@ bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool p
if (match) {
float jm_abs_axis_value = Math::abs(jm->get_axis_value());
bool same_direction = (((axis_value < 0) == (jm->axis_value < 0)) || jm->axis_value == 0);
- bool pressed = same_direction && jm_abs_axis_value >= p_deadzone;
+ bool pressed_state = same_direction && jm_abs_axis_value >= p_deadzone;
if (r_pressed != nullptr) {
- *r_pressed = pressed;
+ *r_pressed = pressed_state;
}
if (r_strength != nullptr) {
- if (pressed) {
+ if (pressed_state) {
if (p_deadzone == 1.0f) {
*r_strength = 1.0f;
} else {
@@ -1125,10 +1135,6 @@ void InputEventJoypadButton::set_pressed(bool p_pressed) {
pressed = p_pressed;
}
-bool InputEventJoypadButton::is_pressed() const {
- return pressed;
-}
-
void InputEventJoypadButton::set_pressure(float p_pressure) {
pressure = p_pressure;
}
@@ -1209,7 +1215,7 @@ String InputEventJoypadButton::as_text() const {
}
String InputEventJoypadButton::to_string() {
- String p = pressed ? "true" : "false";
+ String p = is_pressed() ? "true" : "false";
return vformat("InputEventJoypadButton: button_index=%d, pressed=%s, pressure=%.2f", button_index, p, pressure);
}
@@ -1229,7 +1235,6 @@ void InputEventJoypadButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pressure"), &InputEventJoypadButton::get_pressure);
ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &InputEventJoypadButton::set_pressed);
- // ClassDB::bind_method(D_METHOD("is_pressed"), &InputEventJoypadButton::is_pressed);
ADD_PROPERTY(PropertyInfo(Variant::INT, "button_index"), "set_button_index", "get_button_index");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pressure"), "set_pressure", "get_pressure");
@@ -1258,8 +1263,8 @@ void InputEventScreenTouch::set_pressed(bool p_pressed) {
pressed = p_pressed;
}
-bool InputEventScreenTouch::is_pressed() const {
- return pressed;
+void InputEventScreenTouch::set_canceled(bool p_canceled) {
+ canceled = p_canceled;
}
void InputEventScreenTouch::set_double_tap(bool p_double_tap) {
@@ -1277,21 +1282,23 @@ Ref<InputEvent> InputEventScreenTouch::xformed_by(const Transform2D &p_xform, co
st->set_index(index);
st->set_position(p_xform.xform(pos + p_local_ofs));
st->set_pressed(pressed);
+ st->set_canceled(canceled);
st->set_double_tap(double_tap);
return st;
}
String InputEventScreenTouch::as_text() const {
- String status = pressed ? RTR("touched") : RTR("released");
+ String status = canceled ? RTR("canceled") : (pressed ? RTR("touched") : RTR("released"));
return vformat(RTR("Screen %s at (%s) with %s touch points"), status, String(get_position()), itos(index));
}
String InputEventScreenTouch::to_string() {
String p = pressed ? "true" : "false";
+ String canceled_state = canceled ? "true" : "false";
String double_tap_string = double_tap ? "true" : "false";
- return vformat("InputEventScreenTouch: index=%d, pressed=%s, position=(%s), double_tap=%s", index, p, String(get_position()), double_tap_string);
+ return vformat("InputEventScreenTouch: index=%d, pressed=%s, canceled=%s, position=(%s), double_tap=%s", index, p, canceled_state, String(get_position()), double_tap_string);
}
void InputEventScreenTouch::_bind_methods() {
@@ -1302,13 +1309,14 @@ void InputEventScreenTouch::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_position"), &InputEventScreenTouch::get_position);
ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &InputEventScreenTouch::set_pressed);
- //ClassDB::bind_method(D_METHOD("is_pressed"),&InputEventScreenTouch::is_pressed);
+ ClassDB::bind_method(D_METHOD("set_canceled", "canceled"), &InputEventScreenTouch::set_canceled);
ClassDB::bind_method(D_METHOD("set_double_tap", "double_tap"), &InputEventScreenTouch::set_double_tap);
ClassDB::bind_method(D_METHOD("is_double_tap"), &InputEventScreenTouch::is_double_tap);
ADD_PROPERTY(PropertyInfo(Variant::INT, "index"), "set_index", "get_index");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_NONE, "suffix:px"), "set_position", "get_position");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "canceled"), "set_canceled", "is_canceled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "double_tap"), "set_double_tap", "is_double_tap");
}
@@ -1460,10 +1468,6 @@ void InputEventAction::set_pressed(bool p_pressed) {
pressed = p_pressed;
}
-bool InputEventAction::is_pressed() const {
- return pressed;
-}
-
void InputEventAction::set_strength(float p_strength) {
strength = CLAMP(p_strength, 0.0f, 1.0f);
}
@@ -1492,7 +1496,7 @@ bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool p_exact
bool match = action == act->action;
if (match) {
- bool act_pressed = act->pressed;
+ bool act_pressed = act->is_pressed();
if (r_pressed != nullptr) {
*r_pressed = act_pressed;
}
@@ -1523,7 +1527,7 @@ String InputEventAction::as_text() const {
}
String InputEventAction::to_string() {
- String p = pressed ? "true" : "false";
+ String p = is_pressed() ? "true" : "false";
return vformat("InputEventAction: action=\"%s\", pressed=%s", action, p);
}
@@ -1532,13 +1536,10 @@ void InputEventAction::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_action"), &InputEventAction::get_action);
ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &InputEventAction::set_pressed);
- //ClassDB::bind_method(D_METHOD("is_pressed"), &InputEventAction::is_pressed);
ClassDB::bind_method(D_METHOD("set_strength", "strength"), &InputEventAction::set_strength);
ClassDB::bind_method(D_METHOD("get_strength"), &InputEventAction::get_strength);
- // ClassDB::bind_method(D_METHOD("is_action", "name"), &InputEventAction::is_action);
-
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "action"), "set_action", "get_action");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_strength", "get_strength");
@@ -1761,10 +1762,6 @@ void InputEventShortcut::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "Shortcut"), "set_shortcut", "get_shortcut");
}
-bool InputEventShortcut::is_pressed() const {
- return true;
-}
-
String InputEventShortcut::as_text() const {
ERR_FAIL_COND_V(shortcut.is_null(), "None");
diff --git a/core/input/input_event.h b/core/input/input_event.h
index 4be42d0bd2..59a87239bd 100644
--- a/core/input/input_event.h
+++ b/core/input/input_event.h
@@ -56,6 +56,9 @@ class InputEvent : public Resource {
int device = 0;
protected:
+ bool canceled = false;
+ bool pressed = false;
+
static void _bind_methods();
public:
@@ -71,8 +74,9 @@ public:
float get_action_strength(const StringName &p_action, bool p_exact_match = false) const;
float get_action_raw_strength(const StringName &p_action, bool p_exact_match = false) const;
- // To be removed someday, since they do not make sense for all events
- virtual bool is_pressed() const;
+ bool is_canceled() const;
+ bool is_pressed() const;
+ bool is_released() const;
virtual bool is_echo() const;
virtual String as_text() const = 0;
@@ -149,8 +153,6 @@ public:
class InputEventKey : public InputEventWithModifiers {
GDCLASS(InputEventKey, InputEventWithModifiers);
- bool pressed = false; /// otherwise release
-
Key keycode = Key::NONE; // Key enum, without modifier masks.
Key physical_keycode = Key::NONE;
Key key_label = Key::NONE;
@@ -163,7 +165,6 @@ protected:
public:
void set_pressed(bool p_pressed);
- virtual bool is_pressed() const override;
void set_keycode(Key p_keycode);
Key get_keycode() const;
@@ -229,7 +230,6 @@ class InputEventMouseButton : public InputEventMouse {
float factor = 1;
MouseButton button_index = MouseButton::NONE;
- bool pressed = false; //otherwise released
bool double_click = false; //last even less than double click time
protected:
@@ -243,7 +243,7 @@ public:
MouseButton get_button_index() const;
void set_pressed(bool p_pressed);
- virtual bool is_pressed() const override;
+ void set_canceled(bool p_canceled);
void set_double_click(bool p_double_click);
bool is_double_click() const;
@@ -312,8 +312,6 @@ public:
void set_axis_value(float p_value);
float get_axis_value() const;
- virtual bool is_pressed() const override;
-
virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override;
virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override;
@@ -328,7 +326,6 @@ class InputEventJoypadButton : public InputEvent {
GDCLASS(InputEventJoypadButton, InputEvent);
JoyButton button_index = (JoyButton)0;
- bool pressed = false;
float pressure = 0; //0 to 1
protected:
static void _bind_methods();
@@ -338,7 +335,6 @@ public:
JoyButton get_button_index() const;
void set_pressed(bool p_pressed);
- virtual bool is_pressed() const override;
void set_pressure(float p_pressure);
float get_pressure() const;
@@ -360,7 +356,6 @@ class InputEventScreenTouch : public InputEventFromWindow {
GDCLASS(InputEventScreenTouch, InputEventFromWindow);
int index = 0;
Vector2 pos;
- bool pressed = false;
bool double_tap = false;
protected:
@@ -374,7 +369,7 @@ public:
Vector2 get_position() const;
void set_pressed(bool p_pressed);
- virtual bool is_pressed() const override;
+ void set_canceled(bool p_canceled);
void set_double_tap(bool p_double_tap);
bool is_double_tap() const;
@@ -434,7 +429,6 @@ class InputEventAction : public InputEvent {
GDCLASS(InputEventAction, InputEvent);
StringName action;
- bool pressed = false;
float strength = 1.0f;
protected:
@@ -445,7 +439,6 @@ public:
StringName get_action() const;
void set_pressed(bool p_pressed);
- virtual bool is_pressed() const override;
void set_strength(float p_strength);
float get_strength() const;
@@ -569,7 +562,6 @@ protected:
public:
void set_shortcut(Ref<Shortcut> p_shortcut);
Ref<Shortcut> get_shortcut();
- virtual bool is_pressed() const override;
virtual String as_text() const override;
virtual String to_string() override;
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 190edbfb82..09505ea05d 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -63,8 +63,9 @@ Error HTTPClient::_request_raw(Method p_method, const String &p_url, const Vecto
}
Error HTTPClient::_request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body) {
- int size = p_body.length();
- return request(p_method, p_url, p_headers, size > 0 ? (const uint8_t *)p_body.utf8().get_data() : nullptr, size);
+ CharString body_utf8 = p_body.utf8();
+ int size = body_utf8.length();
+ return request(p_method, p_url, p_headers, size > 0 ? (const uint8_t *)body_utf8.get_data() : nullptr, size);
}
String HTTPClient::query_string_from_dict(const Dictionary &p_dict) {
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index 3788fa501e..2f45238951 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -60,6 +60,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, Ref<TLSOp
}
ERR_FAIL_COND_V(tls_options.is_valid() && tls_options->is_server(), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V_MSG(tls_options.is_valid() && !StreamPeerTLS::is_available(), ERR_UNAVAILABLE, "HTTPS is not available in this build.");
ERR_FAIL_COND_V(conn_host.length() < HOST_MIN_LEN, ERR_INVALID_PARAMETER);
if (conn_port < 0) {
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index f852e8d382..ac1870fe88 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -302,6 +302,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
if (!Thread::is_main_thread()) {
mq_override = memnew(CallQueue);
MessageQueue::set_thread_singleton_override(mq_override);
+ set_current_thread_safe_for_nodes(true);
}
} else {
DEV_ASSERT(load_task.dependent_path.is_empty());
@@ -309,6 +310,9 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
// --
Ref<Resource> res = _load(load_task.remapped_path, load_task.remapped_path != load_task.local_path ? load_task.local_path : String(), load_task.type_hint, load_task.cache_mode, &load_task.error, load_task.use_sub_threads, &load_task.progress);
+ if (mq_override) {
+ mq_override->flush();
+ }
thread_load_mutex.lock();
@@ -354,7 +358,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
if (load_nesting == 0 && mq_override) {
memdelete(mq_override);
- MessageQueue::set_thread_singleton_override(nullptr);
+ set_current_thread_safe_for_nodes(false);
}
}
@@ -476,9 +480,6 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path,
if (run_on_current_thread) {
load_task_ptr->thread_id = Thread::get_caller_id();
- if (must_not_register) {
- load_token->res_if_unregistered = load_task_ptr->resource;
- }
} else {
load_task_ptr->task_id = WorkerThreadPool::get_singleton()->add_native_task(&ResourceLoader::_thread_load_function, load_task_ptr);
}
@@ -486,6 +487,9 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path,
if (run_on_current_thread) {
_thread_load_function(load_task_ptr);
+ if (must_not_register) {
+ load_token->res_if_unregistered = load_task_ptr->resource;
+ }
}
return load_token;
@@ -613,14 +617,33 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro
return Ref<Resource>();
}
- if (load_task.task_id != 0 && !load_task.awaited) {
- // Loading thread is in the worker pool and still not awaited.
+ if (load_task.task_id != 0) {
+ // Loading thread is in the worker pool.
load_task.awaited = true;
thread_load_mutex.unlock();
- WorkerThreadPool::get_singleton()->wait_for_task_completion(load_task.task_id);
- thread_load_mutex.lock();
+ Error err = WorkerThreadPool::get_singleton()->wait_for_task_completion(load_task.task_id);
+ if (err == ERR_BUSY) {
+ // The WorkerThreadPool has scheduled tasks in a way that the current load depends on
+ // another one in a lower stack frame. Restart such load here. When the stack is eventually
+ // unrolled, the original load will have been notified to go on.
+#ifdef DEV_ENABLED
+ print_verbose("ResourceLoader: Load task happened to wait on another one deep in the call stack. Attempting to avoid deadlock by re-issuing the load now.");
+#endif
+ // CACHE_MODE_IGNORE is needed because, otherwise, the new request would just see there's
+ // an ongoing load for that resource and wait for it again. This value forces a new load.
+ Ref<ResourceLoader::LoadToken> token = _load_start(load_task.local_path, load_task.type_hint, LOAD_THREAD_DISTRIBUTE, ResourceFormatLoader::CACHE_MODE_IGNORE);
+ Ref<Resource> resource = _load_complete(*token.ptr(), &err);
+ if (r_error) {
+ *r_error = err;
+ }
+ thread_load_mutex.lock();
+ return resource;
+ } else {
+ DEV_ASSERT(err == OK);
+ thread_load_mutex.lock();
+ }
} else {
- // Loading thread is main or user thread, or in the worker pool, but already awaited by some other thread.
+ // Loading thread is main or user thread.
if (!load_task.cond_var) {
load_task.cond_var = memnew(ConditionVariable);
}
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index bfd902c7e2..1481dbc32e 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -1016,12 +1016,15 @@ void Basis::rotate_sh(real_t *p_values) {
p_values[8] = d4 * s_scale_dst4;
}
-Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up) {
+Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(p_target.is_zero_approx(), Basis(), "The target vector can't be zero.");
ERR_FAIL_COND_V_MSG(p_up.is_zero_approx(), Basis(), "The up vector can't be zero.");
#endif
- Vector3 v_z = -p_target.normalized();
+ Vector3 v_z = p_target.normalized();
+ if (!p_use_model_front) {
+ v_z = -v_z;
+ }
Vector3 v_x = p_up.cross(v_z);
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(v_x.is_zero_approx(), Basis(), "The target vector and up vector can't be parallel to each other.");
diff --git a/core/math/basis.h b/core/math/basis.h
index bbc1d40469..1a68bee686 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -217,7 +217,7 @@ struct _NO_DISCARD_ Basis {
operator Quaternion() const { return get_quaternion(); }
- static Basis looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0));
+ static Basis looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0), bool p_use_model_front = false);
Basis(const Quaternion &p_quaternion) { set_quaternion(p_quaternion); };
Basis(const Quaternion &p_quaternion, const Vector3 &p_scale) { set_quaternion_scale(p_quaternion, p_scale); }
diff --git a/core/math/color.cpp b/core/math/color.cpp
index 0d9325f236..d36306d968 100644
--- a/core/math/color.cpp
+++ b/core/math/color.cpp
@@ -247,8 +247,7 @@ void Color::set_ok_hsl(float p_h, float p_s, float p_l, float p_alpha) {
hsl.h = p_h;
hsl.s = p_s;
hsl.l = p_l;
- ok_color new_ok_color;
- ok_color::RGB rgb = new_ok_color.okhsl_to_srgb(hsl);
+ ok_color::RGB rgb = ok_color::okhsl_to_srgb(hsl);
Color c = Color(rgb.r, rgb.g, rgb.b, p_alpha).clamp();
r = c.r;
g = c.g;
@@ -595,8 +594,7 @@ float Color::get_ok_hsl_h() const {
rgb.r = r;
rgb.g = g;
rgb.b = b;
- ok_color new_ok_color;
- ok_color::HSL ok_hsl = new_ok_color.srgb_to_okhsl(rgb);
+ ok_color::HSL ok_hsl = ok_color::srgb_to_okhsl(rgb);
if (Math::is_nan(ok_hsl.h)) {
return 0.0f;
}
@@ -608,8 +606,7 @@ float Color::get_ok_hsl_s() const {
rgb.r = r;
rgb.g = g;
rgb.b = b;
- ok_color new_ok_color;
- ok_color::HSL ok_hsl = new_ok_color.srgb_to_okhsl(rgb);
+ ok_color::HSL ok_hsl = ok_color::srgb_to_okhsl(rgb);
if (Math::is_nan(ok_hsl.s)) {
return 0.0f;
}
@@ -621,8 +618,7 @@ float Color::get_ok_hsl_l() const {
rgb.r = r;
rgb.g = g;
rgb.b = b;
- ok_color new_ok_color;
- ok_color::HSL ok_hsl = new_ok_color.srgb_to_okhsl(rgb);
+ ok_color::HSL ok_hsl = ok_color::srgb_to_okhsl(rgb);
if (Math::is_nan(ok_hsl.l)) {
return 0.0f;
}
diff --git a/core/math/transform_3d.cpp b/core/math/transform_3d.cpp
index 8d497209f1..cdc94676c9 100644
--- a/core/math/transform_3d.cpp
+++ b/core/math/transform_3d.cpp
@@ -77,20 +77,20 @@ void Transform3D::rotate_basis(const Vector3 &p_axis, real_t p_angle) {
basis.rotate(p_axis, p_angle);
}
-Transform3D Transform3D::looking_at(const Vector3 &p_target, const Vector3 &p_up) const {
+Transform3D Transform3D::looking_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) const {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(origin.is_equal_approx(p_target), Transform3D(), "The transform's origin and target can't be equal.");
#endif
Transform3D t = *this;
- t.basis = Basis::looking_at(p_target - origin, p_up);
+ t.basis = Basis::looking_at(p_target - origin, p_up, p_use_model_front);
return t;
}
-void Transform3D::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up) {
+void Transform3D::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) {
#ifdef MATH_CHECKS
ERR_FAIL_COND_MSG(p_eye.is_equal_approx(p_target), "The eye and target vectors can't be equal.");
#endif
- basis = Basis::looking_at(p_target - p_eye, p_up);
+ basis = Basis::looking_at(p_target - p_eye, p_up, p_use_model_front);
origin = p_eye;
}
diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h
index bf1b4cdb63..70141a3dbe 100644
--- a/core/math/transform_3d.h
+++ b/core/math/transform_3d.h
@@ -52,8 +52,8 @@ struct _NO_DISCARD_ Transform3D {
void rotate(const Vector3 &p_axis, real_t p_angle);
void rotate_basis(const Vector3 &p_axis, real_t p_angle);
- void set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0));
- Transform3D looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)) const;
+ void set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0), bool p_use_model_front = false);
+ Transform3D looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0), bool p_use_model_front = false) const;
void scale(const Vector3 &p_scale);
Transform3D scaled(const Vector3 &p_scale) const;
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index 760f3bfd0c..cc4a29164d 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -562,6 +562,60 @@ MethodBind *ClassDB::get_method(const StringName &p_class, const StringName &p_n
return nullptr;
}
+Vector<uint32_t> ClassDB::get_method_compatibility_hashes(const StringName &p_class, const StringName &p_name) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ if (type->method_map_compatibility.has(p_name)) {
+ LocalVector<MethodBind *> *c = type->method_map_compatibility.getptr(p_name);
+ Vector<uint32_t> ret;
+ for (uint32_t i = 0; i < c->size(); i++) {
+ ret.push_back((*c)[i]->get_hash());
+ }
+ return ret;
+ }
+ type = type->inherits_ptr;
+ }
+ return Vector<uint32_t>();
+}
+
+MethodBind *ClassDB::get_method_with_compatibility(const StringName &p_class, const StringName &p_name, uint64_t p_hash, bool *r_method_exists, bool *r_is_deprecated) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ MethodBind **method = type->method_map.getptr(p_name);
+ if (method && *method) {
+ if (r_method_exists) {
+ *r_method_exists = true;
+ }
+ if ((*method)->get_hash() == p_hash) {
+ return *method;
+ }
+ }
+
+ LocalVector<MethodBind *> *compat = type->method_map_compatibility.getptr(p_name);
+ if (compat) {
+ if (r_method_exists) {
+ *r_method_exists = true;
+ }
+ for (uint32_t i = 0; i < compat->size(); i++) {
+ if ((*compat)[i]->get_hash() == p_hash) {
+ if (r_is_deprecated) {
+ *r_is_deprecated = true;
+ }
+ return (*compat)[i];
+ }
+ }
+ }
+ type = type->inherits_ptr;
+ }
+ return nullptr;
+}
+
void ClassDB::bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int64_t p_constant, bool p_is_bitfield) {
OBJTYPE_WLOCK;
@@ -1274,11 +1328,30 @@ bool ClassDB::has_method(const StringName &p_class, const StringName &p_method,
}
void ClassDB::bind_method_custom(const StringName &p_class, MethodBind *p_method) {
+ _bind_method_custom(p_class, p_method, false);
+}
+void ClassDB::bind_compatibility_method_custom(const StringName &p_class, MethodBind *p_method) {
+ _bind_method_custom(p_class, p_method, true);
+}
+
+void ClassDB::_bind_compatibility(ClassInfo *type, MethodBind *p_method) {
+ if (!type->method_map_compatibility.has(p_method->get_name())) {
+ type->method_map_compatibility.insert(p_method->get_name(), LocalVector<MethodBind *>());
+ }
+ type->method_map_compatibility[p_method->get_name()].push_back(p_method);
+}
+
+void ClassDB::_bind_method_custom(const StringName &p_class, MethodBind *p_method, bool p_compatibility) {
ClassInfo *type = classes.getptr(p_class);
if (!type) {
ERR_FAIL_MSG("Couldn't bind custom method '" + p_method->get_name() + "' for instance '" + p_class + "'.");
}
+ if (p_compatibility) {
+ _bind_compatibility(type, p_method);
+ return;
+ }
+
if (type->method_map.has(p_method->get_name())) {
// overloading not supported
ERR_FAIL_MSG("Method already bound '" + p_class + "::" + p_method->get_name() + "'.");
@@ -1291,11 +1364,44 @@ void ClassDB::bind_method_custom(const StringName &p_class, MethodBind *p_method
type->method_map[p_method->get_name()] = p_method;
}
+MethodBind *ClassDB::_bind_vararg_method(MethodBind *p_bind, const StringName &p_name, const Vector<Variant> &p_default_args, bool p_compatibility) {
+ MethodBind *bind = p_bind;
+ bind->set_name(p_name);
+ bind->set_default_arguments(p_default_args);
+
+ String instance_type = bind->get_instance_class();
+
+ ClassInfo *type = classes.getptr(instance_type);
+ if (!type) {
+ memdelete(bind);
+ ERR_FAIL_COND_V(!type, nullptr);
+ }
+
+ if (p_compatibility) {
+ _bind_compatibility(type, bind);
+ return bind;
+ }
+
+ if (type->method_map.has(p_name)) {
+ memdelete(bind);
+ // Overloading not supported
+ ERR_FAIL_V_MSG(nullptr, "Method already bound: " + instance_type + "::" + p_name + ".");
+ }
+ type->method_map[p_name] = bind;
+#ifdef DEBUG_METHODS_ENABLED
+ // FIXME: <reduz> set_return_type is no longer in MethodBind, so I guess it should be moved to vararg method bind
+ //bind->set_return_type("Variant");
+ type->method_order.push_back(p_name);
+#endif
+
+ return bind;
+}
+
#ifdef DEBUG_METHODS_ENABLED
-MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount) {
+MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, bool p_compatibility, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount) {
StringName mdname = method_name.name;
#else
-MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const char *method_name, const Variant **p_defs, int p_defcount) {
+MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, bool p_compatibility, const char *method_name, const Variant **p_defs, int p_defcount) {
StringName mdname = StaticCString::create(method_name);
#endif
@@ -1307,7 +1413,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c
#ifdef DEBUG_ENABLED
- ERR_FAIL_COND_V_MSG(has_method(instance_type, mdname), nullptr, "Class " + String(instance_type) + " already has a method " + String(mdname) + ".");
+ ERR_FAIL_COND_V_MSG(!p_compatibility && has_method(instance_type, mdname), nullptr, "Class " + String(instance_type) + " already has a method " + String(mdname) + ".");
#endif
ClassInfo *type = classes.getptr(instance_type);
@@ -1316,7 +1422,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c
ERR_FAIL_V_MSG(nullptr, "Couldn't bind method '" + mdname + "' for instance '" + instance_type + "'.");
}
- if (type->method_map.has(mdname)) {
+ if (!p_compatibility && type->method_map.has(mdname)) {
memdelete(p_bind);
// overloading not supported
ERR_FAIL_V_MSG(nullptr, "Method already bound '" + instance_type + "::" + mdname + "'.");
@@ -1331,10 +1437,16 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c
p_bind->set_argument_names(method_name.args);
- type->method_order.push_back(mdname);
+ if (!p_compatibility) {
+ type->method_order.push_back(mdname);
+ }
#endif
- type->method_map[mdname] = p_bind;
+ if (p_compatibility) {
+ _bind_compatibility(type, p_bind);
+ } else {
+ type->method_map[mdname] = p_bind;
+ }
Vector<Variant> defvals;
@@ -1608,7 +1720,13 @@ void ClassDB::cleanup() {
for (KeyValue<StringName, MethodBind *> &F : ti.method_map) {
memdelete(F.value);
}
+ for (KeyValue<StringName, LocalVector<MethodBind *>> &F : ti.method_map_compatibility) {
+ for (uint32_t i = 0; i < F.value.size(); i++) {
+ memdelete(F.value[i]);
+ }
+ }
}
+
classes.clear();
resource_base_extensions.clear();
compat_classes.clear();
diff --git a/core/object/class_db.h b/core/object/class_db.h
index 1a5e9235cf..ce64336a45 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -105,6 +105,7 @@ public:
ObjectGDExtension *gdextension = nullptr;
HashMap<StringName, MethodBind *> method_map;
+ HashMap<StringName, LocalVector<MethodBind *>> method_map_compatibility;
HashMap<StringName, int64_t> constant_map;
struct EnumInfo {
List<StringName> constants;
@@ -148,9 +149,9 @@ public:
static HashMap<StringName, StringName> compat_classes;
#ifdef DEBUG_METHODS_ENABLED
- static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount);
+ static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, bool p_compatibility, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount);
#else
- static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const char *method_name, const Variant **p_defs, int p_defcount);
+ static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, bool p_compatibility, const char *method_name, const Variant **p_defs, int p_defcount);
#endif
static APIType current_api;
@@ -172,6 +173,9 @@ private:
// Non-locking variants of get_parent_class and is_parent_class.
static StringName _get_parent_class(const StringName &p_class);
static bool _is_parent_class(const StringName &p_class, const StringName &p_inherits);
+ static void _bind_compatibility(ClassInfo *type, MethodBind *p_method);
+ static MethodBind *_bind_vararg_method(MethodBind *p_bind, const StringName &p_name, const Vector<Variant> &p_default_args, bool p_compatibility);
+ static void _bind_method_custom(const StringName &p_class, MethodBind *p_method, bool p_compatibility);
public:
// DO NOT USE THIS!!!!!! NEEDS TO BE PUBLIC BUT DO NOT USE NO MATTER WHAT!!!
@@ -273,7 +277,7 @@ public:
if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
bind->set_return_type_is_raw_object_ptr(true);
}
- return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, false, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
template <class N, class M, typename... VarArgs>
@@ -288,7 +292,36 @@ public:
if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
bind->set_return_type_is_raw_object_ptr(true);
}
- return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, false, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ }
+
+ template <class N, class M, typename... VarArgs>
+ static MethodBind *bind_compatibility_method(N p_method_name, M p_method, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+ MethodBind *bind = create_method_bind(p_method);
+ if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
+ bind->set_return_type_is_raw_object_ptr(true);
+ }
+ return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, true, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ }
+
+ template <class N, class M, typename... VarArgs>
+ static MethodBind *bind_compatibility_static_method(const StringName &p_class, N p_method_name, M p_method, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+ MethodBind *bind = create_static_method_bind(p_method);
+ bind->set_instance_class(p_class);
+ if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
+ bind->set_return_type_is_raw_object_ptr(true);
+ }
+ return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, true, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
template <class M>
@@ -298,36 +331,27 @@ public:
MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant);
ERR_FAIL_COND_V(!bind, nullptr);
- bind->set_name(p_name);
- bind->set_default_arguments(p_default_args);
if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
bind->set_return_type_is_raw_object_ptr(true);
}
+ return _bind_vararg_method(bind, p_name, p_default_args, false);
+ }
- String instance_type = bind->get_instance_class();
+ template <class M>
+ static MethodBind *bind_compatibility_vararg_method(uint32_t p_flags, const StringName &p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const Vector<Variant> &p_default_args = Vector<Variant>(), bool p_return_nil_is_variant = true) {
+ GLOBAL_LOCK_FUNCTION;
- ClassInfo *type = classes.getptr(instance_type);
- if (!type) {
- memdelete(bind);
- ERR_FAIL_COND_V(!type, nullptr);
- }
+ MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant);
+ ERR_FAIL_COND_V(!bind, nullptr);
- if (type->method_map.has(p_name)) {
- memdelete(bind);
- // Overloading not supported
- ERR_FAIL_V_MSG(nullptr, "Method already bound: " + instance_type + "::" + p_name + ".");
+ if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
+ bind->set_return_type_is_raw_object_ptr(true);
}
- type->method_map[p_name] = bind;
-#ifdef DEBUG_METHODS_ENABLED
- // FIXME: <reduz> set_return_type is no longer in MethodBind, so I guess it should be moved to vararg method bind
- //bind->set_return_type("Variant");
- type->method_order.push_back(p_name);
-#endif
-
- return bind;
+ return _bind_vararg_method(bind, p_name, p_default_args, true);
}
static void bind_method_custom(const StringName &p_class, MethodBind *p_method);
+ static void bind_compatibility_method_custom(const StringName &p_class, MethodBind *p_method);
static void add_signal(const StringName &p_class, const MethodInfo &p_signal);
static bool has_signal(const StringName &p_class, const StringName &p_signal, bool p_no_inheritance = false);
@@ -358,6 +382,8 @@ public:
static void get_method_list(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
static bool get_method_info(const StringName &p_class, const StringName &p_method, MethodInfo *r_info, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
static MethodBind *get_method(const StringName &p_class, const StringName &p_name);
+ static MethodBind *get_method_with_compatibility(const StringName &p_class, const StringName &p_name, uint64_t p_hash, bool *r_method_exists = nullptr, bool *r_is_deprecated = nullptr);
+ static Vector<uint32_t> get_method_compatibility_hashes(const StringName &p_class, const StringName &p_name);
static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true, const Vector<String> &p_arg_names = Vector<String>(), bool p_object_core = false);
static void get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false);
diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp
index 55ea5f5ecd..18ba5d5b30 100644
--- a/core/object/message_queue.cpp
+++ b/core/object/message_queue.cpp
@@ -35,10 +35,22 @@
#include "core/object/class_db.h"
#include "core/object/script_language.h"
+#ifdef DEV_ENABLED
+// Includes sanity checks to ensure that a queue set as a thread singleton override
+// is only ever called from the thread it was set for.
+#define LOCK_MUTEX \
+ if (this != MessageQueue::thread_singleton) { \
+ DEV_ASSERT(!this->is_current_thread_override); \
+ mutex.lock(); \
+ } else { \
+ DEV_ASSERT(this->is_current_thread_override); \
+ }
+#else
#define LOCK_MUTEX \
if (this != MessageQueue::thread_singleton) { \
mutex.lock(); \
}
+#endif
#define UNLOCK_MUTEX \
if (this != MessageQueue::thread_singleton) { \
@@ -213,8 +225,8 @@ void CallQueue::_call_function(const Callable &p_callable, const Variant *p_args
Error CallQueue::flush() {
LOCK_MUTEX;
- // Non-main threads are not meant to be flushed, but appended to the main one.
- if (this != MessageQueue::main_singleton) {
+ // Thread overrides are not meant to be flushed, but appended to the main one.
+ if (this == MessageQueue::thread_singleton) {
if (pages.size() == 0) {
return OK;
}
@@ -236,7 +248,8 @@ Error CallQueue::flush() {
uint32_t dst_page = mq->pages_used - 1;
uint32_t dst_offset = mq->page_bytes[dst_page];
if (dst_offset + page_bytes[0] < uint32_t(PAGE_SIZE_BYTES)) {
- memcpy(mq->pages[dst_page] + dst_offset, pages[0], page_bytes[0]);
+ memcpy(mq->pages[dst_page]->data + dst_offset, pages[0]->data, page_bytes[0]);
+ mq->page_bytes[dst_page] += page_bytes[0];
src_page++;
}
}
@@ -253,7 +266,7 @@ Error CallQueue::flush() {
for (; src_page < pages_used; src_page++) {
mq->_add_page();
- memcpy(mq->pages[mq->pages_used - 1], pages[src_page], page_bytes[src_page]);
+ memcpy(mq->pages[mq->pages_used - 1]->data, pages[src_page]->data, page_bytes[src_page]);
mq->page_bytes[mq->pages_used - 1] = page_bytes[src_page];
}
@@ -520,6 +533,10 @@ CallQueue::~CallQueue() {
if (!allocator_is_custom) {
memdelete(allocator);
}
+ // This is done here to avoid a circular dependency between the sanity checks and the thread singleton pointer.
+ if (this == MessageQueue::thread_singleton) {
+ MessageQueue::thread_singleton = nullptr;
+ }
}
//////////////////////
@@ -528,7 +545,18 @@ CallQueue *MessageQueue::main_singleton = nullptr;
thread_local CallQueue *MessageQueue::thread_singleton = nullptr;
void MessageQueue::set_thread_singleton_override(CallQueue *p_thread_singleton) {
+ DEV_ASSERT(p_thread_singleton); // To unset the thread singleton, don't call this with nullptr, but just memfree() it.
+#ifdef DEV_ENABLED
+ if (thread_singleton) {
+ thread_singleton->is_current_thread_override = false;
+ }
+#endif
thread_singleton = p_thread_singleton;
+#ifdef DEV_ENABLED
+ if (thread_singleton) {
+ thread_singleton->is_current_thread_override = true;
+ }
+#endif
}
MessageQueue::MessageQueue() :
diff --git a/core/object/message_queue.h b/core/object/message_queue.h
index c6fcccbd58..9f567e4dd0 100644
--- a/core/object/message_queue.h
+++ b/core/object/message_queue.h
@@ -40,6 +40,8 @@
class Object;
class CallQueue {
+ friend class MessageQueue;
+
public:
enum {
PAGE_SIZE_BYTES = 4096
@@ -75,6 +77,10 @@ private:
uint32_t pages_used = 0;
bool flushing = false;
+#ifdef DEV_ENABLED
+ bool is_current_thread_override = false;
+#endif
+
struct Message {
Callable callable;
int16_t type;
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 5a34328ec4..f276547e7b 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -1350,7 +1350,7 @@ bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable,
}
ERR_FAIL_COND_V_MSG(!s, false, vformat("Disconnecting nonexistent signal '%s' in %s.", p_signal, to_string()));
- ERR_FAIL_COND_V_MSG(!s->slot_map.has(*p_callable.get_base_comparator()), false, "Disconnecting nonexistent signal '" + p_signal + "', callable: " + p_callable + ".");
+ ERR_FAIL_COND_V_MSG(!s->slot_map.has(*p_callable.get_base_comparator()), false, "Attempt to disconnect a nonexistent connection from '" + to_string() + "'. Signal: '" + p_signal + "', callable: '" + p_callable + "'.");
SignalData::Slot *slot = &s->slot_map[*p_callable.get_base_comparator()];
@@ -1739,7 +1739,7 @@ void *Object::get_instance_binding(void *p_token, const GDExtensionInstanceBindi
break;
}
}
- if (unlikely(!binding)) {
+ if (unlikely(!binding && p_callbacks)) {
uint32_t current_size = next_power_of_2(_instance_binding_count);
uint32_t new_size = next_power_of_2(_instance_binding_count + 1);
diff --git a/core/object/object.h b/core/object/object.h
index ae22851c15..6f626b0ed0 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -304,8 +304,10 @@ struct MethodInfo {
// API used to extend in GDExtension and other C compatible compiled languages.
class MethodBind;
+class GDExtension;
struct ObjectGDExtension {
+ GDExtension *library = nullptr;
ObjectGDExtension *parent = nullptr;
List<ObjectGDExtension *> children;
StringName parent_class_name;
@@ -798,6 +800,18 @@ public:
return *_class_name_ptr;
}
+ _FORCE_INLINE_ const StringName &get_class_name_for_extension(const GDExtension *p_library) const {
+ // Only return the class name per the extension if it matches the given p_library.
+ if (_extension && _extension->library == p_library) {
+ return _extension->class_name;
+ }
+ // Otherwise, return the name of the built-in class.
+ if (unlikely(!_class_name_ptr)) {
+ return *_get_class_namev();
+ }
+ return *_class_name_ptr;
+ }
+
/* IAPI */
void set(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr);
@@ -836,14 +850,21 @@ public:
/* SCRIPT */
- void set_script(const Variant &p_script);
- Variant get_script() const;
+// When in debug, some non-virtual functions can be overridden for multithreaded guards.
+#ifdef DEBUG_ENABLED
+#define MTVIRTUAL virtual
+#else
+#define MTVIRTUAL
+#endif
+
+ MTVIRTUAL void set_script(const Variant &p_script);
+ MTVIRTUAL Variant get_script() const;
- bool has_meta(const StringName &p_name) const;
- void set_meta(const StringName &p_name, const Variant &p_value);
- void remove_meta(const StringName &p_name);
- Variant get_meta(const StringName &p_name, const Variant &p_default = Variant()) const;
- void get_meta_list(List<StringName> *p_list) const;
+ MTVIRTUAL bool has_meta(const StringName &p_name) const;
+ MTVIRTUAL void set_meta(const StringName &p_name, const Variant &p_value);
+ MTVIRTUAL void remove_meta(const StringName &p_name);
+ MTVIRTUAL Variant get_meta(const StringName &p_name, const Variant &p_default = Variant()) const;
+ MTVIRTUAL void get_meta_list(List<StringName> *p_list) const;
#ifdef TOOLS_ENABLED
void set_edited(bool p_edited);
@@ -870,17 +891,17 @@ public:
return emit_signalp(p_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
- Error emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount);
- bool has_signal(const StringName &p_name) const;
- void get_signal_list(List<MethodInfo> *p_signals) const;
- void get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const;
- void get_all_signal_connections(List<Connection> *p_connections) const;
- int get_persistent_signal_connection_count() const;
- void get_signals_connected_to_this(List<Connection> *p_connections) const;
+ MTVIRTUAL Error emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount);
+ MTVIRTUAL bool has_signal(const StringName &p_name) const;
+ MTVIRTUAL void get_signal_list(List<MethodInfo> *p_signals) const;
+ MTVIRTUAL void get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const;
+ MTVIRTUAL void get_all_signal_connections(List<Connection> *p_connections) const;
+ MTVIRTUAL int get_persistent_signal_connection_count() const;
+ MTVIRTUAL void get_signals_connected_to_this(List<Connection> *p_connections) const;
- Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0);
- void disconnect(const StringName &p_signal, const Callable &p_callable);
- bool is_connected(const StringName &p_signal, const Callable &p_callable) const;
+ MTVIRTUAL Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0);
+ MTVIRTUAL void disconnect(const StringName &p_signal, const Callable &p_callable);
+ MTVIRTUAL bool is_connected(const StringName &p_signal, const Callable &p_callable) const;
template <typename... VarArgs>
void call_deferred(const StringName &p_name, VarArgs... p_args) {
diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h
index cf0bd47bce..58706fb9a9 100644
--- a/core/object/ref_counted.h
+++ b/core/object/ref_counted.h
@@ -97,26 +97,15 @@ public:
return reference != p_r.reference;
}
- _FORCE_INLINE_ T *operator->() {
+ _FORCE_INLINE_ T *operator*() const {
return reference;
}
- _FORCE_INLINE_ T *operator*() {
+ _FORCE_INLINE_ T *operator->() const {
return reference;
}
- _FORCE_INLINE_ const T *operator->() const {
- return reference;
- }
-
- _FORCE_INLINE_ const T *ptr() const {
- return reference;
- }
- _FORCE_INLINE_ T *ptr() {
- return reference;
- }
-
- _FORCE_INLINE_ const T *operator*() const {
+ _FORCE_INLINE_ T *ptr() const {
return reference;
}
diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp
index e59ab3d6ae..afe6ecd1b3 100644
--- a/core/object/worker_thread_pool.cpp
+++ b/core/object/worker_thread_pool.cpp
@@ -51,6 +51,23 @@ void WorkerThreadPool::_process_task_queue() {
void WorkerThreadPool::_process_task(Task *p_task) {
bool low_priority = p_task->low_priority;
+ int pool_thread_index = -1;
+ Task *prev_low_prio_task = nullptr; // In case this is recursively called.
+
+ if (!use_native_low_priority_threads) {
+ pool_thread_index = thread_ids[Thread::get_caller_id()];
+ ThreadData &curr_thread = threads[pool_thread_index];
+ task_mutex.lock();
+ p_task->pool_thread_index = pool_thread_index;
+ if (low_priority) {
+ low_priority_tasks_running++;
+ prev_low_prio_task = curr_thread.current_low_prio_task;
+ curr_thread.current_low_prio_task = p_task;
+ } else {
+ curr_thread.current_low_prio_task = nullptr;
+ }
+ task_mutex.unlock();
+ }
if (p_task->group) {
// Handling a group
@@ -126,21 +143,36 @@ void WorkerThreadPool::_process_task(Task *p_task) {
p_task->callable.callp(nullptr, 0, ret, ce);
}
+ task_mutex.lock();
p_task->completed = true;
- p_task->done_semaphore.post();
+ for (uint8_t i = 0; i < p_task->waiting; i++) {
+ p_task->done_semaphore.post();
+ }
+ if (!use_native_low_priority_threads) {
+ p_task->pool_thread_index = -1;
+ }
+ task_mutex.unlock(); // Keep mutex down to here since on unlock the task may be freed.
}
- if (!use_native_low_priority_threads && low_priority) {
- // A low prioriry task was freed, so see if we can move a pending one to the high priority queue.
+ // Task may have been freed by now (all callers notified).
+ p_task = nullptr;
+
+ if (!use_native_low_priority_threads) {
bool post = false;
task_mutex.lock();
- if (low_priority_task_queue.first()) {
- Task *low_prio_task = low_priority_task_queue.first()->self();
- low_priority_task_queue.remove(low_priority_task_queue.first());
- task_queue.add_last(&low_prio_task->task_elem);
- post = true;
- } else {
+ ThreadData &curr_thread = threads[pool_thread_index];
+ curr_thread.current_low_prio_task = prev_low_prio_task;
+ if (low_priority) {
low_priority_threads_used--;
+ low_priority_tasks_running--;
+ // A low prioriry task was freed, so see if we can move a pending one to the high priority queue.
+ if (_try_promote_low_priority_task()) {
+ post = true;
+ }
+
+ if (low_priority_tasks_awaiting_others == low_priority_tasks_running) {
+ _prevent_low_prio_saturation_deadlock();
+ }
}
task_mutex.unlock();
if (post) {
@@ -198,6 +230,35 @@ void WorkerThreadPool::_post_task(Task *p_task, bool p_high_priority) {
}
}
+bool WorkerThreadPool::_try_promote_low_priority_task() {
+ if (low_priority_task_queue.first()) {
+ Task *low_prio_task = low_priority_task_queue.first()->self();
+ low_priority_task_queue.remove(low_priority_task_queue.first());
+ task_queue.add_last(&low_prio_task->task_elem);
+ low_priority_threads_used++;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void WorkerThreadPool::_prevent_low_prio_saturation_deadlock() {
+ if (low_priority_tasks_awaiting_others == low_priority_tasks_running) {
+#ifdef DEV_ENABLED
+ print_verbose("WorkerThreadPool: Low-prio slots saturated with tasks all waiting for other low-prio tasks. Attempting to avoid deadlock by scheduling one extra task.");
+#endif
+ // In order not to create dependency cycles, we can only schedule the next one.
+ // We'll keep doing the same until the deadlock is broken,
+ SelfList<Task> *to_promote = low_priority_task_queue.first();
+ if (to_promote) {
+ low_priority_task_queue.remove(to_promote);
+ task_queue.add_last(to_promote);
+ low_priority_threads_used++;
+ task_available_semaphore.post();
+ }
+ }
+}
+
WorkerThreadPool::TaskID WorkerThreadPool::add_native_task(void (*p_func)(void *), void *p_userdata, bool p_high_priority, const String &p_description) {
return _add_task(Callable(), p_func, p_userdata, nullptr, p_high_priority, p_description);
}
@@ -238,66 +299,113 @@ bool WorkerThreadPool::is_task_completed(TaskID p_task_id) const {
return completed;
}
-void WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) {
+Error WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) {
task_mutex.lock();
Task **taskp = tasks.getptr(p_task_id);
if (!taskp) {
task_mutex.unlock();
- ERR_FAIL_MSG("Invalid Task ID"); // Invalid task
+ ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Invalid Task ID"); // Invalid task
}
Task *task = *taskp;
- if (task->waiting) {
- String description = task->description;
- task_mutex.unlock();
- if (description.is_empty()) {
- ERR_FAIL_MSG("Another thread is waiting on this task: " + itos(p_task_id)); // Invalid task
- } else {
- ERR_FAIL_MSG("Another thread is waiting on this task: " + description + " (" + itos(p_task_id) + ")"); // Invalid task
+ if (!task->completed) {
+ if (!use_native_low_priority_threads && task->pool_thread_index != -1) { // Otherwise, it's not running yet.
+ int caller_pool_th_index = thread_ids.has(Thread::get_caller_id()) ? thread_ids[Thread::get_caller_id()] : -1;
+ if (caller_pool_th_index == task->pool_thread_index) {
+ // Deadlock prevention.
+ // Waiting for a task run on this same thread? That means the task to be awaited started waiting as well
+ // and another task was run to make use of the thread in the meantime, with enough bad luck as to
+ // the need to wait for the original task arose in turn.
+ // In other words, the task we want to wait for is buried in the stack.
+ // Let's report the caller about the issue to it handles as it sees fit.
+ task_mutex.unlock();
+ return ERR_BUSY;
+ }
}
- }
-
- task->waiting = true;
- task_mutex.unlock();
+ task->waiting++;
+
+ bool is_low_prio_waiting_for_another = false;
+ if (!use_native_low_priority_threads) {
+ // Deadlock prevention:
+ // If all low-prio tasks are waiting for other low-prio tasks and there are no more free low-prio slots,
+ // we have a no progressable situation. We can apply a workaround, consisting in promoting an awaited queued
+ // low-prio task to the schedule queue so it can run and break the "impasse".
+ // NOTE: A similar reasoning could be made about high priority tasks, but there are usually much more
+ // than low-prio. Therefore, a deadlock there would only happen when dealing with a very complex task graph
+ // or when there are too few worker threads (limited platforms or exotic settings). If that turns out to be
+ // an issue in the real world, a further fix can be applied against that.
+ if (task->low_priority) {
+ bool awaiter_is_a_low_prio_task = thread_ids.has(Thread::get_caller_id()) && threads[thread_ids[Thread::get_caller_id()]].current_low_prio_task;
+ if (awaiter_is_a_low_prio_task) {
+ is_low_prio_waiting_for_another = true;
+ low_priority_tasks_awaiting_others++;
+ if (low_priority_tasks_awaiting_others == low_priority_tasks_running) {
+ _prevent_low_prio_saturation_deadlock();
+ }
+ }
+ }
+ }
- if (use_native_low_priority_threads && task->low_priority) {
- task->low_priority_thread->wait_to_finish();
+ task_mutex.unlock();
- task_mutex.lock();
- native_thread_allocator.free(task->low_priority_thread);
- } else {
- int *index = thread_ids.getptr(Thread::get_caller_id());
-
- if (index) {
- // We are an actual process thread, we must not be blocked so continue processing stuff if available.
- bool must_exit = false;
- while (true) {
- if (task->done_semaphore.try_wait()) {
- // If done, exit
- break;
- }
- if (!must_exit && task_available_semaphore.try_wait()) {
- if (exit_threads) {
- must_exit = true;
- } else {
- // Solve tasks while they are around.
- _process_task_queue();
- continue;
+ if (use_native_low_priority_threads && task->low_priority) {
+ task->done_semaphore.wait();
+ } else {
+ bool current_is_pool_thread = thread_ids.has(Thread::get_caller_id());
+ if (current_is_pool_thread) {
+ // We are an actual process thread, we must not be blocked so continue processing stuff if available.
+ bool must_exit = false;
+ while (true) {
+ if (task->done_semaphore.try_wait()) {
+ // If done, exit
+ break;
}
+ if (!must_exit) {
+ if (task_available_semaphore.try_wait()) {
+ if (exit_threads) {
+ must_exit = true;
+ } else {
+ // Solve tasks while they are around.
+ _process_task_queue();
+ continue;
+ }
+ } else if (!use_native_low_priority_threads && task->low_priority) {
+ // A low prioriry task started waiting, so see if we can move a pending one to the high priority queue.
+ task_mutex.lock();
+ bool post = _try_promote_low_priority_task();
+ task_mutex.unlock();
+ if (post) {
+ task_available_semaphore.post();
+ }
+ }
+ }
+ OS::get_singleton()->delay_usec(1); // Microsleep, this could be converted to waiting for multiple objects in supported platforms for a bit more performance.
}
- OS::get_singleton()->delay_usec(1); // Microsleep, this could be converted to waiting for multiple objects in supported platforms for a bit more performance.
+ } else {
+ task->done_semaphore.wait();
}
- } else {
- task->done_semaphore.wait();
}
task_mutex.lock();
+ if (is_low_prio_waiting_for_another) {
+ low_priority_tasks_awaiting_others--;
+ }
+
+ task->waiting--;
+ }
+
+ if (task->waiting == 0) {
+ if (use_native_low_priority_threads && task->low_priority) {
+ task->low_priority_thread->wait_to_finish();
+ native_thread_allocator.free(task->low_priority_thread);
+ }
+ tasks.erase(p_task_id);
+ task_allocator.free(task);
}
- tasks.erase(p_task_id);
- task_allocator.free(task);
task_mutex.unlock();
+ return OK;
}
WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) {
@@ -429,7 +537,7 @@ void WorkerThreadPool::init(int p_thread_count, bool p_use_native_threads_low_pr
if (p_use_native_threads_low_priority) {
max_low_priority_threads = 0;
} else {
- max_low_priority_threads = CLAMP(p_thread_count * p_low_priority_task_ratio, 1, p_thread_count);
+ max_low_priority_threads = CLAMP(p_thread_count * p_low_priority_task_ratio, 1, p_thread_count - 1);
}
use_native_low_priority_threads = p_use_native_threads_low_priority;
diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h
index d47c6ad714..d4d9387765 100644
--- a/core/object/worker_thread_pool.h
+++ b/core/object/worker_thread_pool.h
@@ -81,10 +81,11 @@ private:
bool completed = false;
Group *group = nullptr;
SelfList<Task> task_elem;
- bool waiting = false; // Waiting for completion
+ uint32_t waiting = 0;
bool low_priority = false;
BaseTemplateUserdata *template_userdata = nullptr;
Thread *low_priority_thread = nullptr;
+ int pool_thread_index = -1;
void free_template_userdata();
Task() :
@@ -104,6 +105,7 @@ private:
struct ThreadData {
uint32_t index;
Thread thread;
+ Task *current_low_prio_task = nullptr;
};
TightLocalVector<ThreadData> threads;
@@ -116,6 +118,8 @@ private:
bool use_native_low_priority_threads = false;
uint32_t max_low_priority_threads = 0;
uint32_t low_priority_threads_used = 0;
+ uint32_t low_priority_tasks_running = 0;
+ uint32_t low_priority_tasks_awaiting_others = 0;
uint64_t last_task = 1;
@@ -127,6 +131,9 @@ private:
void _post_task(Task *p_task, bool p_high_priority);
+ bool _try_promote_low_priority_task();
+ void _prevent_low_prio_saturation_deadlock();
+
static WorkerThreadPool *singleton;
TaskID _add_task(const Callable &p_callable, void (*p_func)(void *), void *p_userdata, BaseTemplateUserdata *p_template_userdata, bool p_high_priority, const String &p_description);
@@ -169,7 +176,7 @@ public:
TaskID add_task(const Callable &p_action, bool p_high_priority = false, const String &p_description = String());
bool is_task_completed(TaskID p_task_id) const;
- void wait_for_task_completion(TaskID p_task_id);
+ Error wait_for_task_completion(TaskID p_task_id);
template <class C, class M, class U>
GroupID add_template_group_task(C *p_instance, M p_method, U p_userdata, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String()) {
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 4123a1d602..5704ef7a40 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -34,6 +34,7 @@
#include "core/input/input.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
+#include "core/io/json.h"
#include "core/os/midi_driver.h"
#include "core/version_generated.gen.h"
@@ -151,6 +152,14 @@ int OS::get_low_processor_usage_mode_sleep_usec() const {
return low_processor_usage_mode_sleep_usec;
}
+void OS::set_delta_smoothing(bool p_enabled) {
+ _delta_smoothing_enabled = p_enabled;
+}
+
+bool OS::is_delta_smoothing_enabled() const {
+ return _delta_smoothing_enabled;
+}
+
String OS::get_executable_path() const {
return _execpath;
}
@@ -581,6 +590,59 @@ OS::PreferredTextureFormat OS::get_preferred_texture_format() const {
#endif
}
+void OS::set_use_benchmark(bool p_use_benchmark) {
+ use_benchmark = p_use_benchmark;
+}
+
+bool OS::is_use_benchmark_set() {
+ return use_benchmark;
+}
+
+void OS::set_benchmark_file(const String &p_benchmark_file) {
+ benchmark_file = p_benchmark_file;
+}
+
+String OS::get_benchmark_file() {
+ return benchmark_file;
+}
+
+void OS::benchmark_begin_measure(const String &p_what) {
+#ifdef TOOLS_ENABLED
+ start_benchmark_from[p_what] = OS::get_singleton()->get_ticks_usec();
+#endif
+}
+void OS::benchmark_end_measure(const String &p_what) {
+#ifdef TOOLS_ENABLED
+ uint64_t total = OS::get_singleton()->get_ticks_usec() - start_benchmark_from[p_what];
+ double total_f = double(total) / double(1000000);
+
+ startup_benchmark_json[p_what] = total_f;
+#endif
+}
+
+void OS::benchmark_dump() {
+#ifdef TOOLS_ENABLED
+ if (!use_benchmark) {
+ return;
+ }
+ if (!benchmark_file.is_empty()) {
+ Ref<FileAccess> f = FileAccess::open(benchmark_file, FileAccess::WRITE);
+ if (f.is_valid()) {
+ Ref<JSON> json;
+ json.instantiate();
+ f->store_string(json->stringify(startup_benchmark_json, "\t", false, true));
+ }
+ } else {
+ List<Variant> keys;
+ startup_benchmark_json.get_key_list(&keys);
+ print_line("BENCHMARK:");
+ for (const Variant &K : keys) {
+ print_line("\t-", K, ": ", startup_benchmark_json[K], +" sec.");
+ }
+ }
+#endif
+}
+
OS::OS() {
singleton = this;
diff --git a/core/os/os.h b/core/os/os.h
index 1652c1ed90..f2787d6381 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -52,6 +52,7 @@ class OS {
bool _keep_screen_on = true; // set default value to true, because this had been true before godot 2.0.
bool low_processor_usage_mode = false;
int low_processor_usage_mode_sleep_usec = 10000;
+ bool _delta_smoothing_enabled = false;
bool _verbose_stdout = false;
bool _debug_stdout = false;
String _local_clipboard;
@@ -75,6 +76,12 @@ class OS {
RemoteFilesystemClient default_rfs;
+ // For tracking benchmark data
+ bool use_benchmark = false;
+ String benchmark_file;
+ HashMap<String, uint64_t> start_benchmark_from;
+ Dictionary startup_benchmark_json;
+
protected:
void _set_logger(CompositeLogger *p_logger);
@@ -154,6 +161,9 @@ public:
virtual void set_low_processor_usage_mode_sleep_usec(int p_usec);
virtual int get_low_processor_usage_mode_sleep_usec() const;
+ void set_delta_smoothing(bool p_enabled);
+ bool is_delta_smoothing_enabled() const;
+
virtual Vector<String> get_system_fonts() const { return Vector<String>(); };
virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const { return String(); };
virtual Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const { return Vector<String>(); };
@@ -295,6 +305,15 @@ public:
virtual bool request_permissions() { return true; }
virtual Vector<String> get_granted_permissions() const { return Vector<String>(); }
+ // For recording / measuring benchmark data. Only enabled with tools
+ void set_use_benchmark(bool p_use_benchmark);
+ bool is_use_benchmark_set();
+ void set_benchmark_file(const String &p_benchmark_file);
+ String get_benchmark_file();
+ virtual void benchmark_begin_measure(const String &p_what);
+ virtual void benchmark_end_measure(const String &p_what);
+ virtual void benchmark_dump();
+
virtual void process_and_drop_events() {}
virtual Error setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path);
diff --git a/core/os/semaphore.h b/core/os/semaphore.h
index a992a4587d..66dfb3ee02 100644
--- a/core/os/semaphore.h
+++ b/core/os/semaphore.h
@@ -33,6 +33,9 @@
#include "core/error/error_list.h"
#include "core/typedefs.h"
+#ifdef DEBUG_ENABLED
+#include "core/error/error_macros.h"
+#endif
#include <condition_variable>
#include <mutex>
@@ -42,6 +45,9 @@ private:
mutable std::mutex mutex;
mutable std::condition_variable condition;
mutable uint32_t count = 0; // Initialized as locked.
+#ifdef DEBUG_ENABLED
+ mutable uint32_t awaiters = 0;
+#endif
public:
_ALWAYS_INLINE_ void post() const {
@@ -52,10 +58,16 @@ public:
_ALWAYS_INLINE_ void wait() const {
std::unique_lock lock(mutex);
+#ifdef DEBUG_ENABLED
+ ++awaiters;
+#endif
while (!count) { // Handle spurious wake-ups.
condition.wait(lock);
}
- count--;
+ --count;
+#ifdef DEBUG_ENABLED
+ --awaiters;
+#endif
}
_ALWAYS_INLINE_ bool try_wait() const {
@@ -67,6 +79,47 @@ public:
return false;
}
}
+
+#ifdef DEBUG_ENABLED
+ ~Semaphore() {
+ // Destroying an std::condition_variable when not all threads waiting on it have been notified
+ // invokes undefined behavior (e.g., it may be nicely destroyed or it may be awaited forever.)
+ // That means other threads could still be running the body of std::condition_variable::wait()
+ // but already past the safety checkpoint. That's the case for instance if that function is already
+ // waiting to lock again.
+ //
+ // We will make the rule a bit more restrictive and simpler to understand at the same time: there
+ // should not be any threads at any stage of the waiting by the time the semaphore is destroyed.
+ //
+ // We do so because of the following reasons:
+ // - We have the guideline that threads must be awaited (i.e., completed), so the waiting thread
+ // must be completely done by the time the thread controlling it finally destroys the semaphore.
+ // Therefore, only a coding mistake could make the program run into such a attempt at premature
+ // destruction of the semaphore.
+ // - In scripting, given that Semaphores are wrapped by RefCounted classes, in general it can't
+ // happen that a thread is trying to destroy a Semaphore while another is still doing whatever with
+ // it, so the simplification is mostly transparent to script writers.
+ // - The redefined rule can be checked for failure to meet it, which is what this implementation does.
+ // This is useful to detect a few cases of potential misuse; namely:
+ // a) In scripting:
+ // * The coder is naughtily dealing with the reference count causing a semaphore to die prematurely.
+ // * The coder is letting the project reach its termination without having cleanly finished threads
+ // that await on semaphores (or at least, let the usual semaphore-controlled loop exit).
+ // b) In the native side, where Semaphore is not a ref-counted beast and certain coding mistakes can
+ // lead to its premature destruction as well.
+ //
+ // Let's let users know they are doing it wrong, but apply a, somewhat hacky, countermeasure against UB
+ // in debug builds.
+ std::lock_guard lock(mutex);
+ if (awaiters) {
+ WARN_PRINT(
+ "A Semaphore object is being destroyed while one or more threads are still waiting on it.\n"
+ "Please call post() on it as necessary to prevent such a situation and so ensure correct cleanup.");
+ // And now, the hacky countermeasure (i.e., leak the condition variable).
+ new (&condition) std::condition_variable();
+ }
+ }
+#endif
};
#endif // SEMAPHORE_H
diff --git a/core/os/thread.cpp b/core/os/thread.cpp
index c067ad1a6a..03e2c5409d 100644
--- a/core/os/thread.cpp
+++ b/core/os/thread.cpp
@@ -101,7 +101,9 @@ Thread::Thread() {
Thread::~Thread() {
if (id != UNASSIGNED_ID) {
#ifdef DEBUG_ENABLED
- WARN_PRINT("A Thread object has been destroyed without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread.");
+ WARN_PRINT(
+ "A Thread object is being destroyed without its completion having been realized.\n"
+ "Please call wait_to_finish() on it to ensure correct cleanup.");
#endif
thread.detach();
}
diff --git a/core/os/thread_safe.cpp b/core/os/thread_safe.cpp
new file mode 100644
index 0000000000..96b7de8ed2
--- /dev/null
+++ b/core/os/thread_safe.cpp
@@ -0,0 +1,46 @@
+/**************************************************************************/
+/* thread_safe.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_SAFE_CPP
+#define THREAD_SAFE_CPP
+
+#include "thread_safe.h"
+
+static thread_local bool current_thread_safe_for_nodes = false;
+
+bool is_current_thread_safe_for_nodes() {
+ return current_thread_safe_for_nodes;
+}
+
+void set_current_thread_safe_for_nodes(bool p_safe) {
+ current_thread_safe_for_nodes = p_safe;
+}
+
+#endif // THREAD_SAFE_CPP
diff --git a/core/os/thread_safe.h b/core/os/thread_safe.h
index ac8734b6c1..042a0b7d98 100644
--- a/core/os/thread_safe.h
+++ b/core/os/thread_safe.h
@@ -38,4 +38,7 @@
#define _THREAD_SAFE_LOCK_ _thread_safe_.lock();
#define _THREAD_SAFE_UNLOCK_ _thread_safe_.unlock();
+bool is_current_thread_safe_for_nodes();
+void set_current_thread_safe_for_nodes(bool p_safe);
+
#endif // THREAD_SAFE_H
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index b8b8119618..e3f69fa9e1 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -120,6 +120,7 @@ static ResourceUID *resource_uid = nullptr;
static bool _is_core_extensions_registered = false;
void register_core_types() {
+ OS::get_singleton()->benchmark_begin_measure("register_core_types");
//consistency check
static_assert(sizeof(Callable) <= 16);
@@ -294,6 +295,8 @@ void register_core_types() {
GDREGISTER_NATIVE_STRUCT(ScriptLanguageExtensionProfilingInfo, "StringName signature;uint64_t call_count;uint64_t total_time;uint64_t self_time");
worker_thread_pool = memnew(WorkerThreadPool);
+
+ OS::get_singleton()->benchmark_end_measure("register_core_types");
}
void register_core_settings() {
@@ -360,6 +363,8 @@ void unregister_core_extensions() {
}
void unregister_core_types() {
+ OS::get_singleton()->benchmark_begin_measure("unregister_core_types");
+
memdelete(gdextension_manager);
memdelete(resource_uid);
@@ -425,4 +430,6 @@ void unregister_core_types() {
ResourceCache::clear();
CoreStringNames::free();
StringName::cleanup();
+
+ OS::get_singleton()->benchmark_end_measure("unregister_core_types");
}
diff --git a/core/string/string_name.h b/core/string/string_name.h
index 07abc781a2..4ed58d8286 100644
--- a/core/string/string_name.h
+++ b/core/string/string_name.h
@@ -117,6 +117,15 @@ public:
_FORCE_INLINE_ bool operator<(const StringName &p_name) const {
return _data < p_name._data;
}
+ _FORCE_INLINE_ bool operator<=(const StringName &p_name) const {
+ return _data <= p_name._data;
+ }
+ _FORCE_INLINE_ bool operator>(const StringName &p_name) const {
+ return _data > p_name._data;
+ }
+ _FORCE_INLINE_ bool operator>=(const StringName &p_name) const {
+ return _data >= p_name._data;
+ }
_FORCE_INLINE_ bool operator==(const StringName &p_name) const {
// the real magic of all this mess happens here.
// this is why path comparisons are very fast
diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h
index 58ed019287..bfc9f6fc9a 100644
--- a/core/templates/safe_refcount.h
+++ b/core/templates/safe_refcount.h
@@ -50,11 +50,14 @@
// value and, as an important benefit, you can be sure the value is properly synchronized
// even with threads that are already running.
-// This is used in very specific areas of the engine where it's critical that these guarantees are held
+// These are used in very specific areas of the engine where it's critical that these guarantees are held
#define SAFE_NUMERIC_TYPE_PUN_GUARANTEES(m_type) \
static_assert(sizeof(SafeNumeric<m_type>) == sizeof(m_type)); \
static_assert(alignof(SafeNumeric<m_type>) == alignof(m_type)); \
static_assert(std::is_trivially_destructible<std::atomic<m_type>>::value);
+#define SAFE_FLAG_TYPE_PUN_GUARANTEES \
+ static_assert(sizeof(SafeFlag) == sizeof(bool)); \
+ static_assert(alignof(SafeFlag) == alignof(bool));
template <class T>
class SafeNumeric {
@@ -102,6 +105,17 @@ public:
return value.fetch_sub(p_value, std::memory_order_acq_rel) - p_value;
}
+ _ALWAYS_INLINE_ T bit_or(T p_value) {
+ return value.fetch_or(p_value, std::memory_order_acq_rel);
+ }
+ _ALWAYS_INLINE_ T bit_and(T p_value) {
+ return value.fetch_and(p_value, std::memory_order_acq_rel);
+ }
+
+ _ALWAYS_INLINE_ T bit_xor(T p_value) {
+ return value.fetch_xor(p_value, std::memory_order_acq_rel);
+ }
+
// Returns the original value instead of the new one
_ALWAYS_INLINE_ T postsub(T p_value) {
return value.fetch_sub(p_value, std::memory_order_acq_rel);
diff --git a/core/templates/self_list.h b/core/templates/self_list.h
index c3d7391d6c..ff6fa953ae 100644
--- a/core/templates/self_list.h
+++ b/core/templates/self_list.h
@@ -99,11 +99,20 @@ public:
p_elem->_root = nullptr;
}
+ void clear() {
+ while (_first) {
+ remove(_first);
+ }
+ }
+
_FORCE_INLINE_ SelfList<T> *first() { return _first; }
_FORCE_INLINE_ const SelfList<T> *first() const { return _first; }
_FORCE_INLINE_ List() {}
- _FORCE_INLINE_ ~List() { ERR_FAIL_COND(_first != nullptr); }
+ _FORCE_INLINE_ ~List() {
+ // A self list must be empty on destruction.
+ DEV_ASSERT(_first == nullptr);
+ }
};
private:
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 0a836c125a..dad9183216 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -2101,7 +2101,7 @@ static void _register_variant_builtin_methods() {
bind_method(Basis, is_equal_approx, sarray("b"), varray());
bind_method(Basis, is_finite, sarray(), varray());
bind_method(Basis, get_rotation_quaternion, sarray(), varray());
- bind_static_method(Basis, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0)));
+ bind_static_method(Basis, looking_at, sarray("target", "up", "use_model_front"), varray(Vector3(0, 1, 0), false));
bind_static_method(Basis, from_scale, sarray("scale"), varray());
bind_static_method(Basis, from_euler, sarray("euler", "order"), varray((int64_t)EulerOrder::YXZ));
@@ -2144,7 +2144,7 @@ static void _register_variant_builtin_methods() {
bind_method(Transform3D, scaled_local, sarray("scale"), varray());
bind_method(Transform3D, translated, sarray("offset"), varray());
bind_method(Transform3D, translated_local, sarray("offset"), varray());
- bind_method(Transform3D, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0)));
+ bind_method(Transform3D, looking_at, sarray("target", "up", "use_model_front"), varray(Vector3(0, 1, 0), false));
bind_method(Transform3D, interpolate_with, sarray("xform", "weight"), varray());
bind_method(Transform3D, is_equal_approx, sarray("xform"), varray());
bind_method(Transform3D, is_finite, sarray(), varray());
@@ -2532,6 +2532,13 @@ static void _register_variant_builtin_methods() {
_VariantCall::add_variant_constant(Variant::VECTOR3, "FORWARD", Vector3(0, 0, -1));
_VariantCall::add_variant_constant(Variant::VECTOR3, "BACK", Vector3(0, 0, 1));
+ _VariantCall::add_variant_constant(Variant::VECTOR3, "MODEL_LEFT", Vector3(1, 0, 0));
+ _VariantCall::add_variant_constant(Variant::VECTOR3, "MODEL_RIGHT", Vector3(-1, 0, 0));
+ _VariantCall::add_variant_constant(Variant::VECTOR3, "MODEL_TOP", Vector3(0, 1, 0));
+ _VariantCall::add_variant_constant(Variant::VECTOR3, "MODEL_BOTTOM", Vector3(0, -1, 0));
+ _VariantCall::add_variant_constant(Variant::VECTOR3, "MODEL_FRONT", Vector3(0, 0, 1));
+ _VariantCall::add_variant_constant(Variant::VECTOR3, "MODEL_REAR", Vector3(0, 0, -1));
+
_VariantCall::add_constant(Variant::VECTOR4, "AXIS_X", Vector4::AXIS_X);
_VariantCall::add_constant(Variant::VECTOR4, "AXIS_Y", Vector4::AXIS_Y);
_VariantCall::add_constant(Variant::VECTOR4, "AXIS_Z", Vector4::AXIS_Z);
diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h
index b7bd2a9c8c..8013c1a32a 100644
--- a/core/variant/variant_internal.h
+++ b/core/variant/variant_internal.h
@@ -1534,14 +1534,14 @@ struct VariantTypeAdjust<Object *> {
template <class T>
struct VariantTypeConstructor {
- _FORCE_INLINE_ static void variant_from_type(void *p_variant, void *p_value) {
- Variant *variant = reinterpret_cast<Variant *>(p_variant);
- VariantInitializer<T>::init(variant);
- VariantInternalAccessor<T>::set(variant, *((T *)p_value));
+ _FORCE_INLINE_ static void variant_from_type(void *r_variant, void *p_value) {
+ // r_variant is provided by caller as uninitialized memory
+ memnew_placement(r_variant, Variant(*((T *)p_value)));
}
- _FORCE_INLINE_ static void type_from_variant(void *p_value, void *p_variant) {
- *((T *)p_value) = VariantInternalAccessor<T>::get(reinterpret_cast<Variant *>(p_variant));
+ _FORCE_INLINE_ static void type_from_variant(void *r_value, void *p_variant) {
+ // r_value is provided by caller as uninitialized memory
+ memnew_placement(r_value, T(VariantInternalAccessor<T>::get(reinterpret_cast<Variant *>(p_variant))));
}
};