summaryrefslogtreecommitdiff
path: root/core/object
diff options
context:
space:
mode:
Diffstat (limited to 'core/object')
-rw-r--r--core/object/SCsub7
-rw-r--r--core/object/callable_method_pointer.cpp93
-rw-r--r--core/object/callable_method_pointer.h244
-rw-r--r--core/object/class_db.cpp1571
-rw-r--r--core/object/class_db.h427
-rw-r--r--core/object/message_queue.cpp374
-rw-r--r--core/object/message_queue.h98
-rw-r--r--core/object/method_bind.cpp110
-rw-r--r--core/object/method_bind.h591
-rw-r--r--core/object/object.cpp1984
-rw-r--r--core/object/object.h815
-rw-r--r--core/object/object_id.h63
-rw-r--r--core/object/reference.cpp132
-rw-r--r--core/object/reference.h301
-rw-r--r--core/object/script_language.cpp598
-rw-r--r--core/object/script_language.h460
-rw-r--r--core/object/undo_redo.cpp520
-rw-r--r--core/object/undo_redo.h135
18 files changed, 8523 insertions, 0 deletions
diff --git a/core/object/SCsub b/core/object/SCsub
new file mode 100644
index 0000000000..5d429960e5
--- /dev/null
+++ b/core/object/SCsub
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env_object = env.Clone()
+
+env_object.add_source_files(env.core_sources, "*.cpp")
diff --git a/core/object/callable_method_pointer.cpp b/core/object/callable_method_pointer.cpp
new file mode 100644
index 0000000000..21a917cbd7
--- /dev/null
+++ b/core/object/callable_method_pointer.cpp
@@ -0,0 +1,93 @@
+/*************************************************************************/
+/* callable_method_pointer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "callable_method_pointer.h"
+
+bool CallableCustomMethodPointerBase::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomMethodPointerBase *a = static_cast<const CallableCustomMethodPointerBase *>(p_a);
+ const CallableCustomMethodPointerBase *b = static_cast<const CallableCustomMethodPointerBase *>(p_b);
+
+ if (a->comp_size != b->comp_size) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < a->comp_size; i++) {
+ if (a->comp_ptr[i] != b->comp_ptr[i]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool CallableCustomMethodPointerBase::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomMethodPointerBase *a = static_cast<const CallableCustomMethodPointerBase *>(p_a);
+ const CallableCustomMethodPointerBase *b = static_cast<const CallableCustomMethodPointerBase *>(p_b);
+
+ if (a->comp_size != b->comp_size) {
+ return a->comp_size < b->comp_size;
+ }
+
+ for (uint32_t i = 0; i < a->comp_size; i++) {
+ if (a->comp_ptr[i] == b->comp_ptr[i]) {
+ continue;
+ }
+
+ return a->comp_ptr[i] < b->comp_ptr[i];
+ }
+
+ return false;
+}
+
+CallableCustom::CompareEqualFunc CallableCustomMethodPointerBase::get_compare_equal_func() const {
+ return compare_equal;
+}
+
+CallableCustom::CompareLessFunc CallableCustomMethodPointerBase::get_compare_less_func() const {
+ return compare_less;
+}
+
+uint32_t CallableCustomMethodPointerBase::hash() const {
+ return h;
+}
+
+void CallableCustomMethodPointerBase::_setup(uint32_t *p_base_ptr, uint32_t p_ptr_size) {
+ comp_ptr = p_base_ptr;
+ comp_size = p_ptr_size / 4;
+
+ // Precompute hash.
+ for (uint32_t i = 0; i < comp_size; i++) {
+ if (i == 0) {
+ h = hash_djb2_one_32(comp_ptr[i]);
+ } else {
+ h = hash_djb2_one_32(comp_ptr[i], h);
+ }
+ }
+}
diff --git a/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h
new file mode 100644
index 0000000000..ee6da6a8db
--- /dev/null
+++ b/core/object/callable_method_pointer.h
@@ -0,0 +1,244 @@
+/*************************************************************************/
+/* callable_method_pointer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CALLABLE_METHOD_POINTER_H
+#define CALLABLE_METHOD_POINTER_H
+
+#include "core/object/object.h"
+#include "core/os/copymem.h"
+#include "core/templates/hashfuncs.h"
+#include "core/templates/simple_type.h"
+#include "core/variant/binder_common.h"
+#include "core/variant/callable.h"
+
+class CallableCustomMethodPointerBase : public CallableCustom {
+ uint32_t *comp_ptr;
+ uint32_t comp_size;
+ uint32_t h;
+#ifdef DEBUG_METHODS_ENABLED
+ const char *text = "";
+#endif
+ static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
+ static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
+
+protected:
+ void _setup(uint32_t *p_base_ptr, uint32_t p_ptr_size);
+
+public:
+#ifdef DEBUG_METHODS_ENABLED
+ void set_text(const char *p_text) {
+ text = p_text;
+ }
+ virtual String get_as_text() const {
+ return text;
+ }
+#else
+ virtual String get_as_text() const {
+ return String();
+ }
+#endif
+ virtual CompareEqualFunc get_compare_equal_func() const;
+ virtual CompareLessFunc get_compare_less_func() const;
+
+ virtual uint32_t hash() const;
+};
+
+template <class T, class... P>
+class CallableCustomMethodPointer : public CallableCustomMethodPointerBase {
+ struct Data {
+ T *instance;
+#ifdef DEBUG_ENABLED
+ uint64_t object_id;
+#endif
+ void (T::*method)(P...);
+ } data;
+
+public:
+ virtual ObjectID get_object() const {
+#ifdef DEBUG_ENABLED
+ if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) {
+ return ObjectID();
+ }
+#endif
+ return data.instance->get_instance_id();
+ }
+
+ virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
+#endif
+ call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
+ }
+
+ CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) {
+ zeromem(&data, sizeof(Data)); // Clear beforehand, may have padding bytes.
+ data.instance = p_instance;
+#ifdef DEBUG_ENABLED
+ data.object_id = p_instance->get_instance_id();
+#endif
+ data.method = p_method;
+ _setup((uint32_t *)&data, sizeof(Data));
+ }
+};
+
+template <class T, class... P>
+Callable create_custom_callable_function_pointer(T *p_instance,
+#ifdef DEBUG_METHODS_ENABLED
+ const char *p_func_text,
+#endif
+ void (T::*p_method)(P...)) {
+
+ typedef CallableCustomMethodPointer<T, P...> CCMP; // Messes with memnew otherwise.
+ CCMP *ccmp = memnew(CCMP(p_instance, p_method));
+#ifdef DEBUG_METHODS_ENABLED
+ ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
+#endif
+ return Callable(ccmp);
+}
+
+// VERSION WITH RETURN
+
+template <class T, class R, class... P>
+class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase {
+ struct Data {
+ T *instance;
+#ifdef DEBUG_ENABLED
+ uint64_t object_id;
+#endif
+ R(T::*method)
+ (P...);
+ } data;
+
+public:
+ virtual ObjectID get_object() const {
+#ifdef DEBUG_ENABLED
+ if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) {
+ return ObjectID();
+ }
+#endif
+ return data.instance->get_instance_id();
+ }
+
+ virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
+#endif
+ call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
+ }
+
+ CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) {
+ zeromem(&data, sizeof(Data)); // Clear beforehand, may have padding bytes.
+ data.instance = p_instance;
+#ifdef DEBUG_ENABLED
+ data.object_id = p_instance->get_instance_id();
+#endif
+ data.method = p_method;
+ _setup((uint32_t *)&data, sizeof(Data));
+ }
+};
+
+template <class T, class R, class... P>
+Callable create_custom_callable_function_pointer(T *p_instance,
+#ifdef DEBUG_METHODS_ENABLED
+ const char *p_func_text,
+#endif
+ R (T::*p_method)(P...)) {
+
+ typedef CallableCustomMethodPointerRet<T, R, P...> CCMP; // Messes with memnew otherwise.
+ CCMP *ccmp = memnew(CCMP(p_instance, p_method));
+#ifdef DEBUG_METHODS_ENABLED
+ ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
+#endif
+ return Callable(ccmp);
+}
+
+// CONST VERSION WITH RETURN
+
+template <class T, class R, class... P>
+class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase {
+ struct Data {
+ T *instance;
+#ifdef DEBUG_ENABLED
+ uint64_t object_id;
+#endif
+ R(T::*method)
+ (P...) const;
+ } data;
+
+public:
+ virtual ObjectID get_object() const {
+#ifdef DEBUG_ENABLED
+ if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) {
+ return ObjectID();
+ }
+#endif
+ return data.instance->get_instance_id();
+ }
+
+ virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
+#endif
+ call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
+ }
+
+ CallableCustomMethodPointerRetC(T *p_instance, R (T::*p_method)(P...) const) {
+ zeromem(&data, sizeof(Data)); // Clear beforehand, may have padding bytes.
+ data.instance = p_instance;
+#ifdef DEBUG_ENABLED
+ data.object_id = p_instance->get_instance_id();
+#endif
+ data.method = p_method;
+ _setup((uint32_t *)&data, sizeof(Data));
+ }
+};
+
+template <class T, class R, class... P>
+Callable create_custom_callable_function_pointer(T *p_instance,
+#ifdef DEBUG_METHODS_ENABLED
+ const char *p_func_text,
+#endif
+ R (T::*p_method)(P...) const) {
+
+ typedef CallableCustomMethodPointerRetC<T, R, P...> CCMP; // Messes with memnew otherwise.
+ CCMP *ccmp = memnew(CCMP(p_instance, p_method));
+#ifdef DEBUG_METHODS_ENABLED
+ ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
+#endif
+ return Callable(ccmp);
+}
+
+#ifdef DEBUG_METHODS_ENABLED
+#define callable_mp(I, M) create_custom_callable_function_pointer(I, #M, M)
+#else
+#define callable_mp(I, M) create_custom_callable_function_pointer(I, M)
+#endif
+
+#endif // CALLABLE_METHOD_POINTER_H
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
new file mode 100644
index 0000000000..64ebeb427e
--- /dev/null
+++ b/core/object/class_db.cpp
@@ -0,0 +1,1571 @@
+/*************************************************************************/
+/* class_db.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "class_db.h"
+
+#include "core/config/engine.h"
+#include "core/os/mutex.h"
+#include "core/version.h"
+
+#define OBJTYPE_RLOCK RWLockRead _rw_lockr_(lock);
+#define OBJTYPE_WLOCK RWLockWrite _rw_lockw_(lock);
+
+#ifdef DEBUG_METHODS_ENABLED
+
+MethodDefinition D_METHOD(const char *p_name) {
+ MethodDefinition md;
+ md.name = StaticCString::create(p_name);
+ return md;
+}
+
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1) {
+ MethodDefinition md;
+ md.name = StaticCString::create(p_name);
+ md.args.push_back(StaticCString::create(p_arg1));
+ return md;
+}
+
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2) {
+ MethodDefinition md;
+ md.name = StaticCString::create(p_name);
+ md.args.resize(2);
+ md.args.write[0] = StaticCString::create(p_arg1);
+ md.args.write[1] = StaticCString::create(p_arg2);
+ return md;
+}
+
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3) {
+ MethodDefinition md;
+ md.name = StaticCString::create(p_name);
+ md.args.resize(3);
+ md.args.write[0] = StaticCString::create(p_arg1);
+ md.args.write[1] = StaticCString::create(p_arg2);
+ md.args.write[2] = StaticCString::create(p_arg3);
+ return md;
+}
+
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4) {
+ MethodDefinition md;
+ md.name = StaticCString::create(p_name);
+ md.args.resize(4);
+ md.args.write[0] = StaticCString::create(p_arg1);
+ md.args.write[1] = StaticCString::create(p_arg2);
+ md.args.write[2] = StaticCString::create(p_arg3);
+ md.args.write[3] = StaticCString::create(p_arg4);
+ return md;
+}
+
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5) {
+ MethodDefinition md;
+ md.name = StaticCString::create(p_name);
+ md.args.resize(5);
+ md.args.write[0] = StaticCString::create(p_arg1);
+ md.args.write[1] = StaticCString::create(p_arg2);
+ md.args.write[2] = StaticCString::create(p_arg3);
+ md.args.write[3] = StaticCString::create(p_arg4);
+ md.args.write[4] = StaticCString::create(p_arg5);
+ return md;
+}
+
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6) {
+ MethodDefinition md;
+ md.name = StaticCString::create(p_name);
+ md.args.resize(6);
+ md.args.write[0] = StaticCString::create(p_arg1);
+ md.args.write[1] = StaticCString::create(p_arg2);
+ md.args.write[2] = StaticCString::create(p_arg3);
+ md.args.write[3] = StaticCString::create(p_arg4);
+ md.args.write[4] = StaticCString::create(p_arg5);
+ md.args.write[5] = StaticCString::create(p_arg6);
+ return md;
+}
+
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7) {
+ MethodDefinition md;
+ md.name = StaticCString::create(p_name);
+ md.args.resize(7);
+ md.args.write[0] = StaticCString::create(p_arg1);
+ md.args.write[1] = StaticCString::create(p_arg2);
+ md.args.write[2] = StaticCString::create(p_arg3);
+ md.args.write[3] = StaticCString::create(p_arg4);
+ md.args.write[4] = StaticCString::create(p_arg5);
+ md.args.write[5] = StaticCString::create(p_arg6);
+ md.args.write[6] = StaticCString::create(p_arg7);
+ return md;
+}
+
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8) {
+ MethodDefinition md;
+ md.name = StaticCString::create(p_name);
+ md.args.resize(8);
+ md.args.write[0] = StaticCString::create(p_arg1);
+ md.args.write[1] = StaticCString::create(p_arg2);
+ md.args.write[2] = StaticCString::create(p_arg3);
+ md.args.write[3] = StaticCString::create(p_arg4);
+ md.args.write[4] = StaticCString::create(p_arg5);
+ md.args.write[5] = StaticCString::create(p_arg6);
+ md.args.write[6] = StaticCString::create(p_arg7);
+ md.args.write[7] = StaticCString::create(p_arg8);
+ return md;
+}
+
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9) {
+ MethodDefinition md;
+ md.name = StaticCString::create(p_name);
+ md.args.resize(9);
+ md.args.write[0] = StaticCString::create(p_arg1);
+ md.args.write[1] = StaticCString::create(p_arg2);
+ md.args.write[2] = StaticCString::create(p_arg3);
+ md.args.write[3] = StaticCString::create(p_arg4);
+ md.args.write[4] = StaticCString::create(p_arg5);
+ md.args.write[5] = StaticCString::create(p_arg6);
+ md.args.write[6] = StaticCString::create(p_arg7);
+ md.args.write[7] = StaticCString::create(p_arg8);
+ md.args.write[8] = StaticCString::create(p_arg9);
+ return md;
+}
+
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10) {
+ MethodDefinition md;
+ md.name = StaticCString::create(p_name);
+ md.args.resize(10);
+ md.args.write[0] = StaticCString::create(p_arg1);
+ md.args.write[1] = StaticCString::create(p_arg2);
+ md.args.write[2] = StaticCString::create(p_arg3);
+ md.args.write[3] = StaticCString::create(p_arg4);
+ md.args.write[4] = StaticCString::create(p_arg5);
+ md.args.write[5] = StaticCString::create(p_arg6);
+ md.args.write[6] = StaticCString::create(p_arg7);
+ md.args.write[7] = StaticCString::create(p_arg8);
+ md.args.write[8] = StaticCString::create(p_arg9);
+ md.args.write[9] = StaticCString::create(p_arg10);
+ return md;
+}
+
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11) {
+ MethodDefinition md;
+ md.name = StaticCString::create(p_name);
+ md.args.resize(11);
+ md.args.write[0] = StaticCString::create(p_arg1);
+ md.args.write[1] = StaticCString::create(p_arg2);
+ md.args.write[2] = StaticCString::create(p_arg3);
+ md.args.write[3] = StaticCString::create(p_arg4);
+ md.args.write[4] = StaticCString::create(p_arg5);
+ md.args.write[5] = StaticCString::create(p_arg6);
+ md.args.write[6] = StaticCString::create(p_arg7);
+ md.args.write[7] = StaticCString::create(p_arg8);
+ md.args.write[8] = StaticCString::create(p_arg9);
+ md.args.write[9] = StaticCString::create(p_arg10);
+ md.args.write[10] = StaticCString::create(p_arg11);
+ return md;
+}
+
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12) {
+ MethodDefinition md;
+ md.name = StaticCString::create(p_name);
+ md.args.resize(12);
+ md.args.write[0] = StaticCString::create(p_arg1);
+ md.args.write[1] = StaticCString::create(p_arg2);
+ md.args.write[2] = StaticCString::create(p_arg3);
+ md.args.write[3] = StaticCString::create(p_arg4);
+ md.args.write[4] = StaticCString::create(p_arg5);
+ md.args.write[5] = StaticCString::create(p_arg6);
+ md.args.write[6] = StaticCString::create(p_arg7);
+ md.args.write[7] = StaticCString::create(p_arg8);
+ md.args.write[8] = StaticCString::create(p_arg9);
+ md.args.write[9] = StaticCString::create(p_arg10);
+ md.args.write[10] = StaticCString::create(p_arg11);
+ md.args.write[11] = StaticCString::create(p_arg12);
+ return md;
+}
+
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12, const char *p_arg13) {
+ MethodDefinition md;
+ md.name = StaticCString::create(p_name);
+ md.args.resize(13);
+ md.args.write[0] = StaticCString::create(p_arg1);
+ md.args.write[1] = StaticCString::create(p_arg2);
+ md.args.write[2] = StaticCString::create(p_arg3);
+ md.args.write[3] = StaticCString::create(p_arg4);
+ md.args.write[4] = StaticCString::create(p_arg5);
+ md.args.write[5] = StaticCString::create(p_arg6);
+ md.args.write[6] = StaticCString::create(p_arg7);
+ md.args.write[7] = StaticCString::create(p_arg8);
+ md.args.write[8] = StaticCString::create(p_arg9);
+ md.args.write[9] = StaticCString::create(p_arg10);
+ md.args.write[10] = StaticCString::create(p_arg11);
+ md.args.write[11] = StaticCString::create(p_arg12);
+ md.args.write[12] = StaticCString::create(p_arg13);
+ return md;
+}
+
+#endif
+
+ClassDB::APIType ClassDB::current_api = API_CORE;
+
+void ClassDB::set_current_api(APIType p_api) {
+ current_api = p_api;
+}
+
+ClassDB::APIType ClassDB::get_current_api() {
+ return current_api;
+}
+
+HashMap<StringName, ClassDB::ClassInfo> ClassDB::classes;
+HashMap<StringName, StringName> ClassDB::resource_base_extensions;
+HashMap<StringName, StringName> ClassDB::compat_classes;
+
+bool ClassDB::_is_parent_class(const StringName &p_class, const StringName &p_inherits) {
+ StringName inherits = p_class;
+
+ while (inherits.operator String().length()) {
+ if (inherits == p_inherits) {
+ return true;
+ }
+ inherits = _get_parent_class(inherits);
+ }
+
+ return false;
+}
+
+bool ClassDB::is_parent_class(const StringName &p_class, const StringName &p_inherits) {
+ OBJTYPE_RLOCK;
+
+ return _is_parent_class(p_class, p_inherits);
+}
+
+void ClassDB::get_class_list(List<StringName> *p_classes) {
+ OBJTYPE_RLOCK;
+
+ const StringName *k = nullptr;
+
+ while ((k = classes.next(k))) {
+ p_classes->push_back(*k);
+ }
+
+ p_classes->sort();
+}
+
+void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) {
+ OBJTYPE_RLOCK;
+
+ const StringName *k = nullptr;
+
+ while ((k = classes.next(k))) {
+ if (*k != p_class && _is_parent_class(*k, p_class)) {
+ p_classes->push_back(*k);
+ }
+ }
+}
+
+void ClassDB::get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) {
+ OBJTYPE_RLOCK;
+
+ const StringName *k = nullptr;
+
+ while ((k = classes.next(k))) {
+ if (*k != p_class && _get_parent_class(*k) == p_class) {
+ p_classes->push_back(*k);
+ }
+ }
+}
+
+StringName ClassDB::get_parent_class_nocheck(const StringName &p_class) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *ti = classes.getptr(p_class);
+ if (!ti) {
+ return StringName();
+ }
+ return ti->inherits;
+}
+
+StringName ClassDB::get_compatibility_remapped_class(const StringName &p_class) {
+ if (classes.has(p_class)) {
+ return p_class;
+ }
+
+ if (compat_classes.has(p_class)) {
+ return compat_classes[p_class];
+ }
+
+ return p_class;
+}
+
+StringName ClassDB::_get_parent_class(const StringName &p_class) {
+ ClassInfo *ti = classes.getptr(p_class);
+ ERR_FAIL_COND_V_MSG(!ti, StringName(), "Cannot get class '" + String(p_class) + "'.");
+ return ti->inherits;
+}
+
+StringName ClassDB::get_parent_class(const StringName &p_class) {
+ OBJTYPE_RLOCK;
+
+ return _get_parent_class(p_class);
+}
+
+ClassDB::APIType ClassDB::get_api_type(const StringName &p_class) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *ti = classes.getptr(p_class);
+
+ ERR_FAIL_COND_V_MSG(!ti, API_NONE, "Cannot get class '" + String(p_class) + "'.");
+ return ti->api;
+}
+
+uint64_t ClassDB::get_api_hash(APIType p_api) {
+ OBJTYPE_RLOCK;
+#ifdef DEBUG_METHODS_ENABLED
+
+ uint64_t hash = hash_djb2_one_64(HashMapHasherDefault::hash(VERSION_FULL_CONFIG));
+
+ List<StringName> names;
+
+ const StringName *k = nullptr;
+
+ while ((k = classes.next(k))) {
+ names.push_back(*k);
+ }
+ //must be alphabetically sorted for hash to compute
+ names.sort_custom<StringName::AlphCompare>();
+
+ for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
+ ClassInfo *t = classes.getptr(E->get());
+ ERR_FAIL_COND_V_MSG(!t, 0, "Cannot get class '" + String(E->get()) + "'.");
+ if (t->api != p_api || !t->exposed) {
+ continue;
+ }
+ hash = hash_djb2_one_64(t->name.hash(), hash);
+ hash = hash_djb2_one_64(t->inherits.hash(), hash);
+
+ { //methods
+
+ List<StringName> snames;
+
+ k = nullptr;
+
+ while ((k = t->method_map.next(k))) {
+ String name = k->operator String();
+
+ ERR_CONTINUE(name.empty());
+
+ if (name[0] == '_') {
+ continue; // Ignore non-virtual methods that start with an underscore
+ }
+
+ snames.push_back(*k);
+ }
+
+ snames.sort_custom<StringName::AlphCompare>();
+
+ for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
+ MethodBind *mb = t->method_map[F->get()];
+ hash = hash_djb2_one_64(mb->get_name().hash(), hash);
+ hash = hash_djb2_one_64(mb->get_argument_count(), hash);
+ hash = hash_djb2_one_64(mb->get_argument_type(-1), hash); //return
+
+ for (int i = 0; i < mb->get_argument_count(); i++) {
+ const PropertyInfo info = mb->get_argument_info(i);
+ hash = hash_djb2_one_64(info.type, hash);
+ hash = hash_djb2_one_64(info.name.hash(), hash);
+ hash = hash_djb2_one_64(info.hint, hash);
+ hash = hash_djb2_one_64(info.hint_string.hash(), hash);
+ }
+
+ hash = hash_djb2_one_64(mb->get_default_argument_count(), hash);
+
+ for (int i = 0; i < mb->get_default_argument_count(); i++) {
+ //hash should not change, i hope for tis
+ Variant da = mb->get_default_argument(i);
+ hash = hash_djb2_one_64(da.hash(), hash);
+ }
+
+ hash = hash_djb2_one_64(mb->get_hint_flags(), hash);
+ }
+ }
+
+ { //constants
+
+ List<StringName> snames;
+
+ k = nullptr;
+
+ while ((k = t->constant_map.next(k))) {
+ snames.push_back(*k);
+ }
+
+ snames.sort_custom<StringName::AlphCompare>();
+
+ for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
+ hash = hash_djb2_one_64(F->get().hash(), hash);
+ hash = hash_djb2_one_64(t->constant_map[F->get()], hash);
+ }
+ }
+
+ { //signals
+
+ List<StringName> snames;
+
+ k = nullptr;
+
+ while ((k = t->signal_map.next(k))) {
+ snames.push_back(*k);
+ }
+
+ snames.sort_custom<StringName::AlphCompare>();
+
+ for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
+ MethodInfo &mi = t->signal_map[F->get()];
+ hash = hash_djb2_one_64(F->get().hash(), hash);
+ for (int i = 0; i < mi.arguments.size(); i++) {
+ hash = hash_djb2_one_64(mi.arguments[i].type, hash);
+ }
+ }
+ }
+
+ { //properties
+
+ List<StringName> snames;
+
+ k = nullptr;
+
+ while ((k = t->property_setget.next(k))) {
+ snames.push_back(*k);
+ }
+
+ snames.sort_custom<StringName::AlphCompare>();
+
+ for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
+ PropertySetGet *psg = t->property_setget.getptr(F->get());
+ ERR_FAIL_COND_V(!psg, 0);
+
+ hash = hash_djb2_one_64(F->get().hash(), hash);
+ hash = hash_djb2_one_64(psg->setter.hash(), hash);
+ hash = hash_djb2_one_64(psg->getter.hash(), hash);
+ }
+ }
+
+ //property list
+ for (List<PropertyInfo>::Element *F = t->property_list.front(); F; F = F->next()) {
+ hash = hash_djb2_one_64(F->get().name.hash(), hash);
+ hash = hash_djb2_one_64(F->get().type, hash);
+ hash = hash_djb2_one_64(F->get().hint, hash);
+ hash = hash_djb2_one_64(F->get().hint_string.hash(), hash);
+ hash = hash_djb2_one_64(F->get().usage, hash);
+ }
+ }
+
+ return hash;
+#else
+ return 0;
+#endif
+}
+
+bool ClassDB::class_exists(const StringName &p_class) {
+ OBJTYPE_RLOCK;
+ return classes.has(p_class);
+}
+
+void ClassDB::add_compatibility_class(const StringName &p_class, const StringName &p_fallback) {
+ OBJTYPE_WLOCK;
+ compat_classes[p_class] = p_fallback;
+}
+
+Object *ClassDB::instance(const StringName &p_class) {
+ ClassInfo *ti;
+ {
+ OBJTYPE_RLOCK;
+ ti = classes.getptr(p_class);
+ if (!ti || ti->disabled || !ti->creation_func) {
+ if (compat_classes.has(p_class)) {
+ ti = classes.getptr(compat_classes[p_class]);
+ }
+ }
+ ERR_FAIL_COND_V_MSG(!ti, nullptr, "Cannot get class '" + String(p_class) + "'.");
+ ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled.");
+ ERR_FAIL_COND_V(!ti->creation_func, nullptr);
+ }
+#ifdef TOOLS_ENABLED
+ if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) {
+ ERR_PRINT("Class '" + String(p_class) + "' can only be instantiated by editor.");
+ return nullptr;
+ }
+#endif
+ return ti->creation_func();
+}
+
+bool ClassDB::can_instance(const StringName &p_class) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *ti = classes.getptr(p_class);
+ ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'.");
+#ifdef TOOLS_ENABLED
+ if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) {
+ return false;
+ }
+#endif
+ return (!ti->disabled && ti->creation_func != nullptr);
+}
+
+void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) {
+ OBJTYPE_WLOCK;
+
+ const StringName &name = p_class;
+
+ ERR_FAIL_COND_MSG(classes.has(name), "Class '" + String(p_class) + "' already exists.");
+
+ classes[name] = ClassInfo();
+ ClassInfo &ti = classes[name];
+ ti.name = name;
+ ti.inherits = p_inherits;
+ ti.api = current_api;
+
+ if (ti.inherits) {
+ ERR_FAIL_COND(!classes.has(ti.inherits)); //it MUST be registered.
+ ti.inherits_ptr = &classes[ti.inherits];
+
+ } else {
+ ti.inherits_ptr = nullptr;
+ }
+}
+
+#ifdef DEBUG_METHODS_ENABLED
+static MethodInfo info_from_bind(MethodBind *p_method) {
+ MethodInfo minfo;
+ minfo.name = p_method->get_name();
+ minfo.id = p_method->get_method_id();
+
+ for (int i = 0; i < p_method->get_argument_count(); i++) {
+ minfo.arguments.push_back(p_method->get_argument_info(i));
+ }
+
+ minfo.return_val = p_method->get_return_info();
+ minfo.flags = p_method->get_hint_flags();
+
+ for (int i = 0; i < p_method->get_argument_count(); i++) {
+ if (p_method->has_default_argument(i)) {
+ minfo.default_arguments.push_back(p_method->get_default_argument(i));
+ }
+ }
+
+ return minfo;
+}
+#endif
+
+void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ if (type->disabled) {
+ if (p_no_inheritance) {
+ break;
+ }
+
+ type = type->inherits_ptr;
+ continue;
+ }
+
+#ifdef DEBUG_METHODS_ENABLED
+
+ for (List<MethodInfo>::Element *E = type->virtual_methods.front(); E; E = E->next()) {
+ p_methods->push_back(E->get());
+ }
+
+ for (List<StringName>::Element *E = type->method_order.front(); E; E = E->next()) {
+ if (p_exclude_from_properties && type->methods_in_properties.has(E->get())) {
+ continue;
+ }
+
+ MethodBind *method = type->method_map.get(E->get());
+ MethodInfo minfo = info_from_bind(method);
+
+ p_methods->push_back(minfo);
+ }
+
+#else
+
+ const StringName *K = nullptr;
+
+ while ((K = type->method_map.next(K))) {
+ MethodBind *m = type->method_map[*K];
+ MethodInfo mi;
+ mi.name = m->get_name();
+ p_methods->push_back(mi);
+ }
+
+#endif
+
+ if (p_no_inheritance) {
+ break;
+ }
+
+ type = type->inherits_ptr;
+ }
+}
+
+bool ClassDB::get_method_info(StringName p_class, StringName p_method, MethodInfo *r_info, bool p_no_inheritance, bool p_exclude_from_properties) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ if (type->disabled) {
+ if (p_no_inheritance) {
+ break;
+ }
+
+ type = type->inherits_ptr;
+ continue;
+ }
+
+#ifdef DEBUG_METHODS_ENABLED
+ MethodBind **method = type->method_map.getptr(p_method);
+ if (method && *method) {
+ if (r_info != nullptr) {
+ MethodInfo minfo = info_from_bind(*method);
+ *r_info = minfo;
+ }
+ return true;
+ } else if (type->virtual_methods_map.has(p_method)) {
+ if (r_info) {
+ *r_info = type->virtual_methods_map[p_method];
+ }
+ return true;
+ }
+#else
+ if (type->method_map.has(p_method)) {
+ if (r_info) {
+ MethodBind *m = type->method_map[p_method];
+ MethodInfo mi;
+ mi.name = m->get_name();
+ *r_info = mi;
+ }
+ return true;
+ }
+#endif
+
+ if (p_no_inheritance) {
+ break;
+ }
+
+ type = type->inherits_ptr;
+ }
+
+ return false;
+}
+
+MethodBind *ClassDB::get_method(StringName p_class, StringName p_name) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ MethodBind **method = type->method_map.getptr(p_name);
+ if (method && *method) {
+ return *method;
+ }
+ type = type->inherits_ptr;
+ }
+ return nullptr;
+}
+
+void ClassDB::bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int p_constant) {
+ OBJTYPE_WLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ ERR_FAIL_COND(!type);
+
+ if (type->constant_map.has(p_name)) {
+ ERR_FAIL();
+ }
+
+ type->constant_map[p_name] = p_constant;
+
+ String enum_name = p_enum;
+ if (enum_name != String()) {
+ if (enum_name.find(".") != -1) {
+ enum_name = enum_name.get_slicec('.', 1);
+ }
+
+ List<StringName> *constants_list = type->enum_map.getptr(enum_name);
+
+ if (constants_list) {
+ constants_list->push_back(p_name);
+ } else {
+ List<StringName> new_list;
+ new_list.push_back(p_name);
+ type->enum_map[enum_name] = new_list;
+ }
+ }
+
+#ifdef DEBUG_METHODS_ENABLED
+ type->constant_order.push_back(p_name);
+#endif
+}
+
+void ClassDB::get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+#ifdef DEBUG_METHODS_ENABLED
+ for (List<StringName>::Element *E = type->constant_order.front(); E; E = E->next()) {
+ p_constants->push_back(E->get());
+ }
+#else
+ const StringName *K = nullptr;
+
+ while ((K = type->constant_map.next(K))) {
+ p_constants->push_back(*K);
+ }
+
+#endif
+ if (p_no_inheritance) {
+ break;
+ }
+
+ type = type->inherits_ptr;
+ }
+}
+
+int ClassDB::get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ int *constant = type->constant_map.getptr(p_name);
+ if (constant) {
+ if (p_success) {
+ *p_success = true;
+ }
+ return *constant;
+ }
+
+ type = type->inherits_ptr;
+ }
+
+ if (p_success) {
+ *p_success = false;
+ }
+
+ return 0;
+}
+
+bool ClassDB::has_integer_constant(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ if (type->constant_map.has(p_name)) {
+ return true;
+ }
+ if (p_no_inheritance) {
+ return false;
+ }
+
+ type = type->inherits_ptr;
+ }
+
+ return false;
+}
+
+StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ const StringName *k = nullptr;
+ while ((k = type->enum_map.next(k))) {
+ List<StringName> &constants_list = type->enum_map.get(*k);
+ const List<StringName>::Element *found = constants_list.find(p_name);
+ if (found) {
+ return *k;
+ }
+ }
+
+ if (p_no_inheritance) {
+ break;
+ }
+
+ type = type->inherits_ptr;
+ }
+
+ return StringName();
+}
+
+void ClassDB::get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ const StringName *k = nullptr;
+ while ((k = type->enum_map.next(k))) {
+ p_enums->push_back(*k);
+ }
+
+ if (p_no_inheritance) {
+ break;
+ }
+
+ type = type->inherits_ptr;
+ }
+}
+
+void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ const List<StringName> *constants = type->enum_map.getptr(p_enum);
+
+ if (constants) {
+ for (const List<StringName>::Element *E = constants->front(); E; E = E->next()) {
+ p_constants->push_back(E->get());
+ }
+ }
+
+ if (p_no_inheritance) {
+ break;
+ }
+
+ type = type->inherits_ptr;
+ }
+}
+
+bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ if (type->enum_map.has(p_name)) {
+ return true;
+ }
+ if (p_no_inheritance) {
+ return false;
+ }
+
+ type = type->inherits_ptr;
+ }
+
+ return false;
+}
+
+void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) {
+ OBJTYPE_WLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+ ERR_FAIL_COND(!type);
+
+ StringName sname = p_signal.name;
+
+#ifdef DEBUG_METHODS_ENABLED
+ ClassInfo *check = type;
+ while (check) {
+ ERR_FAIL_COND_MSG(check->signal_map.has(sname), "Class '" + String(p_class) + "' already has signal '" + String(sname) + "'.");
+ check = check->inherits_ptr;
+ }
+#endif
+
+ type->signal_map[sname] = p_signal;
+}
+
+void ClassDB::get_signal_list(StringName p_class, List<MethodInfo> *p_signals, bool p_no_inheritance) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+ ERR_FAIL_COND(!type);
+
+ ClassInfo *check = type;
+
+ while (check) {
+ const StringName *S = nullptr;
+ while ((S = check->signal_map.next(S))) {
+ p_signals->push_back(check->signal_map[*S]);
+ }
+
+ if (p_no_inheritance) {
+ return;
+ }
+
+ check = check->inherits_ptr;
+ }
+}
+
+bool ClassDB::has_signal(StringName p_class, StringName p_signal, bool p_no_inheritance) {
+ OBJTYPE_RLOCK;
+ ClassInfo *type = classes.getptr(p_class);
+ ClassInfo *check = type;
+ while (check) {
+ if (check->signal_map.has(p_signal)) {
+ return true;
+ }
+ if (p_no_inheritance) {
+ return false;
+ }
+ check = check->inherits_ptr;
+ }
+
+ return false;
+}
+
+bool ClassDB::get_signal(StringName p_class, StringName p_signal, MethodInfo *r_signal) {
+ OBJTYPE_RLOCK;
+ ClassInfo *type = classes.getptr(p_class);
+ ClassInfo *check = type;
+ while (check) {
+ if (check->signal_map.has(p_signal)) {
+ if (r_signal) {
+ *r_signal = check->signal_map[p_signal];
+ }
+ return true;
+ }
+ check = check->inherits_ptr;
+ }
+
+ return false;
+}
+
+void ClassDB::add_property_group(StringName p_class, const String &p_name, const String &p_prefix) {
+ OBJTYPE_WLOCK;
+ ClassInfo *type = classes.getptr(p_class);
+ ERR_FAIL_COND(!type);
+
+ type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_GROUP));
+}
+
+void ClassDB::add_property_subgroup(StringName p_class, const String &p_name, const String &p_prefix) {
+ OBJTYPE_WLOCK;
+ ClassInfo *type = classes.getptr(p_class);
+ ERR_FAIL_COND(!type);
+
+ type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_SUBGROUP));
+}
+
+void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index) {
+ lock->read_lock();
+ ClassInfo *type = classes.getptr(p_class);
+ lock->read_unlock();
+
+ ERR_FAIL_COND(!type);
+
+ MethodBind *mb_set = nullptr;
+ if (p_setter) {
+ mb_set = get_method(p_class, p_setter);
+#ifdef DEBUG_METHODS_ENABLED
+
+ ERR_FAIL_COND_MSG(!mb_set, "Invalid setter '" + p_class + "::" + p_setter + "' for property '" + p_pinfo.name + "'.");
+
+ int exp_args = 1 + (p_index >= 0 ? 1 : 0);
+ ERR_FAIL_COND_MSG(mb_set->get_argument_count() != exp_args, "Invalid function for setter '" + p_class + "::" + p_setter + " for property '" + p_pinfo.name + "'.");
+#endif
+ }
+
+ MethodBind *mb_get = nullptr;
+ if (p_getter) {
+ mb_get = get_method(p_class, p_getter);
+#ifdef DEBUG_METHODS_ENABLED
+
+ ERR_FAIL_COND_MSG(!mb_get, "Invalid getter '" + p_class + "::" + p_getter + "' for property '" + p_pinfo.name + "'.");
+
+ int exp_args = 0 + (p_index >= 0 ? 1 : 0);
+ ERR_FAIL_COND_MSG(mb_get->get_argument_count() != exp_args, "Invalid function for getter '" + p_class + "::" + p_getter + "' for property: '" + p_pinfo.name + "'.");
+#endif
+ }
+
+#ifdef DEBUG_METHODS_ENABLED
+ ERR_FAIL_COND_MSG(type->property_setget.has(p_pinfo.name), "Object '" + p_class + "' already has property '" + p_pinfo.name + "'.");
+#endif
+
+ OBJTYPE_WLOCK
+
+ type->property_list.push_back(p_pinfo);
+ type->property_map[p_pinfo.name] = p_pinfo;
+#ifdef DEBUG_METHODS_ENABLED
+ if (mb_get) {
+ type->methods_in_properties.insert(p_getter);
+ }
+ if (mb_set) {
+ type->methods_in_properties.insert(p_setter);
+ }
+#endif
+ PropertySetGet psg;
+ psg.setter = p_setter;
+ psg.getter = p_getter;
+ psg._setptr = mb_set;
+ psg._getptr = mb_get;
+ psg.index = p_index;
+ psg.type = p_pinfo.type;
+
+ type->property_setget[p_pinfo.name] = psg;
+}
+
+void ClassDB::set_property_default_value(StringName p_class, const StringName &p_name, const Variant &p_default) {
+ if (!default_values.has(p_class)) {
+ default_values[p_class] = HashMap<StringName, Variant>();
+ }
+ default_values[p_class][p_name] = p_default;
+}
+
+void ClassDB::get_property_list(StringName p_class, List<PropertyInfo> *p_list, bool p_no_inheritance, const Object *p_validator) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+ ClassInfo *check = type;
+ while (check) {
+ for (List<PropertyInfo>::Element *E = check->property_list.front(); E; E = E->next()) {
+ if (p_validator) {
+ PropertyInfo pi = E->get();
+ p_validator->_validate_property(pi);
+ p_list->push_back(pi);
+ } else {
+ p_list->push_back(E->get());
+ }
+ }
+
+ if (p_no_inheritance) {
+ return;
+ }
+ check = check->inherits_ptr;
+ }
+}
+
+bool ClassDB::get_property_info(StringName p_class, StringName p_property, PropertyInfo *r_info, bool p_no_inheritance, const Object *p_validator) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *check = classes.getptr(p_class);
+ while (check) {
+ if (check->property_map.has(p_property)) {
+ PropertyInfo pinfo = check->property_map[p_property];
+ if (p_validator) {
+ p_validator->_validate_property(pinfo);
+ }
+ if (r_info) {
+ *r_info = pinfo;
+ }
+ return true;
+ }
+ if (p_no_inheritance) {
+ break;
+ }
+ check = check->inherits_ptr;
+ }
+
+ return false;
+}
+
+bool ClassDB::set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid) {
+ ClassInfo *type = classes.getptr(p_object->get_class_name());
+ ClassInfo *check = type;
+ while (check) {
+ const PropertySetGet *psg = check->property_setget.getptr(p_property);
+ if (psg) {
+ if (!psg->setter) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+ return true; //return true but do nothing
+ }
+
+ Callable::CallError ce;
+
+ if (psg->index >= 0) {
+ Variant index = psg->index;
+ const Variant *arg[2] = { &index, &p_value };
+ //p_object->call(psg->setter,arg,2,ce);
+ if (psg->_setptr) {
+ psg->_setptr->call(p_object, arg, 2, ce);
+ } else {
+ p_object->call(psg->setter, arg, 2, ce);
+ }
+
+ } else {
+ const Variant *arg[1] = { &p_value };
+ if (psg->_setptr) {
+ psg->_setptr->call(p_object, arg, 1, ce);
+ } else {
+ p_object->call(psg->setter, arg, 1, ce);
+ }
+ }
+
+ if (r_valid) {
+ *r_valid = ce.error == Callable::CallError::CALL_OK;
+ }
+
+ return true;
+ }
+
+ check = check->inherits_ptr;
+ }
+
+ return false;
+}
+
+bool ClassDB::get_property(Object *p_object, const StringName &p_property, Variant &r_value) {
+ ClassInfo *type = classes.getptr(p_object->get_class_name());
+ ClassInfo *check = type;
+ while (check) {
+ const PropertySetGet *psg = check->property_setget.getptr(p_property);
+ if (psg) {
+ if (!psg->getter) {
+ return true; //return true but do nothing
+ }
+
+ if (psg->index >= 0) {
+ Variant index = psg->index;
+ const Variant *arg[1] = { &index };
+ Callable::CallError ce;
+ r_value = p_object->call(psg->getter, arg, 1, ce);
+
+ } else {
+ Callable::CallError ce;
+ if (psg->_getptr) {
+ r_value = psg->_getptr->call(p_object, nullptr, 0, ce);
+ } else {
+ r_value = p_object->call(psg->getter, nullptr, 0, ce);
+ }
+ }
+ return true;
+ }
+
+ const int *c = check->constant_map.getptr(p_property); //constants count
+ if (c) {
+ r_value = *c;
+ return true;
+ }
+
+ if (check->method_map.has(p_property)) { //methods count
+ r_value = Callable(p_object, p_property);
+ return true;
+ }
+
+ if (check->signal_map.has(p_property)) { //signals count
+ r_value = Signal(p_object, p_property);
+ return true;
+ }
+
+ check = check->inherits_ptr;
+ }
+
+ return false;
+}
+
+int ClassDB::get_property_index(const StringName &p_class, const StringName &p_property, bool *r_is_valid) {
+ ClassInfo *type = classes.getptr(p_class);
+ ClassInfo *check = type;
+ while (check) {
+ const PropertySetGet *psg = check->property_setget.getptr(p_property);
+ if (psg) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+
+ return psg->index;
+ }
+
+ check = check->inherits_ptr;
+ }
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+
+ return -1;
+}
+
+Variant::Type ClassDB::get_property_type(const StringName &p_class, const StringName &p_property, bool *r_is_valid) {
+ ClassInfo *type = classes.getptr(p_class);
+ ClassInfo *check = type;
+ while (check) {
+ const PropertySetGet *psg = check->property_setget.getptr(p_property);
+ if (psg) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+
+ return psg->type;
+ }
+
+ check = check->inherits_ptr;
+ }
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+
+ return Variant::NIL;
+}
+
+StringName ClassDB::get_property_setter(StringName p_class, const StringName &p_property) {
+ ClassInfo *type = classes.getptr(p_class);
+ ClassInfo *check = type;
+ while (check) {
+ const PropertySetGet *psg = check->property_setget.getptr(p_property);
+ if (psg) {
+ return psg->setter;
+ }
+
+ check = check->inherits_ptr;
+ }
+
+ return StringName();
+}
+
+StringName ClassDB::get_property_getter(StringName p_class, const StringName &p_property) {
+ ClassInfo *type = classes.getptr(p_class);
+ ClassInfo *check = type;
+ while (check) {
+ const PropertySetGet *psg = check->property_setget.getptr(p_property);
+ if (psg) {
+ return psg->getter;
+ }
+
+ check = check->inherits_ptr;
+ }
+
+ return StringName();
+}
+
+bool ClassDB::has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance) {
+ ClassInfo *type = classes.getptr(p_class);
+ ClassInfo *check = type;
+ while (check) {
+ if (check->property_setget.has(p_property)) {
+ return true;
+ }
+
+ if (p_no_inheritance) {
+ break;
+ }
+ check = check->inherits_ptr;
+ }
+
+ return false;
+}
+
+void ClassDB::set_method_flags(StringName p_class, StringName p_method, int p_flags) {
+ OBJTYPE_WLOCK;
+ ClassInfo *type = classes.getptr(p_class);
+ ClassInfo *check = type;
+ ERR_FAIL_COND(!check);
+ ERR_FAIL_COND(!check->method_map.has(p_method));
+ check->method_map[p_method]->set_hint_flags(p_flags);
+}
+
+bool ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inheritance) {
+ ClassInfo *type = classes.getptr(p_class);
+ ClassInfo *check = type;
+ while (check) {
+ if (check->method_map.has(p_method)) {
+ return true;
+ }
+ if (p_no_inheritance) {
+ return false;
+ }
+ check = check->inherits_ptr;
+ }
+
+ return false;
+}
+
+#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) {
+ 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) {
+ StringName mdname = StaticCString::create(method_name);
+#endif
+
+ OBJTYPE_WLOCK;
+ ERR_FAIL_COND_V(!p_bind, nullptr);
+ p_bind->set_name(mdname);
+
+ String instance_type = p_bind->get_instance_class();
+
+#ifdef DEBUG_ENABLED
+
+ ERR_FAIL_COND_V_MSG(has_method(instance_type, mdname), nullptr, "Class " + String(instance_type) + " already has a method " + String(mdname) + ".");
+#endif
+
+ ClassInfo *type = classes.getptr(instance_type);
+ if (!type) {
+ memdelete(p_bind);
+ ERR_FAIL_V_MSG(nullptr, "Couldn't bind method '" + mdname + "' for instance '" + instance_type + "'.");
+ }
+
+ if (type->method_map.has(mdname)) {
+ memdelete(p_bind);
+ // overloading not supported
+ ERR_FAIL_V_MSG(nullptr, "Method already bound '" + instance_type + "::" + mdname + "'.");
+ }
+
+#ifdef DEBUG_METHODS_ENABLED
+
+ if (method_name.args.size() > p_bind->get_argument_count()) {
+ memdelete(p_bind);
+ ERR_FAIL_V_MSG(nullptr, "Method definition provides more arguments than the method actually has '" + instance_type + "::" + mdname + "'.");
+ }
+
+ p_bind->set_argument_names(method_name.args);
+
+ type->method_order.push_back(mdname);
+#endif
+
+ type->method_map[mdname] = p_bind;
+
+ Vector<Variant> defvals;
+
+ defvals.resize(p_defcount);
+ for (int i = 0; i < p_defcount; i++) {
+ defvals.write[i] = *p_defs[i];
+ }
+
+ p_bind->set_default_arguments(defvals);
+ p_bind->set_hint_flags(p_flags);
+ return p_bind;
+}
+
+void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual) {
+ ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'.");
+
+ OBJTYPE_WLOCK;
+
+#ifdef DEBUG_METHODS_ENABLED
+ MethodInfo mi = p_method;
+ if (p_virtual) {
+ mi.flags |= METHOD_FLAG_VIRTUAL;
+ }
+ classes[p_class].virtual_methods.push_back(mi);
+ classes[p_class].virtual_methods_map[p_method.name] = mi;
+
+#endif
+}
+
+void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance) {
+ ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'.");
+
+#ifdef DEBUG_METHODS_ENABLED
+
+ ClassInfo *type = classes.getptr(p_class);
+ ClassInfo *check = type;
+ while (check) {
+ for (List<MethodInfo>::Element *E = check->virtual_methods.front(); E; E = E->next()) {
+ p_methods->push_back(E->get());
+ }
+
+ if (p_no_inheritance) {
+ return;
+ }
+ check = check->inherits_ptr;
+ }
+
+#endif
+}
+
+void ClassDB::set_class_enabled(StringName p_class, bool p_enable) {
+ OBJTYPE_WLOCK;
+
+ ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'.");
+ classes[p_class].disabled = !p_enable;
+}
+
+bool ClassDB::is_class_enabled(StringName p_class) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *ti = classes.getptr(p_class);
+ if (!ti || !ti->creation_func) {
+ if (compat_classes.has(p_class)) {
+ ti = classes.getptr(compat_classes[p_class]);
+ }
+ }
+
+ ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'.");
+ return !ti->disabled;
+}
+
+bool ClassDB::is_class_exposed(StringName p_class) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *ti = classes.getptr(p_class);
+ ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'.");
+ return ti->exposed;
+}
+
+StringName ClassDB::get_category(const StringName &p_node) {
+ ERR_FAIL_COND_V(!classes.has(p_node), StringName());
+#ifdef DEBUG_ENABLED
+ return classes[p_node].category;
+#else
+ return StringName();
+#endif
+}
+
+void ClassDB::add_resource_base_extension(const StringName &p_extension, const StringName &p_class) {
+ if (resource_base_extensions.has(p_extension)) {
+ return;
+ }
+
+ resource_base_extensions[p_extension] = p_class;
+}
+
+void ClassDB::get_resource_base_extensions(List<String> *p_extensions) {
+ const StringName *K = nullptr;
+
+ while ((K = resource_base_extensions.next(K))) {
+ p_extensions->push_back(*K);
+ }
+}
+
+void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p_extensions) {
+ const StringName *K = nullptr;
+
+ while ((K = resource_base_extensions.next(K))) {
+ StringName cmp = resource_base_extensions[*K];
+ if (is_parent_class(p_class, cmp) || is_parent_class(cmp, p_class)) {
+ p_extensions->push_back(*K);
+ }
+ }
+}
+
+HashMap<StringName, HashMap<StringName, Variant>> ClassDB::default_values;
+Set<StringName> ClassDB::default_values_cached;
+
+Variant ClassDB::class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid) {
+ if (!default_values_cached.has(p_class)) {
+ if (!default_values.has(p_class)) {
+ default_values[p_class] = HashMap<StringName, Variant>();
+ }
+
+ Object *c = nullptr;
+ bool cleanup_c = false;
+
+ if (Engine::get_singleton()->has_singleton(p_class)) {
+ c = Engine::get_singleton()->get_singleton_object(p_class);
+ cleanup_c = false;
+ } else if (ClassDB::can_instance(p_class)) {
+ c = ClassDB::instance(p_class);
+ cleanup_c = true;
+ }
+
+ if (c) {
+ List<PropertyInfo> plist;
+ c->get_property_list(&plist);
+ for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
+ if (E->get().usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR)) {
+ if (!default_values[p_class].has(E->get().name)) {
+ Variant v = c->get(E->get().name);
+ default_values[p_class][E->get().name] = v;
+ }
+ }
+ }
+
+ if (cleanup_c) {
+ memdelete(c);
+ }
+ }
+
+ default_values_cached.insert(p_class);
+ }
+
+ if (!default_values.has(p_class)) {
+ if (r_valid != nullptr) {
+ *r_valid = false;
+ }
+ return Variant();
+ }
+
+ if (!default_values[p_class].has(p_property)) {
+ if (r_valid != nullptr) {
+ *r_valid = false;
+ }
+ return Variant();
+ }
+
+ if (r_valid != nullptr) {
+ *r_valid = true;
+ }
+
+ Variant var = default_values[p_class][p_property];
+
+#ifdef DEBUG_ENABLED
+ // Some properties may have an instantiated Object as default value,
+ // (like Path2D's `curve` used to have), but that's not a good practice.
+ // Instead, those properties should use PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT
+ // to be auto-instantiated when created in the editor.
+ if (var.get_type() == Variant::OBJECT) {
+ Object *obj = var.get_validated_object();
+ if (obj) {
+ WARN_PRINT(vformat("Instantiated %s used as default value for %s's \"%s\" property.", obj->get_class(), p_class, p_property));
+ }
+ }
+#endif
+
+ return var;
+}
+
+RWLock *ClassDB::lock = nullptr;
+
+void ClassDB::init() {
+ lock = RWLock::create();
+}
+
+void ClassDB::cleanup_defaults() {
+ default_values.clear();
+ default_values_cached.clear();
+}
+
+void ClassDB::cleanup() {
+ //OBJTYPE_LOCK; hah not here
+
+ const StringName *k = nullptr;
+
+ while ((k = classes.next(k))) {
+ ClassInfo &ti = classes[*k];
+
+ const StringName *m = nullptr;
+ while ((m = ti.method_map.next(m))) {
+ memdelete(ti.method_map[*m]);
+ }
+ }
+ classes.clear();
+ resource_base_extensions.clear();
+ compat_classes.clear();
+
+ memdelete(lock);
+}
+
+//
diff --git a/core/object/class_db.h b/core/object/class_db.h
new file mode 100644
index 0000000000..94f26da60d
--- /dev/null
+++ b/core/object/class_db.h
@@ -0,0 +1,427 @@
+/*************************************************************************/
+/* class_db.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CLASS_DB_H
+#define CLASS_DB_H
+
+#include "core/object/method_bind.h"
+#include "core/object/object.h"
+#include "core/string/print_string.h"
+
+/** To bind more then 6 parameters include this:
+ *
+ */
+
+// Makes callable_mp readily available in all classes connecting signals.
+// Needs to come after method_bind and object have been included.
+#include "core/object/callable_method_pointer.h"
+
+#define DEFVAL(m_defval) (m_defval)
+
+#ifdef DEBUG_METHODS_ENABLED
+
+struct MethodDefinition {
+ StringName name;
+ Vector<StringName> args;
+ MethodDefinition() {}
+ MethodDefinition(const char *p_name) :
+ name(p_name) {}
+ MethodDefinition(const StringName &p_name) :
+ name(p_name) {}
+};
+
+MethodDefinition D_METHOD(const char *p_name);
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1);
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2);
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3);
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4);
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5);
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6);
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7);
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8);
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9);
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10);
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11);
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12);
+MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12, const char *p_arg13);
+
+#else
+
+//#define NO_VARIADIC_MACROS
+
+#ifdef NO_VARIADIC_MACROS
+
+static _FORCE_INLINE_ const char *D_METHOD(const char *m_name, ...) {
+ return m_name;
+}
+
+#else
+
+// When DEBUG_METHODS_ENABLED is set this will let the engine know
+// the argument names for easier debugging.
+#define D_METHOD(m_c, ...) m_c
+
+#endif
+
+#endif
+
+class ClassDB {
+public:
+ enum APIType {
+ API_CORE,
+ API_EDITOR,
+ API_NONE
+ };
+
+public:
+ struct PropertySetGet {
+ int index;
+ StringName setter;
+ StringName getter;
+ MethodBind *_setptr;
+ MethodBind *_getptr;
+ Variant::Type type;
+ };
+
+ struct ClassInfo {
+ APIType api = API_NONE;
+ ClassInfo *inherits_ptr = nullptr;
+ void *class_ptr = nullptr;
+
+ HashMap<StringName, MethodBind *> method_map;
+ HashMap<StringName, int> constant_map;
+ HashMap<StringName, List<StringName>> enum_map;
+ HashMap<StringName, MethodInfo> signal_map;
+ List<PropertyInfo> property_list;
+ HashMap<StringName, PropertyInfo> property_map;
+#ifdef DEBUG_METHODS_ENABLED
+ List<StringName> constant_order;
+ List<StringName> method_order;
+ Set<StringName> methods_in_properties;
+ List<MethodInfo> virtual_methods;
+ Map<StringName, MethodInfo> virtual_methods_map;
+ StringName category;
+#endif
+ HashMap<StringName, PropertySetGet> property_setget;
+
+ StringName inherits;
+ StringName name;
+ bool disabled = false;
+ bool exposed = false;
+ Object *(*creation_func)() = nullptr;
+
+ ClassInfo() {}
+ ~ClassInfo() {}
+ };
+
+ template <class T>
+ static Object *creator() {
+ return memnew(T);
+ }
+
+ static RWLock *lock;
+ static HashMap<StringName, ClassInfo> classes;
+ static HashMap<StringName, StringName> resource_base_extensions;
+ 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);
+#else
+ static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const char *method_name, const Variant **p_defs, int p_defcount);
+#endif
+
+ static APIType current_api;
+
+ static void _add_class2(const StringName &p_class, const StringName &p_inherits);
+
+ static HashMap<StringName, HashMap<StringName, Variant>> default_values;
+ static Set<StringName> default_values_cached;
+
+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);
+
+public:
+ // DO NOT USE THIS!!!!!! NEEDS TO BE PUBLIC BUT DO NOT USE NO MATTER WHAT!!!
+ template <class T>
+ static void _add_class() {
+ _add_class2(T::get_class_static(), T::get_parent_class_static());
+ }
+
+ template <class T>
+ static void register_class() {
+ GLOBAL_LOCK_FUNCTION;
+ T::initialize_class();
+ ClassInfo *t = classes.getptr(T::get_class_static());
+ ERR_FAIL_COND(!t);
+ t->creation_func = &creator<T>;
+ t->exposed = true;
+ t->class_ptr = T::get_class_ptr_static();
+ T::register_custom_data_to_otdb();
+ }
+
+ template <class T>
+ static void register_virtual_class() {
+ GLOBAL_LOCK_FUNCTION;
+ T::initialize_class();
+ ClassInfo *t = classes.getptr(T::get_class_static());
+ ERR_FAIL_COND(!t);
+ t->exposed = true;
+ t->class_ptr = T::get_class_ptr_static();
+ //nothing
+ }
+
+ template <class T>
+ static Object *_create_ptr_func() {
+ return T::create();
+ }
+
+ template <class T>
+ static void register_custom_instance_class() {
+ GLOBAL_LOCK_FUNCTION;
+ T::initialize_class();
+ ClassInfo *t = classes.getptr(T::get_class_static());
+ ERR_FAIL_COND(!t);
+ t->creation_func = &_create_ptr_func<T>;
+ t->exposed = true;
+ t->class_ptr = T::get_class_ptr_static();
+ T::register_custom_data_to_otdb();
+ }
+
+ static void get_class_list(List<StringName> *p_classes);
+ static void get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes);
+ static void get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes);
+ static StringName get_parent_class_nocheck(const StringName &p_class);
+ static StringName get_parent_class(const StringName &p_class);
+ static StringName get_compatibility_remapped_class(const StringName &p_class);
+ static bool class_exists(const StringName &p_class);
+ static bool is_parent_class(const StringName &p_class, const StringName &p_inherits);
+ static bool can_instance(const StringName &p_class);
+ static Object *instance(const StringName &p_class);
+ static APIType get_api_type(const StringName &p_class);
+
+ static uint64_t get_api_hash(APIType p_api);
+
+ template <class N, class M>
+ static MethodBind *bind_method(N p_method_name, M p_method) {
+ MethodBind *bind = create_method_bind(p_method);
+
+ return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, nullptr, 0); //use static function, much smaller binary usage
+ }
+
+ template <class N, class M>
+ static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1) {
+ MethodBind *bind = create_method_bind(p_method);
+ const Variant *ptr[1] = { &p_def1 };
+
+ return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 1);
+ }
+
+ template <class N, class M>
+ static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2) {
+ MethodBind *bind = create_method_bind(p_method);
+ const Variant *ptr[2] = { &p_def1, &p_def2 };
+
+ return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 2);
+ }
+
+ template <class N, class M>
+ static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3) {
+ MethodBind *bind = create_method_bind(p_method);
+ const Variant *ptr[3] = { &p_def1, &p_def2, &p_def3 };
+
+ return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 3);
+ }
+
+ template <class N, class M>
+ static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4) {
+ MethodBind *bind = create_method_bind(p_method);
+ const Variant *ptr[4] = { &p_def1, &p_def2, &p_def3, &p_def4 };
+
+ return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 4);
+ }
+
+ template <class N, class M>
+ static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4, const Variant &p_def5) {
+ MethodBind *bind = create_method_bind(p_method);
+ const Variant *ptr[5] = { &p_def1, &p_def2, &p_def3, &p_def4, &p_def5 };
+
+ return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 5);
+ }
+
+ template <class N, class M>
+ static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4, const Variant &p_def5, const Variant &p_def6) {
+ MethodBind *bind = create_method_bind(p_method);
+ const Variant *ptr[6] = { &p_def1, &p_def2, &p_def3, &p_def4, &p_def5, &p_def6 };
+
+ return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 6);
+ }
+
+ template <class N, class M>
+ static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4, const Variant &p_def5, const Variant &p_def6, const Variant &p_def7) {
+ MethodBind *bind = create_method_bind(p_method);
+ const Variant *ptr[7] = { &p_def1, &p_def2, &p_def3, &p_def4, &p_def5, &p_def6, &p_def7 };
+
+ return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 7);
+ }
+
+ template <class N, class M>
+ static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4, const Variant &p_def5, const Variant &p_def6, const Variant &p_def7, const Variant &p_def8) {
+ MethodBind *bind = create_method_bind(p_method);
+ const Variant *ptr[8] = { &p_def1, &p_def2, &p_def3, &p_def4, &p_def5, &p_def6, &p_def7, &p_def8 };
+
+ return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 8);
+ }
+
+ template <class M>
+ static MethodBind *bind_vararg_method(uint32_t p_flags, 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;
+
+ 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);
+
+ String instance_type = bind->get_instance_class();
+
+ ClassInfo *type = classes.getptr(instance_type);
+ if (!type) {
+ memdelete(bind);
+ ERR_FAIL_COND_V(!type, 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 + ".");
+ }
+ 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;
+ }
+
+ static void add_signal(StringName p_class, const MethodInfo &p_signal);
+ static bool has_signal(StringName p_class, StringName p_signal, bool p_no_inheritance = false);
+ static bool get_signal(StringName p_class, StringName p_signal, MethodInfo *r_signal);
+ static void get_signal_list(StringName p_class, List<MethodInfo> *p_signals, bool p_no_inheritance = false);
+
+ static void add_property_group(StringName p_class, const String &p_name, const String &p_prefix = "");
+ static void add_property_subgroup(StringName p_class, const String &p_name, const String &p_prefix = "");
+ static void add_property(StringName p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index = -1);
+ static void set_property_default_value(StringName p_class, const StringName &p_name, const Variant &p_default);
+ static void get_property_list(StringName p_class, List<PropertyInfo> *p_list, bool p_no_inheritance = false, const Object *p_validator = nullptr);
+ static bool get_property_info(StringName p_class, StringName p_property, PropertyInfo *r_info, bool p_no_inheritance = false, const Object *p_validator = nullptr);
+ static bool set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid = nullptr);
+ static bool get_property(Object *p_object, const StringName &p_property, Variant &r_value);
+ static bool has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance = false);
+ static int get_property_index(const StringName &p_class, const StringName &p_property, bool *r_is_valid = nullptr);
+ static Variant::Type get_property_type(const StringName &p_class, const StringName &p_property, bool *r_is_valid = nullptr);
+ static StringName get_property_setter(StringName p_class, const StringName &p_property);
+ static StringName get_property_getter(StringName p_class, const StringName &p_property);
+
+ static bool has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false);
+ static void set_method_flags(StringName p_class, StringName p_method, int p_flags);
+
+ static void get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
+ static bool get_method_info(StringName p_class, StringName p_method, MethodInfo *r_info, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
+ static MethodBind *get_method(StringName p_class, StringName p_name);
+
+ static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true);
+ static void get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false);
+
+ static void bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int p_constant);
+ static void get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance = false);
+ static int get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success = nullptr);
+ static bool has_integer_constant(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
+
+ static StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
+ static void get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance = false);
+ static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance = false);
+ static bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
+
+ static Variant class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid = nullptr);
+
+ static StringName get_category(const StringName &p_node);
+
+ static void set_class_enabled(StringName p_class, bool p_enable);
+ static bool is_class_enabled(StringName p_class);
+
+ static bool is_class_exposed(StringName p_class);
+
+ static void add_resource_base_extension(const StringName &p_extension, const StringName &p_class);
+ static void get_resource_base_extensions(List<String> *p_extensions);
+ static void get_extensions_for_type(const StringName &p_class, List<String> *p_extensions);
+
+ static void add_compatibility_class(const StringName &p_class, const StringName &p_fallback);
+ static void init();
+
+ static void set_current_api(APIType p_api);
+ static APIType get_current_api();
+ static void cleanup_defaults();
+ static void cleanup();
+};
+
+#ifdef DEBUG_METHODS_ENABLED
+
+#define BIND_CONSTANT(m_constant) \
+ ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant);
+
+#define BIND_ENUM_CONSTANT(m_constant) \
+ ClassDB::bind_integer_constant(get_class_static(), __constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant);
+
+#else
+
+#define BIND_CONSTANT(m_constant) \
+ ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant);
+
+#define BIND_ENUM_CONSTANT(m_constant) \
+ ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant);
+
+#endif
+
+#ifdef TOOLS_ENABLED
+
+#define BIND_VMETHOD(m_method) \
+ ClassDB::add_virtual_method(get_class_static(), m_method);
+
+#else
+
+#define BIND_VMETHOD(m_method)
+
+#endif
+
+#endif // CLASS_DB_H
diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp
new file mode 100644
index 0000000000..f0d6786853
--- /dev/null
+++ b/core/object/message_queue.cpp
@@ -0,0 +1,374 @@
+/*************************************************************************/
+/* message_queue.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "message_queue.h"
+
+#include "core/config/project_settings.h"
+#include "core/core_string_names.h"
+#include "core/object/script_language.h"
+
+MessageQueue *MessageQueue::singleton = nullptr;
+
+MessageQueue *MessageQueue::get_singleton() {
+ return singleton;
+}
+
+Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) {
+ return push_callable(Callable(p_id, p_method), p_args, p_argcount, p_show_error);
+}
+
+Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
+ VARIANT_ARGPTRS;
+
+ int argc = 0;
+
+ for (int i = 0; i < VARIANT_ARG_MAX; i++) {
+ if (argptr[i]->get_type() == Variant::NIL) {
+ break;
+ }
+ argc++;
+ }
+
+ return push_call(p_id, p_method, argptr, argc, false);
+}
+
+Error MessageQueue::push_set(ObjectID p_id, const StringName &p_prop, const Variant &p_value) {
+ _THREAD_SAFE_METHOD_
+
+ uint8_t room_needed = sizeof(Message) + sizeof(Variant);
+
+ if ((buffer_end + room_needed) >= buffer_size) {
+ String type;
+ if (ObjectDB::get_instance(p_id)) {
+ type = ObjectDB::get_instance(p_id)->get_class();
+ }
+ print_line("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id));
+ statistics();
+ ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_kb' in project settings.");
+ }
+
+ Message *msg = memnew_placement(&buffer[buffer_end], Message);
+ msg->args = 1;
+ msg->callable = Callable(p_id, p_prop);
+ msg->type = TYPE_SET;
+
+ buffer_end += sizeof(Message);
+
+ Variant *v = memnew_placement(&buffer[buffer_end], Variant);
+ buffer_end += sizeof(Variant);
+ *v = p_value;
+
+ return OK;
+}
+
+Error MessageQueue::push_notification(ObjectID p_id, int p_notification) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(p_notification < 0, ERR_INVALID_PARAMETER);
+
+ uint8_t room_needed = sizeof(Message);
+
+ if ((buffer_end + room_needed) >= buffer_size) {
+ print_line("Failed notification: " + itos(p_notification) + " target ID: " + itos(p_id));
+ statistics();
+ ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_kb' in project settings.");
+ }
+
+ Message *msg = memnew_placement(&buffer[buffer_end], Message);
+
+ msg->type = TYPE_NOTIFICATION;
+ msg->callable = Callable(p_id, CoreStringNames::get_singleton()->notification); //name is meaningless but callable needs it
+ //msg->target;
+ msg->notification = p_notification;
+
+ buffer_end += sizeof(Message);
+
+ return OK;
+}
+
+Error MessageQueue::push_call(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) {
+ return push_call(p_object->get_instance_id(), p_method, VARIANT_ARG_PASS);
+}
+
+Error MessageQueue::push_notification(Object *p_object, int p_notification) {
+ return push_notification(p_object->get_instance_id(), p_notification);
+}
+
+Error MessageQueue::push_set(Object *p_object, const StringName &p_prop, const Variant &p_value) {
+ return push_set(p_object->get_instance_id(), p_prop, p_value);
+}
+
+Error MessageQueue::push_callable(const Callable &p_callable, const Variant **p_args, int p_argcount, bool p_show_error) {
+ _THREAD_SAFE_METHOD_
+
+ int room_needed = sizeof(Message) + sizeof(Variant) * p_argcount;
+
+ if ((buffer_end + room_needed) >= buffer_size) {
+ print_line("Failed method: " + p_callable);
+ statistics();
+ ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_kb' in project settings.");
+ }
+
+ Message *msg = memnew_placement(&buffer[buffer_end], Message);
+ msg->args = p_argcount;
+ msg->callable = p_callable;
+ msg->type = TYPE_CALL;
+ if (p_show_error) {
+ msg->type |= FLAG_SHOW_ERROR;
+ }
+
+ buffer_end += sizeof(Message);
+
+ for (int i = 0; i < p_argcount; i++) {
+ Variant *v = memnew_placement(&buffer[buffer_end], Variant);
+ buffer_end += sizeof(Variant);
+ *v = *p_args[i];
+ }
+
+ return OK;
+}
+
+Error MessageQueue::push_callable(const Callable &p_callable, VARIANT_ARG_DECLARE) {
+ VARIANT_ARGPTRS;
+
+ int argc = 0;
+
+ for (int i = 0; i < VARIANT_ARG_MAX; i++) {
+ if (argptr[i]->get_type() == Variant::NIL) {
+ break;
+ }
+ argc++;
+ }
+
+ return push_callable(p_callable, argptr, argc);
+}
+
+void MessageQueue::statistics() {
+ Map<StringName, int> set_count;
+ Map<int, int> notify_count;
+ Map<Callable, int> call_count;
+ int null_count = 0;
+
+ uint32_t read_pos = 0;
+ while (read_pos < buffer_end) {
+ Message *message = (Message *)&buffer[read_pos];
+
+ Object *target = message->callable.get_object();
+
+ if (target != nullptr) {
+ switch (message->type & FLAG_MASK) {
+ case TYPE_CALL: {
+ if (!call_count.has(message->callable)) {
+ call_count[message->callable] = 0;
+ }
+
+ call_count[message->callable]++;
+
+ } break;
+ case TYPE_NOTIFICATION: {
+ if (!notify_count.has(message->notification)) {
+ notify_count[message->notification] = 0;
+ }
+
+ notify_count[message->notification]++;
+
+ } break;
+ case TYPE_SET: {
+ StringName t = message->callable.get_method();
+ if (!set_count.has(t)) {
+ set_count[t] = 0;
+ }
+
+ set_count[t]++;
+
+ } break;
+ }
+
+ } else {
+ //object was deleted
+ print_line("Object was deleted while awaiting a callback");
+
+ null_count++;
+ }
+
+ read_pos += sizeof(Message);
+ if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) {
+ read_pos += sizeof(Variant) * message->args;
+ }
+ }
+
+ print_line("TOTAL BYTES: " + itos(buffer_end));
+ print_line("NULL count: " + itos(null_count));
+
+ for (Map<StringName, int>::Element *E = set_count.front(); E; E = E->next()) {
+ print_line("SET " + E->key() + ": " + itos(E->get()));
+ }
+
+ for (Map<Callable, int>::Element *E = call_count.front(); E; E = E->next()) {
+ print_line("CALL " + E->key() + ": " + itos(E->get()));
+ }
+
+ for (Map<int, int>::Element *E = notify_count.front(); E; E = E->next()) {
+ print_line("NOTIFY " + itos(E->key()) + ": " + itos(E->get()));
+ }
+}
+
+int MessageQueue::get_max_buffer_usage() const {
+ return buffer_max_used;
+}
+
+void MessageQueue::_call_function(const Callable &p_callable, const Variant *p_args, int p_argcount, bool p_show_error) {
+ const Variant **argptrs = nullptr;
+ if (p_argcount) {
+ argptrs = (const Variant **)alloca(sizeof(Variant *) * p_argcount);
+ for (int i = 0; i < p_argcount; i++) {
+ argptrs[i] = &p_args[i];
+ }
+ }
+
+ Callable::CallError ce;
+ Variant ret;
+ p_callable.call(argptrs, p_argcount, ret, ce);
+ if (p_show_error && ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT("Error calling deferred method: " + Variant::get_callable_error_text(p_callable, argptrs, p_argcount, ce) + ".");
+ }
+}
+
+void MessageQueue::flush() {
+ if (buffer_end > buffer_max_used) {
+ buffer_max_used = buffer_end;
+ }
+
+ uint32_t read_pos = 0;
+
+ //using reverse locking strategy
+ _THREAD_SAFE_LOCK_
+
+ if (flushing) {
+ _THREAD_SAFE_UNLOCK_
+ ERR_FAIL_COND(flushing); //already flushing, you did something odd
+ }
+ flushing = true;
+
+ while (read_pos < buffer_end) {
+ //lock on each iteration, so a call can re-add itself to the message queue
+
+ Message *message = (Message *)&buffer[read_pos];
+
+ uint32_t advance = sizeof(Message);
+ if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) {
+ advance += sizeof(Variant) * message->args;
+ }
+
+ //pre-advance so this function is reentrant
+ read_pos += advance;
+
+ _THREAD_SAFE_UNLOCK_
+
+ Object *target = message->callable.get_object();
+
+ if (target != nullptr) {
+ switch (message->type & FLAG_MASK) {
+ case TYPE_CALL: {
+ Variant *args = (Variant *)(message + 1);
+
+ // messages don't expect a return value
+
+ _call_function(message->callable, args, message->args, message->type & FLAG_SHOW_ERROR);
+
+ } break;
+ case TYPE_NOTIFICATION: {
+ // messages don't expect a return value
+ target->notification(message->notification);
+
+ } break;
+ case TYPE_SET: {
+ Variant *arg = (Variant *)(message + 1);
+ // messages don't expect a return value
+ target->set(message->callable.get_method(), *arg);
+
+ } break;
+ }
+ }
+
+ if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) {
+ Variant *args = (Variant *)(message + 1);
+ for (int i = 0; i < message->args; i++) {
+ args[i].~Variant();
+ }
+ }
+
+ message->~Message();
+
+ _THREAD_SAFE_LOCK_
+ }
+
+ buffer_end = 0; // reset buffer
+ flushing = false;
+ _THREAD_SAFE_UNLOCK_
+}
+
+bool MessageQueue::is_flushing() const {
+ return flushing;
+}
+
+MessageQueue::MessageQueue() {
+ ERR_FAIL_COND_MSG(singleton != nullptr, "A MessageQueue singleton already exists.");
+ singleton = this;
+
+ buffer_size = GLOBAL_DEF_RST("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB);
+ ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/message_queue/max_size_kb", PropertyInfo(Variant::INT, "memory/limits/message_queue/max_size_kb", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater"));
+ buffer_size *= 1024;
+ buffer = memnew_arr(uint8_t, buffer_size);
+}
+
+MessageQueue::~MessageQueue() {
+ uint32_t read_pos = 0;
+
+ while (read_pos < buffer_end) {
+ Message *message = (Message *)&buffer[read_pos];
+ Variant *args = (Variant *)(message + 1);
+ int argc = message->args;
+ if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) {
+ for (int i = 0; i < argc; i++) {
+ args[i].~Variant();
+ }
+ }
+ message->~Message();
+
+ read_pos += sizeof(Message);
+ if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) {
+ read_pos += sizeof(Variant) * message->args;
+ }
+ }
+
+ singleton = nullptr;
+ memdelete_arr(buffer);
+}
diff --git a/core/object/message_queue.h b/core/object/message_queue.h
new file mode 100644
index 0000000000..2901ab196a
--- /dev/null
+++ b/core/object/message_queue.h
@@ -0,0 +1,98 @@
+/*************************************************************************/
+/* message_queue.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MESSAGE_QUEUE_H
+#define MESSAGE_QUEUE_H
+
+#include "core/object/class_db.h"
+#include "core/os/thread_safe.h"
+
+class MessageQueue {
+ _THREAD_SAFE_CLASS_
+
+ enum {
+ DEFAULT_QUEUE_SIZE_KB = 4096
+ };
+
+ enum {
+ TYPE_CALL,
+ TYPE_NOTIFICATION,
+ TYPE_SET,
+ FLAG_SHOW_ERROR = 1 << 14,
+ FLAG_MASK = FLAG_SHOW_ERROR - 1
+
+ };
+
+ struct Message {
+ Callable callable;
+ int16_t type;
+ union {
+ int16_t notification;
+ int16_t args;
+ };
+ };
+
+ uint8_t *buffer;
+ uint32_t buffer_end = 0;
+ uint32_t buffer_max_used = 0;
+ uint32_t buffer_size;
+
+ void _call_function(const Callable &p_callable, const Variant *p_args, int p_argcount, bool p_show_error);
+
+ static MessageQueue *singleton;
+
+ bool flushing = false;
+
+public:
+ static MessageQueue *get_singleton();
+
+ Error push_call(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false);
+ Error push_call(ObjectID p_id, const StringName &p_method, VARIANT_ARG_LIST);
+ Error push_notification(ObjectID p_id, int p_notification);
+ Error push_set(ObjectID p_id, const StringName &p_prop, const Variant &p_value);
+ Error push_callable(const Callable &p_callable, const Variant **p_args, int p_argcount, bool p_show_error = false);
+ Error push_callable(const Callable &p_callable, VARIANT_ARG_LIST);
+
+ Error push_call(Object *p_object, const StringName &p_method, VARIANT_ARG_LIST);
+ Error push_notification(Object *p_object, int p_notification);
+ Error push_set(Object *p_object, const StringName &p_prop, const Variant &p_value);
+
+ void statistics();
+ void flush();
+
+ bool is_flushing() const;
+
+ int get_max_buffer_usage() const;
+
+ MessageQueue();
+ ~MessageQueue();
+};
+
+#endif // MESSAGE_QUEUE_H
diff --git a/core/object/method_bind.cpp b/core/object/method_bind.cpp
new file mode 100644
index 0000000000..e6652ac09f
--- /dev/null
+++ b/core/object/method_bind.cpp
@@ -0,0 +1,110 @@
+/*************************************************************************/
+/* method_bind.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+// object.h needs to be the first include *before* method_bind.h
+// FIXME: Find out why and fix potential cyclical dependencies.
+#include "core/object/object.h"
+
+#include "method_bind.h"
+
+#ifdef DEBUG_METHODS_ENABLED
+PropertyInfo MethodBind::get_argument_info(int p_argument) const {
+ ERR_FAIL_INDEX_V(p_argument, get_argument_count(), PropertyInfo());
+
+ PropertyInfo info = _gen_argument_type_info(p_argument);
+ info.name = p_argument < arg_names.size() ? String(arg_names[p_argument]) : String("arg" + itos(p_argument));
+ return info;
+}
+
+PropertyInfo MethodBind::get_return_info() const {
+ return _gen_argument_type_info(-1);
+}
+
+#endif
+void MethodBind::_set_const(bool p_const) {
+ _const = p_const;
+}
+
+void MethodBind::_set_returns(bool p_returns) {
+ _returns = p_returns;
+}
+
+StringName MethodBind::get_name() const {
+ return name;
+}
+
+void MethodBind::set_name(const StringName &p_name) {
+ name = p_name;
+}
+
+#ifdef DEBUG_METHODS_ENABLED
+void MethodBind::set_argument_names(const Vector<StringName> &p_names) {
+ arg_names = p_names;
+}
+
+Vector<StringName> MethodBind::get_argument_names() const {
+ return arg_names;
+}
+
+#endif
+
+void MethodBind::set_default_arguments(const Vector<Variant> &p_defargs) {
+ default_arguments = p_defargs;
+ default_argument_count = default_arguments.size();
+}
+
+#ifdef DEBUG_METHODS_ENABLED
+void MethodBind::_generate_argument_types(int p_count) {
+ set_argument_count(p_count);
+
+ Variant::Type *argt = memnew_arr(Variant::Type, p_count + 1);
+ argt[0] = _gen_argument_type(-1); // return type
+
+ for (int i = 0; i < p_count; i++) {
+ argt[i + 1] = _gen_argument_type(i);
+ }
+
+ argument_types = argt;
+}
+
+#endif
+
+MethodBind::MethodBind() {
+ static int last_id = 0;
+ method_id = last_id++;
+}
+
+MethodBind::~MethodBind() {
+#ifdef DEBUG_METHODS_ENABLED
+ if (argument_types) {
+ memdelete_arr(argument_types);
+ }
+#endif
+}
diff --git a/core/object/method_bind.h b/core/object/method_bind.h
new file mode 100644
index 0000000000..ab4ba90b94
--- /dev/null
+++ b/core/object/method_bind.h
@@ -0,0 +1,591 @@
+/*************************************************************************/
+/* method_bind.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef METHOD_BIND_H
+#define METHOD_BIND_H
+
+#include "core/variant/binder_common.h"
+
+enum MethodFlags {
+
+ METHOD_FLAG_NORMAL = 1,
+ METHOD_FLAG_EDITOR = 2,
+ METHOD_FLAG_NOSCRIPT = 4,
+ METHOD_FLAG_CONST = 8,
+ METHOD_FLAG_REVERSE = 16, // used for events
+ METHOD_FLAG_VIRTUAL = 32,
+ METHOD_FLAG_FROM_SCRIPT = 64,
+ METHOD_FLAG_VARARG = 128,
+ METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL,
+};
+
+VARIANT_ENUM_CAST(MethodFlags)
+
+// some helpers
+
+class MethodBind {
+ int method_id;
+ uint32_t hint_flags = METHOD_FLAGS_DEFAULT;
+ StringName name;
+ StringName instance_class;
+ Vector<Variant> default_arguments;
+ int default_argument_count = 0;
+ int argument_count = 0;
+
+ bool _const = false;
+ bool _returns = false;
+
+protected:
+#ifdef DEBUG_METHODS_ENABLED
+ Variant::Type *argument_types = nullptr;
+ Vector<StringName> arg_names;
+#endif
+ void _set_const(bool p_const);
+ void _set_returns(bool p_returns);
+#ifdef DEBUG_METHODS_ENABLED
+ virtual Variant::Type _gen_argument_type(int p_arg) const = 0;
+ virtual PropertyInfo _gen_argument_type_info(int p_arg) const = 0;
+ void _generate_argument_types(int p_count);
+
+#endif
+ void set_argument_count(int p_count) { argument_count = p_count; }
+
+public:
+ _FORCE_INLINE_ const Vector<Variant> &get_default_arguments() const { return default_arguments; }
+ _FORCE_INLINE_ int get_default_argument_count() const { return default_argument_count; }
+
+ _FORCE_INLINE_ Variant has_default_argument(int p_arg) const {
+ int idx = p_arg - (argument_count - default_arguments.size());
+
+ if (idx < 0 || idx >= default_arguments.size()) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ _FORCE_INLINE_ Variant get_default_argument(int p_arg) const {
+ int idx = p_arg - (argument_count - default_arguments.size());
+
+ if (idx < 0 || idx >= default_arguments.size()) {
+ return Variant();
+ } else {
+ return default_arguments[idx];
+ }
+ }
+
+#ifdef DEBUG_METHODS_ENABLED
+ _FORCE_INLINE_ Variant::Type get_argument_type(int p_argument) const {
+ ERR_FAIL_COND_V(p_argument < -1 || p_argument > argument_count, Variant::NIL);
+ return argument_types[p_argument + 1];
+ }
+
+ PropertyInfo get_argument_info(int p_argument) const;
+ PropertyInfo get_return_info() const;
+
+ void set_argument_names(const Vector<StringName> &p_names); // Set by ClassDB, can't be inferred otherwise.
+ Vector<StringName> get_argument_names() const;
+
+ virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const = 0;
+#endif
+
+ void set_hint_flags(uint32_t p_hint) { hint_flags = p_hint; }
+ uint32_t get_hint_flags() const { return hint_flags | (is_const() ? METHOD_FLAG_CONST : 0) | (is_vararg() ? METHOD_FLAG_VARARG : 0); }
+ _FORCE_INLINE_ StringName get_instance_class() const { return instance_class; }
+ _FORCE_INLINE_ void set_instance_class(const StringName &p_class) { instance_class = p_class; }
+
+ _FORCE_INLINE_ int get_argument_count() const { return argument_count; };
+
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) = 0;
+
+#ifdef PTRCALL_ENABLED
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) = 0;
+#endif
+
+ StringName get_name() const;
+ void set_name(const StringName &p_name);
+ _FORCE_INLINE_ int get_method_id() const { return method_id; }
+ _FORCE_INLINE_ bool is_const() const { return _const; }
+ _FORCE_INLINE_ bool has_return() const { return _returns; }
+ virtual bool is_vararg() const { return false; }
+
+ void set_default_arguments(const Vector<Variant> &p_defargs);
+
+ MethodBind();
+ virtual ~MethodBind();
+};
+
+template <class T>
+class MethodBindVarArg : public MethodBind {
+public:
+ typedef Variant (T::*NativeCall)(const Variant **, int, Callable::CallError &);
+
+protected:
+ NativeCall call_method = nullptr;
+#ifdef DEBUG_METHODS_ENABLED
+ MethodInfo arguments;
+#endif
+
+public:
+#ifdef DEBUG_METHODS_ENABLED
+ virtual PropertyInfo _gen_argument_type_info(int p_arg) const {
+ if (p_arg < 0) {
+ return arguments.return_val;
+ } else if (p_arg < arguments.arguments.size()) {
+ return arguments.arguments[p_arg];
+ } else {
+ return PropertyInfo(Variant::NIL, "arg_" + itos(p_arg), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT);
+ }
+ }
+
+ virtual Variant::Type _gen_argument_type(int p_arg) const {
+ return _gen_argument_type_info(p_arg).type;
+ }
+
+ virtual GodotTypeInfo::Metadata get_argument_meta(int) const {
+ return GodotTypeInfo::METADATA_NONE;
+ }
+#else
+ virtual Variant::Type _gen_argument_type(int p_arg) const {
+ return Variant::NIL;
+ }
+#endif
+
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ T *instance = static_cast<T *>(p_object);
+ return (instance->*call_method)(p_args, p_arg_count, r_error);
+ }
+
+ void set_method_info(const MethodInfo &p_info, bool p_return_nil_is_variant) {
+ set_argument_count(p_info.arguments.size());
+#ifdef DEBUG_METHODS_ENABLED
+ Variant::Type *at = memnew_arr(Variant::Type, p_info.arguments.size() + 1);
+ at[0] = p_info.return_val.type;
+ if (p_info.arguments.size()) {
+ Vector<StringName> names;
+ names.resize(p_info.arguments.size());
+ for (int i = 0; i < p_info.arguments.size(); i++) {
+ at[i + 1] = p_info.arguments[i].type;
+ names.write[i] = p_info.arguments[i].name;
+ }
+
+ set_argument_names(names);
+ }
+ argument_types = at;
+ arguments = p_info;
+ if (p_return_nil_is_variant) {
+ arguments.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+#endif
+ }
+
+#ifdef PTRCALL_ENABLED
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) {
+ ERR_FAIL(); //can't call
+ } //todo
+#endif
+
+ void set_method(NativeCall p_method) { call_method = p_method; }
+ virtual bool is_const() const { return false; }
+
+ virtual bool is_vararg() const { return true; }
+
+ MethodBindVarArg() {
+ _set_returns(true);
+ }
+};
+
+template <class T>
+MethodBind *create_vararg_method_bind(Variant (T::*p_method)(const Variant **, int, Callable::CallError &), const MethodInfo &p_info, bool p_return_nil_is_variant) {
+ MethodBindVarArg<T> *a = memnew((MethodBindVarArg<T>));
+ a->set_method(p_method);
+ a->set_method_info(p_info, p_return_nil_is_variant);
+ a->set_instance_class(T::get_class_static());
+ return a;
+}
+
+/**** VARIADIC TEMPLATES ****/
+
+#ifndef TYPED_METHOD_BIND
+class __UnexistingClass;
+#define MB_T __UnexistingClass
+#else
+#define MB_T T
+#endif
+
+// no return, not const
+#ifdef TYPED_METHOD_BIND
+template <class T, class... P>
+#else
+template <class... P>
+#endif
+class MethodBindT : public MethodBind {
+ void (MB_T::*method)(P...);
+
+protected:
+#ifdef DEBUG_METHODS_ENABLED
+// GCC raises warnings in the case P = {} as the comparison is always false...
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wlogical-op"
+#endif
+ virtual Variant::Type _gen_argument_type(int p_arg) const {
+ if (p_arg >= 0 && p_arg < (int)sizeof...(P)) {
+ return call_get_argument_type<P...>(p_arg);
+ } else {
+ return Variant::NIL;
+ }
+ }
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+ virtual PropertyInfo _gen_argument_type_info(int p_arg) const {
+ PropertyInfo pi;
+ call_get_argument_type_info<P...>(p_arg, pi);
+ return pi;
+ }
+#endif
+
+public:
+#ifdef DEBUG_METHODS_ENABLED
+ virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const {
+ return call_get_argument_metadata<P...>(p_arg);
+ }
+
+#endif
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+#ifdef TYPED_METHOD_BIND
+ call_with_variant_args_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments());
+#else
+ call_with_variant_args_dv((MB_T *)(p_object), method, p_args, p_arg_count, r_error, get_default_arguments());
+#endif
+ return Variant();
+ }
+
+#ifdef PTRCALL_ENABLED
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) {
+#ifdef TYPED_METHOD_BIND
+ call_with_ptr_args<T, P...>(static_cast<T *>(p_object), method, p_args);
+#else
+ call_with_ptr_args<MB_T, P...>((MB_T *)(p_object), method, p_args);
+#endif
+ }
+#endif
+
+ MethodBindT(void (MB_T::*p_method)(P...)) {
+ method = p_method;
+#ifdef DEBUG_METHODS_ENABLED
+ _generate_argument_types(sizeof...(P));
+#endif
+ set_argument_count(sizeof...(P));
+ }
+};
+
+template <class T, class... P>
+MethodBind *create_method_bind(void (T::*p_method)(P...)) {
+#ifdef TYPED_METHOD_BIND
+ MethodBind *a = memnew((MethodBindT<T, P...>)(p_method));
+#else
+ MethodBind *a = memnew((MethodBindT<P...>)(reinterpret_cast<void (MB_T::*)(P...)>(p_method)));
+#endif
+ a->set_instance_class(T::get_class_static());
+ return a;
+}
+
+// no return, not const
+
+#ifdef TYPED_METHOD_BIND
+template <class T, class... P>
+#else
+template <class... P>
+#endif
+class MethodBindTC : public MethodBind {
+ void (MB_T::*method)(P...) const;
+
+protected:
+#ifdef DEBUG_METHODS_ENABLED
+// GCC raises warnings in the case P = {} as the comparison is always false...
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wlogical-op"
+#endif
+ virtual Variant::Type _gen_argument_type(int p_arg) const {
+ if (p_arg >= 0 && p_arg < (int)sizeof...(P)) {
+ return call_get_argument_type<P...>(p_arg);
+ } else {
+ return Variant::NIL;
+ }
+ }
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+ virtual PropertyInfo _gen_argument_type_info(int p_arg) const {
+ PropertyInfo pi;
+ call_get_argument_type_info<P...>(p_arg, pi);
+ return pi;
+ }
+#endif
+
+public:
+#ifdef DEBUG_METHODS_ENABLED
+ virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const {
+ return call_get_argument_metadata<P...>(p_arg);
+ }
+
+#endif
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+#ifdef TYPED_METHOD_BIND
+ call_with_variant_argsc_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments());
+#else
+ call_with_variant_argsc_dv((MB_T *)(p_object), method, p_args, p_arg_count, r_error, get_default_arguments());
+#endif
+ return Variant();
+ }
+
+#ifdef PTRCALL_ENABLED
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) {
+#ifdef TYPED_METHOD_BIND
+ call_with_ptr_argsc<T, P...>(static_cast<T *>(p_object), method, p_args);
+#else
+ call_with_ptr_argsc<MB_T, P...>((MB_T *)(p_object), method, p_args);
+#endif
+ }
+#endif
+
+ MethodBindTC(void (MB_T::*p_method)(P...) const) {
+ method = p_method;
+ _set_const(true);
+#ifdef DEBUG_METHODS_ENABLED
+ _generate_argument_types(sizeof...(P));
+#endif
+ set_argument_count(sizeof...(P));
+ }
+};
+
+template <class T, class... P>
+MethodBind *create_method_bind(void (T::*p_method)(P...) const) {
+#ifdef TYPED_METHOD_BIND
+ MethodBind *a = memnew((MethodBindTC<T, P...>)(p_method));
+#else
+ MethodBind *a = memnew((MethodBindTC<P...>)(reinterpret_cast<void (MB_T::*)(P...) const>(p_method)));
+#endif
+ a->set_instance_class(T::get_class_static());
+ return a;
+}
+
+// return, not const
+
+#ifdef TYPED_METHOD_BIND
+template <class T, class R, class... P>
+#else
+template <class R, class... P>
+#endif
+class MethodBindTR : public MethodBind {
+ R(MB_T::*method)
+ (P...);
+
+protected:
+#ifdef DEBUG_METHODS_ENABLED
+// GCC raises warnings in the case P = {} as the comparison is always false...
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wlogical-op"
+#endif
+ virtual Variant::Type _gen_argument_type(int p_arg) const {
+ if (p_arg >= 0 && p_arg < (int)sizeof...(P)) {
+ return call_get_argument_type<P...>(p_arg);
+ } else {
+ return GetTypeInfo<R>::VARIANT_TYPE;
+ }
+ }
+
+ virtual PropertyInfo _gen_argument_type_info(int p_arg) const {
+ if (p_arg >= 0 && p_arg < (int)sizeof...(P)) {
+ PropertyInfo pi;
+ call_get_argument_type_info<P...>(p_arg, pi);
+ return pi;
+ } else {
+ return GetTypeInfo<R>::get_class_info();
+ }
+ }
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+#endif
+
+public:
+#ifdef DEBUG_METHODS_ENABLED
+ virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const {
+ if (p_arg >= 0) {
+ return call_get_argument_metadata<P...>(p_arg);
+ } else {
+ return GetTypeInfo<R>::METADATA;
+ }
+ }
+#endif
+
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ Variant ret;
+#ifdef TYPED_METHOD_BIND
+ call_with_variant_args_ret_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments());
+#else
+ call_with_variant_args_ret_dv((MB_T *)p_object, method, p_args, p_arg_count, ret, r_error, get_default_arguments());
+#endif
+ return ret;
+ }
+
+#ifdef PTRCALL_ENABLED
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) {
+#ifdef TYPED_METHOD_BIND
+ call_with_ptr_args_ret<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret);
+#else
+ call_with_ptr_args_ret<MB_T, R, P...>((MB_T *)(p_object), method, p_args, r_ret);
+#endif
+ }
+#endif
+
+ MethodBindTR(R (MB_T::*p_method)(P...)) {
+ method = p_method;
+ _set_returns(true);
+#ifdef DEBUG_METHODS_ENABLED
+ _generate_argument_types(sizeof...(P));
+#endif
+ set_argument_count(sizeof...(P));
+ }
+};
+
+template <class T, class R, class... P>
+MethodBind *create_method_bind(R (T::*p_method)(P...)) {
+#ifdef TYPED_METHOD_BIND
+ MethodBind *a = memnew((MethodBindTR<T, R, P...>)(p_method));
+#else
+ MethodBind *a = memnew((MethodBindTR<R, P...>)(reinterpret_cast<R (MB_T::*)(P...)>(p_method)));
+#endif
+
+ a->set_instance_class(T::get_class_static());
+ return a;
+}
+
+// return, const
+
+#ifdef TYPED_METHOD_BIND
+template <class T, class R, class... P>
+#else
+template <class R, class... P>
+#endif
+class MethodBindTRC : public MethodBind {
+ R(MB_T::*method)
+ (P...) const;
+
+protected:
+#ifdef DEBUG_METHODS_ENABLED
+// GCC raises warnings in the case P = {} as the comparison is always false...
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wlogical-op"
+#endif
+ virtual Variant::Type _gen_argument_type(int p_arg) const {
+ if (p_arg >= 0 && p_arg < (int)sizeof...(P)) {
+ return call_get_argument_type<P...>(p_arg);
+ } else {
+ return GetTypeInfo<R>::VARIANT_TYPE;
+ }
+ }
+
+ virtual PropertyInfo _gen_argument_type_info(int p_arg) const {
+ if (p_arg >= 0 && p_arg < (int)sizeof...(P)) {
+ PropertyInfo pi;
+ call_get_argument_type_info<P...>(p_arg, pi);
+ return pi;
+ } else {
+ return GetTypeInfo<R>::get_class_info();
+ }
+ }
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+#endif
+
+public:
+#ifdef DEBUG_METHODS_ENABLED
+ virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const {
+ if (p_arg >= 0) {
+ return call_get_argument_metadata<P...>(p_arg);
+ } else {
+ return GetTypeInfo<R>::METADATA;
+ }
+ }
+#endif
+
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ Variant ret;
+#ifdef TYPED_METHOD_BIND
+ call_with_variant_args_retc_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments());
+#else
+ call_with_variant_args_retc_dv((MB_T *)(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments());
+#endif
+ return ret;
+ }
+
+#ifdef PTRCALL_ENABLED
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) {
+#ifdef TYPED_METHOD_BIND
+ call_with_ptr_args_retc<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret);
+#else
+ call_with_ptr_args_retc<MB_T, R, P...>((MB_T *)(p_object), method, p_args, r_ret);
+#endif
+ }
+#endif
+
+ MethodBindTRC(R (MB_T::*p_method)(P...) const) {
+ method = p_method;
+ _set_returns(true);
+ _set_const(true);
+#ifdef DEBUG_METHODS_ENABLED
+ _generate_argument_types(sizeof...(P));
+#endif
+ set_argument_count(sizeof...(P));
+ }
+};
+
+template <class T, class R, class... P>
+MethodBind *create_method_bind(R (T::*p_method)(P...) const) {
+#ifdef TYPED_METHOD_BIND
+ MethodBind *a = memnew((MethodBindTRC<T, R, P...>)(p_method));
+#else
+ MethodBind *a = memnew((MethodBindTRC<R, P...>)(reinterpret_cast<R (MB_T::*)(P...) const>(p_method)));
+#endif
+ a->set_instance_class(T::get_class_static());
+ return a;
+}
+
+#endif // METHOD_BIND_H
diff --git a/core/object/object.cpp b/core/object/object.cpp
new file mode 100644
index 0000000000..c3f49856ca
--- /dev/null
+++ b/core/object/object.cpp
@@ -0,0 +1,1984 @@
+/*************************************************************************/
+/* object.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "object.h"
+
+#include "core/core_string_names.h"
+#include "core/io/resource.h"
+#include "core/object/class_db.h"
+#include "core/object/message_queue.h"
+#include "core/object/script_language.h"
+#include "core/os/os.h"
+#include "core/string/print_string.h"
+#include "core/string/translation.h"
+
+#ifdef DEBUG_ENABLED
+
+struct _ObjectDebugLock {
+ Object *obj;
+
+ _ObjectDebugLock(Object *p_obj) {
+ obj = p_obj;
+ obj->_lock_index.ref();
+ }
+ ~_ObjectDebugLock() {
+ obj->_lock_index.unref();
+ }
+};
+
+#define OBJ_DEBUG_LOCK _ObjectDebugLock _debug_lock(this);
+
+#else
+
+#define OBJ_DEBUG_LOCK
+
+#endif
+
+PropertyInfo::operator Dictionary() const {
+ Dictionary d;
+ d["name"] = name;
+ d["class_name"] = class_name;
+ d["type"] = type;
+ d["hint"] = hint;
+ d["hint_string"] = hint_string;
+ d["usage"] = usage;
+ return d;
+}
+
+PropertyInfo PropertyInfo::from_dict(const Dictionary &p_dict) {
+ PropertyInfo pi;
+
+ if (p_dict.has("type")) {
+ pi.type = Variant::Type(int(p_dict["type"]));
+ }
+
+ if (p_dict.has("name")) {
+ pi.name = p_dict["name"];
+ }
+
+ if (p_dict.has("class_name")) {
+ pi.class_name = p_dict["class_name"];
+ }
+
+ if (p_dict.has("hint")) {
+ pi.hint = PropertyHint(int(p_dict["hint"]));
+ }
+
+ if (p_dict.has("hint_string")) {
+ pi.hint_string = p_dict["hint_string"];
+ }
+
+ if (p_dict.has("usage")) {
+ pi.usage = p_dict["usage"];
+ }
+
+ return pi;
+}
+
+Array convert_property_list(const List<PropertyInfo> *p_list) {
+ Array va;
+ for (const List<PropertyInfo>::Element *E = p_list->front(); E; E = E->next()) {
+ va.push_back(Dictionary(E->get()));
+ }
+
+ return va;
+}
+
+MethodInfo::operator Dictionary() const {
+ Dictionary d;
+ d["name"] = name;
+ d["args"] = convert_property_list(&arguments);
+ Array da;
+ for (int i = 0; i < default_arguments.size(); i++) {
+ da.push_back(default_arguments[i]);
+ }
+ d["default_args"] = da;
+ d["flags"] = flags;
+ d["id"] = id;
+ Dictionary r = return_val;
+ d["return"] = r;
+ return d;
+}
+
+MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) {
+ MethodInfo mi;
+
+ if (p_dict.has("name")) {
+ mi.name = p_dict["name"];
+ }
+ Array args;
+ if (p_dict.has("args")) {
+ args = p_dict["args"];
+ }
+
+ for (int i = 0; i < args.size(); i++) {
+ Dictionary d = args[i];
+ mi.arguments.push_back(PropertyInfo::from_dict(d));
+ }
+ Array defargs;
+ if (p_dict.has("default_args")) {
+ defargs = p_dict["default_args"];
+ }
+ for (int i = 0; i < defargs.size(); i++) {
+ mi.default_arguments.push_back(defargs[i]);
+ }
+
+ if (p_dict.has("return")) {
+ mi.return_val = PropertyInfo::from_dict(p_dict["return"]);
+ }
+
+ if (p_dict.has("flags")) {
+ mi.flags = p_dict["flags"];
+ }
+
+ return mi;
+}
+
+MethodInfo::MethodInfo() :
+ flags(METHOD_FLAG_NORMAL) {}
+
+MethodInfo::MethodInfo(const String &p_name) :
+ name(p_name),
+ flags(METHOD_FLAG_NORMAL) {
+}
+
+MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1) :
+ name(p_name),
+ flags(METHOD_FLAG_NORMAL) {
+ arguments.push_back(p_param1);
+}
+
+MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2) :
+ name(p_name),
+ flags(METHOD_FLAG_NORMAL) {
+ arguments.push_back(p_param1);
+ arguments.push_back(p_param2);
+}
+
+MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3) :
+ name(p_name),
+ flags(METHOD_FLAG_NORMAL) {
+ arguments.push_back(p_param1);
+ arguments.push_back(p_param2);
+ arguments.push_back(p_param3);
+}
+
+MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4) :
+ name(p_name),
+ flags(METHOD_FLAG_NORMAL) {
+ arguments.push_back(p_param1);
+ arguments.push_back(p_param2);
+ arguments.push_back(p_param3);
+ arguments.push_back(p_param4);
+}
+
+MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5) :
+ name(p_name),
+ flags(METHOD_FLAG_NORMAL) {
+ arguments.push_back(p_param1);
+ arguments.push_back(p_param2);
+ arguments.push_back(p_param3);
+ arguments.push_back(p_param4);
+ arguments.push_back(p_param5);
+}
+
+MethodInfo::MethodInfo(Variant::Type ret) :
+ flags(METHOD_FLAG_NORMAL) {
+ return_val.type = ret;
+}
+
+MethodInfo::MethodInfo(Variant::Type ret, const String &p_name) :
+ name(p_name),
+ flags(METHOD_FLAG_NORMAL) {
+ return_val.type = ret;
+}
+
+MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1) :
+ name(p_name),
+ flags(METHOD_FLAG_NORMAL) {
+ return_val.type = ret;
+ arguments.push_back(p_param1);
+}
+
+MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2) :
+ name(p_name),
+ flags(METHOD_FLAG_NORMAL) {
+ return_val.type = ret;
+ arguments.push_back(p_param1);
+ arguments.push_back(p_param2);
+}
+
+MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3) :
+ name(p_name),
+ flags(METHOD_FLAG_NORMAL) {
+ return_val.type = ret;
+ arguments.push_back(p_param1);
+ arguments.push_back(p_param2);
+ arguments.push_back(p_param3);
+}
+
+MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4) :
+ name(p_name),
+ flags(METHOD_FLAG_NORMAL) {
+ return_val.type = ret;
+ arguments.push_back(p_param1);
+ arguments.push_back(p_param2);
+ arguments.push_back(p_param3);
+ arguments.push_back(p_param4);
+}
+
+MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5) :
+ name(p_name),
+ flags(METHOD_FLAG_NORMAL) {
+ return_val.type = ret;
+ arguments.push_back(p_param1);
+ arguments.push_back(p_param2);
+ arguments.push_back(p_param3);
+ arguments.push_back(p_param4);
+ arguments.push_back(p_param5);
+}
+
+MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name) :
+ name(p_name),
+ return_val(p_ret),
+ flags(METHOD_FLAG_NORMAL) {
+}
+
+MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1) :
+ name(p_name),
+ return_val(p_ret),
+ flags(METHOD_FLAG_NORMAL) {
+ arguments.push_back(p_param1);
+}
+
+MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2) :
+ name(p_name),
+ return_val(p_ret),
+ flags(METHOD_FLAG_NORMAL) {
+ arguments.push_back(p_param1);
+ arguments.push_back(p_param2);
+}
+
+MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3) :
+ name(p_name),
+ return_val(p_ret),
+ flags(METHOD_FLAG_NORMAL) {
+ arguments.push_back(p_param1);
+ arguments.push_back(p_param2);
+ arguments.push_back(p_param3);
+}
+
+MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4) :
+ name(p_name),
+ return_val(p_ret),
+ flags(METHOD_FLAG_NORMAL) {
+ arguments.push_back(p_param1);
+ arguments.push_back(p_param2);
+ arguments.push_back(p_param3);
+ arguments.push_back(p_param4);
+}
+
+MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5) :
+ name(p_name),
+ return_val(p_ret),
+ flags(METHOD_FLAG_NORMAL) {
+ arguments.push_back(p_param1);
+ arguments.push_back(p_param2);
+ arguments.push_back(p_param3);
+ arguments.push_back(p_param4);
+ arguments.push_back(p_param5);
+}
+
+Object::Connection::operator Variant() const {
+ Dictionary d;
+ d["signal"] = signal;
+ d["callable"] = callable;
+ d["flags"] = flags;
+ d["binds"] = binds;
+ return d;
+}
+
+bool Object::Connection::operator<(const Connection &p_conn) const {
+ if (signal == p_conn.signal) {
+ return callable < p_conn.callable;
+ } else {
+ return signal < p_conn.signal;
+ }
+}
+
+Object::Connection::Connection(const Variant &p_variant) {
+ Dictionary d = p_variant;
+ if (d.has("signal")) {
+ signal = d["signal"];
+ }
+ if (d.has("callable")) {
+ callable = d["callable"];
+ }
+ if (d.has("flags")) {
+ flags = d["flags"];
+ }
+ if (d.has("binds")) {
+ binds = d["binds"];
+ }
+}
+
+bool Object::_predelete() {
+ _predelete_ok = 1;
+ notification(NOTIFICATION_PREDELETE, true);
+ if (_predelete_ok) {
+ _class_ptr = nullptr; //must restore so destructors can access class ptr correctly
+ }
+ return _predelete_ok;
+}
+
+void Object::_postinitialize() {
+ _class_ptr = _get_class_namev();
+ _initialize_classv();
+ notification(NOTIFICATION_POSTINITIALIZE);
+}
+
+void Object::get_valid_parents_static(List<String> *p_parents) {
+}
+
+void Object::_get_valid_parents_static(List<String> *p_parents) {
+}
+
+void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid) {
+#ifdef TOOLS_ENABLED
+
+ _edited = true;
+#endif
+
+ if (script_instance) {
+ if (script_instance->set(p_name, p_value)) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return;
+ }
+ }
+
+ //try built-in setgetter
+ {
+ if (ClassDB::set_property(this, p_name, p_value, r_valid)) {
+ /*
+ if (r_valid)
+ *r_valid=true;
+ */
+ return;
+ }
+ }
+
+ if (p_name == CoreStringNames::get_singleton()->_script) {
+ set_script(p_value);
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return;
+
+ } else if (p_name == CoreStringNames::get_singleton()->_meta) {
+ //set_meta(p_name,p_value);
+ metadata = p_value.duplicate();
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return;
+ }
+
+ //something inside the object... :|
+ bool success = _setv(p_name, p_value);
+ if (success) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return;
+ }
+
+#ifdef TOOLS_ENABLED
+ if (script_instance) {
+ bool valid;
+ script_instance->property_set_fallback(p_name, p_value, &valid);
+ if (valid) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return;
+ }
+ }
+#endif
+
+ if (r_valid) {
+ *r_valid = false;
+ }
+}
+
+Variant Object::get(const StringName &p_name, bool *r_valid) const {
+ Variant ret;
+
+ if (script_instance) {
+ if (script_instance->get(p_name, ret)) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return ret;
+ }
+ }
+
+ //try built-in setgetter
+ {
+ if (ClassDB::get_property(const_cast<Object *>(this), p_name, ret)) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return ret;
+ }
+ }
+
+ if (p_name == CoreStringNames::get_singleton()->_script) {
+ ret = get_script();
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return ret;
+
+ } else if (p_name == CoreStringNames::get_singleton()->_meta) {
+ ret = metadata;
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return ret;
+
+ } else {
+ //something inside the object... :|
+ bool success = _getv(p_name, ret);
+ if (success) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return ret;
+ }
+
+#ifdef TOOLS_ENABLED
+ if (script_instance) {
+ bool valid;
+ ret = script_instance->property_get_fallback(p_name, &valid);
+ if (valid) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return ret;
+ }
+ }
+#endif
+
+ if (r_valid) {
+ *r_valid = false;
+ }
+ return Variant();
+ }
+}
+
+void Object::set_indexed(const Vector<StringName> &p_names, const Variant &p_value, bool *r_valid) {
+ if (p_names.empty()) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+ return;
+ }
+ if (p_names.size() == 1) {
+ set(p_names[0], p_value, r_valid);
+ return;
+ }
+
+ bool valid = false;
+ if (!r_valid) {
+ r_valid = &valid;
+ }
+
+ List<Variant> value_stack;
+
+ value_stack.push_back(get(p_names[0], r_valid));
+
+ if (!*r_valid) {
+ value_stack.clear();
+ return;
+ }
+
+ for (int i = 1; i < p_names.size() - 1; i++) {
+ value_stack.push_back(value_stack.back()->get().get_named(p_names[i], valid));
+ if (r_valid) {
+ *r_valid = valid;
+ }
+
+ if (!valid) {
+ value_stack.clear();
+ return;
+ }
+ }
+
+ value_stack.push_back(p_value); // p_names[p_names.size() - 1]
+
+ for (int i = p_names.size() - 1; i > 0; i--) {
+ value_stack.back()->prev()->get().set_named(p_names[i], value_stack.back()->get(), valid);
+ value_stack.pop_back();
+
+ if (r_valid) {
+ *r_valid = valid;
+ }
+ if (!valid) {
+ value_stack.clear();
+ return;
+ }
+ }
+
+ set(p_names[0], value_stack.back()->get(), r_valid);
+ value_stack.pop_back();
+
+ ERR_FAIL_COND(!value_stack.empty());
+}
+
+Variant Object::get_indexed(const Vector<StringName> &p_names, bool *r_valid) const {
+ if (p_names.empty()) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+ return Variant();
+ }
+ bool valid = false;
+
+ Variant current_value = get(p_names[0], &valid);
+ for (int i = 1; i < p_names.size(); i++) {
+ current_value = current_value.get_named(p_names[i], valid);
+
+ if (!valid) {
+ break;
+ }
+ }
+ if (r_valid) {
+ *r_valid = valid;
+ }
+
+ return current_value;
+}
+
+void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) const {
+ if (script_instance && p_reversed) {
+ p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
+ script_instance->get_property_list(p_list);
+ }
+
+ _get_property_listv(p_list, p_reversed);
+
+ if (!is_class("Script")) { // can still be set, but this is for userfriendlyness
+#ifdef TOOLS_ENABLED
+ p_list->push_back(PropertyInfo(Variant::NIL, "Script", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+#endif
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT));
+ }
+ if (!metadata.empty()) {
+ p_list->push_back(PropertyInfo(Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ }
+ if (script_instance && !p_reversed) {
+ p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
+ script_instance->get_property_list(p_list);
+ }
+}
+
+void Object::_validate_property(PropertyInfo &property) const {
+}
+
+void Object::get_method_list(List<MethodInfo> *p_list) const {
+ ClassDB::get_method_list(get_class_name(), p_list);
+ if (script_instance) {
+ script_instance->get_method_list(p_list);
+ }
+}
+
+Variant Object::_call_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ if (p_argcount < 1) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 0;
+ return Variant();
+ }
+
+ if (p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING_NAME;
+ return Variant();
+ }
+
+ StringName method = *p_args[0];
+
+ return call(method, &p_args[1], p_argcount - 1, r_error);
+}
+
+Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ if (p_argcount < 1) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 0;
+ return Variant();
+ }
+
+ if (p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING_NAME;
+ return Variant();
+ }
+
+ r_error.error = Callable::CallError::CALL_OK;
+
+ StringName method = *p_args[0];
+
+ MessageQueue::get_singleton()->push_call(get_instance_id(), method, &p_args[1], p_argcount - 1, true);
+
+ return Variant();
+}
+
+bool Object::has_method(const StringName &p_method) const {
+ if (p_method == CoreStringNames::get_singleton()->_free) {
+ return true;
+ }
+
+ if (script_instance && script_instance->has_method(p_method)) {
+ return true;
+ }
+
+ MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
+
+ return method != nullptr;
+}
+
+Variant Object::getvar(const Variant &p_key, bool *r_valid) const {
+ if (r_valid) {
+ *r_valid = false;
+ }
+
+ if (p_key.get_type() == Variant::STRING_NAME || p_key.get_type() == Variant::STRING) {
+ return get(p_key, r_valid);
+ }
+ return Variant();
+}
+
+void Object::setvar(const Variant &p_key, const Variant &p_value, bool *r_valid) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+ if (p_key.get_type() == Variant::STRING_NAME || p_key.get_type() == Variant::STRING) {
+ return set(p_key, p_value, r_valid);
+ }
+}
+
+Variant Object::callv(const StringName &p_method, const Array &p_args) {
+ const Variant **argptrs = nullptr;
+
+ if (p_args.size() > 0) {
+ argptrs = (const Variant **)alloca(sizeof(Variant *) * p_args.size());
+ for (int i = 0; i < p_args.size(); i++) {
+ argptrs[i] = &p_args[i];
+ }
+ }
+
+ Callable::CallError ce;
+ Variant ret = call(p_method, argptrs, p_args.size(), ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_V_MSG(Variant(), "Error calling method from 'callv': " + Variant::get_call_error_text(this, p_method, argptrs, p_args.size(), ce) + ".");
+ }
+ return ret;
+}
+
+Variant Object::call(const StringName &p_name, VARIANT_ARG_DECLARE) {
+ VARIANT_ARGPTRS;
+
+ int argc = 0;
+ for (int i = 0; i < VARIANT_ARG_MAX; i++) {
+ if (argptr[i]->get_type() == Variant::NIL) {
+ break;
+ }
+ argc++;
+ }
+
+ Callable::CallError error;
+
+ Variant ret = call(p_name, argptr, argc, error);
+ return ret;
+}
+
+Variant Object::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+
+ if (p_method == CoreStringNames::get_singleton()->_free) {
+//free must be here, before anything, always ready
+#ifdef DEBUG_ENABLED
+ if (p_argcount != 0) {
+ r_error.argument = 0;
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ return Variant();
+ }
+ if (Object::cast_to<Reference>(this)) {
+ r_error.argument = 0;
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ ERR_FAIL_V_MSG(Variant(), "Can't 'free' a reference.");
+ }
+
+ if (_lock_index.get() > 1) {
+ r_error.argument = 0;
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ ERR_FAIL_V_MSG(Variant(), "Object is locked and can't be freed.");
+ }
+
+#endif
+ //must be here, must be before everything,
+ memdelete(this);
+ r_error.error = Callable::CallError::CALL_OK;
+ return Variant();
+ }
+
+ Variant ret;
+ OBJ_DEBUG_LOCK
+ if (script_instance) {
+ ret = script_instance->call(p_method, p_args, p_argcount, r_error);
+ //force jumptable
+ switch (r_error.error) {
+ case Callable::CallError::CALL_OK:
+ return ret;
+ case Callable::CallError::CALL_ERROR_INVALID_METHOD:
+ break;
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ return ret;
+ case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: {
+ }
+ }
+ }
+
+ MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
+
+ if (method) {
+ ret = method->call(this, p_args, p_argcount, r_error);
+ } else {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ }
+
+ return ret;
+}
+
+void Object::notification(int p_notification, bool p_reversed) {
+ _notificationv(p_notification, p_reversed);
+
+ if (script_instance) {
+ script_instance->notification(p_notification);
+ }
+}
+
+String Object::to_string() {
+ if (script_instance) {
+ bool valid;
+ String ret = script_instance->to_string(&valid);
+ if (valid) {
+ return ret;
+ }
+ }
+ return "[" + get_class() + ":" + itos(get_instance_id()) + "]";
+}
+
+void Object::_changed_callback(Object *p_changed, const char *p_prop) {
+}
+
+void Object::add_change_receptor(Object *p_receptor) {
+ change_receptors.insert(p_receptor);
+}
+
+void Object::remove_change_receptor(Object *p_receptor) {
+ change_receptors.erase(p_receptor);
+}
+
+void Object::property_list_changed_notify() {
+ _change_notify();
+}
+
+void Object::cancel_delete() {
+ _predelete_ok = true;
+}
+
+void Object::set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance) {
+ //this function is not meant to be used in any of these ways
+ ERR_FAIL_COND(p_script.is_null());
+ ERR_FAIL_COND(!p_instance);
+ ERR_FAIL_COND(script_instance != nullptr || !script.is_null());
+
+ script = p_script;
+ script_instance = p_instance;
+}
+
+void Object::set_script(const Variant &p_script) {
+ if (script == p_script) {
+ return;
+ }
+
+ if (script_instance) {
+ memdelete(script_instance);
+ script_instance = nullptr;
+ }
+
+ script = p_script;
+ Ref<Script> s = script;
+
+ if (!s.is_null()) {
+ if (s->can_instance()) {
+ OBJ_DEBUG_LOCK
+ script_instance = s->instance_create(this);
+ } else if (Engine::get_singleton()->is_editor_hint()) {
+ OBJ_DEBUG_LOCK
+ script_instance = s->placeholder_instance_create(this);
+ }
+ }
+
+ _change_notify(); //scripts may add variables, so refresh is desired
+ emit_signal(CoreStringNames::get_singleton()->script_changed);
+}
+
+void Object::set_script_instance(ScriptInstance *p_instance) {
+ if (script_instance == p_instance) {
+ return;
+ }
+
+ if (script_instance) {
+ memdelete(script_instance);
+ }
+
+ script_instance = p_instance;
+
+ if (p_instance) {
+ script = p_instance->get_script();
+ } else {
+ script = Variant();
+ }
+}
+
+Variant Object::get_script() const {
+ return script;
+}
+
+bool Object::has_meta(const String &p_name) const {
+ return metadata.has(p_name);
+}
+
+void Object::set_meta(const String &p_name, const Variant &p_value) {
+ if (p_value.get_type() == Variant::NIL) {
+ metadata.erase(p_name);
+ return;
+ }
+
+ metadata[p_name] = p_value;
+}
+
+Variant Object::get_meta(const String &p_name) const {
+ ERR_FAIL_COND_V(!metadata.has(p_name), Variant());
+ return metadata[p_name];
+}
+
+void Object::remove_meta(const String &p_name) {
+ metadata.erase(p_name);
+}
+
+Array Object::_get_property_list_bind() const {
+ List<PropertyInfo> lpi;
+ get_property_list(&lpi);
+ return convert_property_list(&lpi);
+}
+
+Array Object::_get_method_list_bind() const {
+ List<MethodInfo> ml;
+ get_method_list(&ml);
+ Array ret;
+
+ for (List<MethodInfo>::Element *E = ml.front(); E; E = E->next()) {
+ Dictionary d = E->get();
+ //va.push_back(d);
+ ret.push_back(d);
+ }
+
+ return ret;
+}
+
+Vector<String> Object::_get_meta_list_bind() const {
+ Vector<String> _metaret;
+
+ List<Variant> keys;
+ metadata.get_key_list(&keys);
+ for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+ _metaret.push_back(E->get());
+ }
+
+ return _metaret;
+}
+
+void Object::get_meta_list(List<String> *p_list) const {
+ List<Variant> keys;
+ metadata.get_key_list(&keys);
+ for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+ p_list->push_back(E->get());
+ }
+}
+
+void Object::add_user_signal(const MethodInfo &p_signal) {
+ ERR_FAIL_COND_MSG(p_signal.name == "", "Signal name cannot be empty.");
+ ERR_FAIL_COND_MSG(ClassDB::has_signal(get_class_name(), p_signal.name), "User signal's name conflicts with a built-in signal of '" + get_class_name() + "'.");
+ ERR_FAIL_COND_MSG(signal_map.has(p_signal.name), "Trying to add already existing signal '" + p_signal.name + "'.");
+ SignalData s;
+ s.user = p_signal;
+ signal_map[p_signal.name] = s;
+}
+
+bool Object::_has_user_signal(const StringName &p_name) const {
+ if (!signal_map.has(p_name)) {
+ return false;
+ }
+ return signal_map[p_name].user.name.length() > 0;
+}
+
+struct _ObjectSignalDisconnectData {
+ StringName signal;
+ Callable callable;
+};
+
+Variant Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+
+ ERR_FAIL_COND_V(p_argcount < 1, Variant());
+ if (p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING_NAME;
+ ERR_FAIL_COND_V(p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING, Variant());
+ }
+
+ r_error.error = Callable::CallError::CALL_OK;
+
+ StringName signal = *p_args[0];
+
+ const Variant **args = nullptr;
+
+ int argc = p_argcount - 1;
+ if (argc) {
+ args = &p_args[1];
+ }
+
+ emit_signal(signal, args, argc);
+
+ return Variant();
+}
+
+Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int p_argcount) {
+ if (_block_signals) {
+ return ERR_CANT_ACQUIRE_RESOURCE; //no emit, signals blocked
+ }
+
+ SignalData *s = signal_map.getptr(p_name);
+ if (!s) {
+#ifdef DEBUG_ENABLED
+ bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_name);
+ //check in script
+ ERR_FAIL_COND_V_MSG(!signal_is_valid && !script.is_null() && !Ref<Script>(script)->has_script_signal(p_name), ERR_UNAVAILABLE, "Can't emit non-existing signal " + String("\"") + p_name + "\".");
+#endif
+ //not connected? just return
+ return ERR_UNAVAILABLE;
+ }
+
+ List<_ObjectSignalDisconnectData> disconnect_data;
+
+ //copy on write will ensure that disconnecting the signal or even deleting the object will not affect the signal calling.
+ //this happens automatically and will not change the performance of calling.
+ //awesome, isn't it?
+ VMap<Callable, SignalData::Slot> slot_map = s->slot_map;
+
+ int ssize = slot_map.size();
+
+ OBJ_DEBUG_LOCK
+
+ Vector<const Variant *> bind_mem;
+
+ Error err = OK;
+
+ for (int i = 0; i < ssize; i++) {
+ const Connection &c = slot_map.getv(i).conn;
+
+ Object *target = c.callable.get_object();
+ if (!target) {
+ // Target might have been deleted during signal callback, this is expected and OK.
+ continue;
+ }
+
+ const Variant **args = p_args;
+ int argc = p_argcount;
+
+ if (c.binds.size()) {
+ //handle binds
+ bind_mem.resize(p_argcount + c.binds.size());
+
+ for (int j = 0; j < p_argcount; j++) {
+ bind_mem.write[j] = p_args[j];
+ }
+ for (int j = 0; j < c.binds.size(); j++) {
+ bind_mem.write[p_argcount + j] = &c.binds[j];
+ }
+
+ args = (const Variant **)bind_mem.ptr();
+ argc = bind_mem.size();
+ }
+
+ if (c.flags & CONNECT_DEFERRED) {
+ MessageQueue::get_singleton()->push_callable(c.callable, args, argc, true);
+ } else {
+ Callable::CallError ce;
+ _emitting = true;
+ Variant ret;
+ c.callable.call(args, argc, ret, ce);
+ _emitting = false;
+
+ if (ce.error != Callable::CallError::CALL_OK) {
+#ifdef DEBUG_ENABLED
+ if (c.flags & CONNECT_PERSIST && Engine::get_singleton()->is_editor_hint() && (script.is_null() || !Ref<Script>(script)->is_tool())) {
+ continue;
+ }
+#endif
+ if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) {
+ //most likely object is not initialized yet, do not throw error.
+ } else {
+ ERR_PRINT("Error calling from signal '" + String(p_name) + "' to callable: " + Variant::get_callable_error_text(c.callable, args, argc, ce) + ".");
+ err = ERR_METHOD_NOT_FOUND;
+ }
+ }
+ }
+
+ bool disconnect = c.flags & CONNECT_ONESHOT;
+#ifdef TOOLS_ENABLED
+ if (disconnect && (c.flags & CONNECT_PERSIST) && Engine::get_singleton()->is_editor_hint()) {
+ //this signal was connected from the editor, and is being edited. just don't disconnect for now
+ disconnect = false;
+ }
+#endif
+ if (disconnect) {
+ _ObjectSignalDisconnectData dd;
+ dd.signal = p_name;
+ dd.callable = c.callable;
+ disconnect_data.push_back(dd);
+ }
+ }
+
+ while (!disconnect_data.empty()) {
+ const _ObjectSignalDisconnectData &dd = disconnect_data.front()->get();
+
+ _disconnect(dd.signal, dd.callable);
+ disconnect_data.pop_front();
+ }
+
+ return err;
+}
+
+Error Object::emit_signal(const StringName &p_name, VARIANT_ARG_DECLARE) {
+ VARIANT_ARGPTRS;
+
+ int argc = 0;
+
+ for (int i = 0; i < VARIANT_ARG_MAX; i++) {
+ if (argptr[i]->get_type() == Variant::NIL) {
+ break;
+ }
+ argc++;
+ }
+
+ return emit_signal(p_name, argptr, argc);
+}
+
+void Object::_add_user_signal(const String &p_name, const Array &p_args) {
+ // this version of add_user_signal is meant to be used from scripts or external apis
+ // without access to ADD_SIGNAL in bind_methods
+ // added events are per instance, as opposed to the other ones, which are global
+
+ MethodInfo mi;
+ mi.name = p_name;
+
+ for (int i = 0; i < p_args.size(); i++) {
+ Dictionary d = p_args[i];
+ PropertyInfo param;
+
+ if (d.has("name")) {
+ param.name = d["name"];
+ }
+ if (d.has("type")) {
+ param.type = (Variant::Type)(int)d["type"];
+ }
+
+ mi.arguments.push_back(param);
+ }
+
+ add_user_signal(mi);
+}
+
+Array Object::_get_signal_list() const {
+ List<MethodInfo> signal_list;
+ get_signal_list(&signal_list);
+
+ Array ret;
+ for (List<MethodInfo>::Element *E = signal_list.front(); E; E = E->next()) {
+ ret.push_back(Dictionary(E->get()));
+ }
+
+ return ret;
+}
+
+Array Object::_get_signal_connection_list(const String &p_signal) const {
+ List<Connection> conns;
+ get_all_signal_connections(&conns);
+
+ Array ret;
+
+ for (List<Connection>::Element *E = conns.front(); E; E = E->next()) {
+ Connection &c = E->get();
+ if (c.signal.get_name() == p_signal) {
+ ret.push_back(c);
+ }
+ }
+
+ return ret;
+}
+
+Array Object::_get_incoming_connections() const {
+ Array ret;
+ int connections_amount = connections.size();
+ for (int idx_conn = 0; idx_conn < connections_amount; idx_conn++) {
+ ret.push_back(connections[idx_conn]);
+ }
+
+ return ret;
+}
+
+bool Object::has_signal(const StringName &p_name) const {
+ if (!script.is_null()) {
+ Ref<Script> scr = script;
+ if (scr.is_valid() && scr->has_script_signal(p_name)) {
+ return true;
+ }
+ }
+
+ if (ClassDB::has_signal(get_class_name(), p_name)) {
+ return true;
+ }
+
+ if (_has_user_signal(p_name)) {
+ return true;
+ }
+
+ return false;
+}
+
+void Object::get_signal_list(List<MethodInfo> *p_signals) const {
+ if (!script.is_null()) {
+ Ref<Script> scr = script;
+ if (scr.is_valid()) {
+ scr->get_script_signal_list(p_signals);
+ }
+ }
+
+ ClassDB::get_signal_list(get_class_name(), p_signals);
+ //find maybe usersignals?
+ const StringName *S = nullptr;
+
+ while ((S = signal_map.next(S))) {
+ if (signal_map[*S].user.name != "") {
+ //user signal
+ p_signals->push_back(signal_map[*S].user);
+ }
+ }
+}
+
+void Object::get_all_signal_connections(List<Connection> *p_connections) const {
+ const StringName *S = nullptr;
+
+ while ((S = signal_map.next(S))) {
+ const SignalData *s = &signal_map[*S];
+
+ for (int i = 0; i < s->slot_map.size(); i++) {
+ p_connections->push_back(s->slot_map.getv(i).conn);
+ }
+ }
+}
+
+void Object::get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const {
+ const SignalData *s = signal_map.getptr(p_signal);
+ if (!s) {
+ return; //nothing
+ }
+
+ for (int i = 0; i < s->slot_map.size(); i++) {
+ p_connections->push_back(s->slot_map.getv(i).conn);
+ }
+}
+
+int Object::get_persistent_signal_connection_count() const {
+ int count = 0;
+ const StringName *S = nullptr;
+
+ while ((S = signal_map.next(S))) {
+ const SignalData *s = &signal_map[*S];
+
+ for (int i = 0; i < s->slot_map.size(); i++) {
+ if (s->slot_map.getv(i).conn.flags & CONNECT_PERSIST) {
+ count += 1;
+ }
+ }
+ }
+
+ return count;
+}
+
+void Object::get_signals_connected_to_this(List<Connection> *p_connections) const {
+ for (const List<Connection>::Element *E = connections.front(); E; E = E->next()) {
+ p_connections->push_back(E->get());
+ }
+}
+
+Error Object::connect_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, const Vector<Variant> &p_binds, uint32_t p_flags) {
+ return connect(p_signal, Callable(p_to_object, p_to_method), p_binds, p_flags);
+}
+
+Error Object::connect(const StringName &p_signal, const Callable &p_callable, const Vector<Variant> &p_binds, uint32_t p_flags) {
+ ERR_FAIL_COND_V(p_callable.is_null(), ERR_INVALID_PARAMETER);
+
+ Object *target_object = p_callable.get_object();
+ ERR_FAIL_COND_V(!target_object, ERR_INVALID_PARAMETER);
+
+ SignalData *s = signal_map.getptr(p_signal);
+ if (!s) {
+ bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal);
+ //check in script
+ if (!signal_is_valid && !script.is_null()) {
+ if (Ref<Script>(script)->has_script_signal(p_signal)) {
+ signal_is_valid = true;
+ }
+#ifdef TOOLS_ENABLED
+ else {
+ //allow connecting signals anyway if script is invalid, see issue #17070
+ if (!Ref<Script>(script)->is_valid()) {
+ signal_is_valid = true;
+ }
+ }
+#endif
+ }
+
+ ERR_FAIL_COND_V_MSG(!signal_is_valid, ERR_INVALID_PARAMETER, "In Object of type '" + String(get_class()) + "': Attempt to connect nonexistent signal '" + p_signal + "' to callable '" + p_callable + "'.");
+
+ signal_map[p_signal] = SignalData();
+ s = &signal_map[p_signal];
+ }
+
+ Callable target = p_callable;
+
+ //compare with the base callable, so binds can be ignored
+ if (s->slot_map.has(*target.get_base_comparator())) {
+ if (p_flags & CONNECT_REFERENCE_COUNTED) {
+ s->slot_map[*target.get_base_comparator()].reference_count++;
+ return OK;
+ } else {
+ ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Signal '" + p_signal + "' is already connected to given callable '" + p_callable + "' in that object.");
+ }
+ }
+
+ SignalData::Slot slot;
+
+ Connection conn;
+ conn.callable = target;
+ conn.signal = ::Signal(this, p_signal);
+ conn.flags = p_flags;
+ conn.binds = p_binds;
+ slot.conn = conn;
+ slot.cE = target_object->connections.push_back(conn);
+ if (p_flags & CONNECT_REFERENCE_COUNTED) {
+ slot.reference_count = 1;
+ }
+
+ //use callable version as key, so binds can be ignored
+ s->slot_map[*target.get_base_comparator()] = slot;
+
+ return OK;
+}
+
+bool Object::is_connected_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) const {
+ return is_connected(p_signal, Callable(p_to_object, p_to_method));
+}
+
+bool Object::is_connected(const StringName &p_signal, const Callable &p_callable) const {
+ ERR_FAIL_COND_V(p_callable.is_null(), false);
+ const SignalData *s = signal_map.getptr(p_signal);
+ if (!s) {
+ bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal);
+ if (signal_is_valid) {
+ return false;
+ }
+
+ if (!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal)) {
+ return false;
+ }
+
+ ERR_FAIL_V_MSG(false, "Nonexistent signal: " + p_signal + ".");
+ }
+
+ Callable target = p_callable;
+
+ return s->slot_map.has(*target.get_base_comparator());
+ //const Map<Signal::Target,Signal::Slot>::Element *E = s->slot_map.find(target);
+ //return (E!=nullptr );
+}
+
+void Object::disconnect_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) {
+ _disconnect(p_signal, Callable(p_to_object, p_to_method));
+}
+
+void Object::disconnect(const StringName &p_signal, const Callable &p_callable) {
+ _disconnect(p_signal, p_callable);
+}
+
+void Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) {
+ ERR_FAIL_COND(p_callable.is_null());
+
+ Object *target_object = p_callable.get_object();
+ ERR_FAIL_COND(!target_object);
+
+ SignalData *s = signal_map.getptr(p_signal);
+ ERR_FAIL_COND_MSG(!s, vformat("Nonexistent signal '%s' in %s.", p_signal, to_string()));
+
+ ERR_FAIL_COND_MSG(!s->slot_map.has(*p_callable.get_base_comparator()), "Disconnecting nonexistent signal '" + p_signal + "', callable: " + p_callable + ".");
+
+ SignalData::Slot *slot = &s->slot_map[p_callable];
+
+ if (!p_force) {
+ slot->reference_count--; // by default is zero, if it was not referenced it will go below it
+ if (slot->reference_count >= 0) {
+ return;
+ }
+ }
+
+ target_object->connections.erase(slot->cE);
+ s->slot_map.erase(*p_callable.get_base_comparator());
+
+ if (s->slot_map.empty() && ClassDB::has_signal(get_class_name(), p_signal)) {
+ //not user signal, delete
+ signal_map.erase(p_signal);
+ }
+}
+
+void Object::_set_bind(const String &p_set, const Variant &p_value) {
+ set(p_set, p_value);
+}
+
+Variant Object::_get_bind(const String &p_name) const {
+ return get(p_name);
+}
+
+void Object::_set_indexed_bind(const NodePath &p_name, const Variant &p_value) {
+ set_indexed(p_name.get_as_property_path().get_subnames(), p_value);
+}
+
+Variant Object::_get_indexed_bind(const NodePath &p_name) const {
+ return get_indexed(p_name.get_as_property_path().get_subnames());
+}
+
+void Object::initialize_class() {
+ static bool initialized = false;
+ if (initialized) {
+ return;
+ }
+ ClassDB::_add_class<Object>();
+ _bind_methods();
+ initialized = true;
+}
+
+String Object::tr(const StringName &p_message, const StringName &p_context) const {
+ if (!_can_translate || !TranslationServer::get_singleton()) {
+ return p_message;
+ }
+ return TranslationServer::get_singleton()->translate(p_message, p_context);
+}
+
+String Object::tr_n(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
+ if (!_can_translate || !TranslationServer::get_singleton()) {
+ // Return message based on English plural rule if translation is not possible.
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+ }
+ return TranslationServer::get_singleton()->translate_plural(p_message, p_message_plural, p_n, p_context);
+}
+
+void Object::_clear_internal_resource_paths(const Variant &p_var) {
+ switch (p_var.get_type()) {
+ case Variant::OBJECT: {
+ RES r = p_var;
+ if (!r.is_valid()) {
+ return;
+ }
+
+ if (!r->get_path().begins_with("res://") || r->get_path().find("::") == -1) {
+ return; //not an internal resource
+ }
+
+ Object *object = p_var;
+ if (!object) {
+ return;
+ }
+
+ r->set_path("");
+ r->clear_internal_resource_paths();
+ } break;
+ case Variant::ARRAY: {
+ Array a = p_var;
+ for (int i = 0; i < a.size(); i++) {
+ _clear_internal_resource_paths(a[i]);
+ }
+
+ } break;
+ case Variant::DICTIONARY: {
+ Dictionary d = p_var;
+ List<Variant> keys;
+ d.get_key_list(&keys);
+
+ for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+ _clear_internal_resource_paths(E->get());
+ _clear_internal_resource_paths(d[E->get()]);
+ }
+ } break;
+ default: {
+ }
+ }
+}
+
+#ifdef TOOLS_ENABLED
+void Object::editor_set_section_unfold(const String &p_section, bool p_unfolded) {
+ set_edited(true);
+ if (p_unfolded) {
+ editor_section_folding.insert(p_section);
+ } else {
+ editor_section_folding.erase(p_section);
+ }
+}
+
+bool Object::editor_is_section_unfolded(const String &p_section) {
+ return editor_section_folding.has(p_section);
+}
+
+#endif
+
+void Object::clear_internal_resource_paths() {
+ List<PropertyInfo> pinfo;
+
+ get_property_list(&pinfo);
+
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+ _clear_internal_resource_paths(get(E->get().name));
+ }
+}
+
+void Object::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_class"), &Object::get_class);
+ ClassDB::bind_method(D_METHOD("is_class", "class"), &Object::is_class);
+ ClassDB::bind_method(D_METHOD("set", "property", "value"), &Object::_set_bind);
+ ClassDB::bind_method(D_METHOD("get", "property"), &Object::_get_bind);
+ ClassDB::bind_method(D_METHOD("set_indexed", "property", "value"), &Object::_set_indexed_bind);
+ ClassDB::bind_method(D_METHOD("get_indexed", "property"), &Object::_get_indexed_bind);
+ ClassDB::bind_method(D_METHOD("get_property_list"), &Object::_get_property_list_bind);
+ ClassDB::bind_method(D_METHOD("get_method_list"), &Object::_get_method_list_bind);
+ ClassDB::bind_method(D_METHOD("notification", "what", "reversed"), &Object::notification, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("to_string"), &Object::to_string);
+ ClassDB::bind_method(D_METHOD("get_instance_id"), &Object::get_instance_id);
+
+ ClassDB::bind_method(D_METHOD("set_script", "script"), &Object::set_script);
+ ClassDB::bind_method(D_METHOD("get_script"), &Object::get_script);
+
+ ClassDB::bind_method(D_METHOD("set_meta", "name", "value"), &Object::set_meta);
+ ClassDB::bind_method(D_METHOD("remove_meta", "name"), &Object::remove_meta);
+ ClassDB::bind_method(D_METHOD("get_meta", "name"), &Object::get_meta);
+ ClassDB::bind_method(D_METHOD("has_meta", "name"), &Object::has_meta);
+ ClassDB::bind_method(D_METHOD("get_meta_list"), &Object::_get_meta_list_bind);
+
+ ClassDB::bind_method(D_METHOD("add_user_signal", "signal", "arguments"), &Object::_add_user_signal, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("has_user_signal", "signal"), &Object::_has_user_signal);
+
+ {
+ MethodInfo mi;
+ mi.name = "emit_signal";
+ mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "signal"));
+
+ ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "emit_signal", &Object::_emit_signal, mi, varray(), false);
+ }
+
+ {
+ MethodInfo mi;
+ mi.name = "call";
+ mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method"));
+
+ ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call", &Object::_call_bind, mi);
+ }
+
+ {
+ MethodInfo mi;
+ mi.name = "call_deferred";
+ mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method"));
+
+ ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call_deferred", &Object::_call_deferred_bind, mi, varray(), false);
+ }
+
+ ClassDB::bind_method(D_METHOD("set_deferred", "property", "value"), &Object::set_deferred);
+
+ ClassDB::bind_method(D_METHOD("callv", "method", "arg_array"), &Object::callv);
+
+ ClassDB::bind_method(D_METHOD("has_method", "method"), &Object::has_method);
+
+ ClassDB::bind_method(D_METHOD("has_signal", "signal"), &Object::has_signal);
+ ClassDB::bind_method(D_METHOD("get_signal_list"), &Object::_get_signal_list);
+ ClassDB::bind_method(D_METHOD("get_signal_connection_list", "signal"), &Object::_get_signal_connection_list);
+ ClassDB::bind_method(D_METHOD("get_incoming_connections"), &Object::_get_incoming_connections);
+
+ ClassDB::bind_method(D_METHOD("connect", "signal", "callable", "binds", "flags"), &Object::connect, DEFVAL(Array()), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("disconnect", "signal", "callable"), &Object::disconnect);
+ ClassDB::bind_method(D_METHOD("is_connected", "signal", "callable"), &Object::is_connected);
+
+ ClassDB::bind_method(D_METHOD("set_block_signals", "enable"), &Object::set_block_signals);
+ ClassDB::bind_method(D_METHOD("is_blocking_signals"), &Object::is_blocking_signals);
+ ClassDB::bind_method(D_METHOD("property_list_changed_notify"), &Object::property_list_changed_notify);
+
+ ClassDB::bind_method(D_METHOD("set_message_translation", "enable"), &Object::set_message_translation);
+ ClassDB::bind_method(D_METHOD("can_translate_messages"), &Object::can_translate_messages);
+ ClassDB::bind_method(D_METHOD("tr", "message", "context"), &Object::tr, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("tr_n", "message", "plural_message", "n", "context"), &Object::tr_n, DEFVAL(""));
+
+ ClassDB::bind_method(D_METHOD("is_queued_for_deletion"), &Object::is_queued_for_deletion);
+
+ ClassDB::add_virtual_method("Object", MethodInfo("free"), false);
+
+ ADD_SIGNAL(MethodInfo("script_changed"));
+
+ BIND_VMETHOD(MethodInfo("_notification", PropertyInfo(Variant::INT, "what")));
+ BIND_VMETHOD(MethodInfo(Variant::BOOL, "_set", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value")));
+#ifdef TOOLS_ENABLED
+ MethodInfo miget("_get", PropertyInfo(Variant::STRING_NAME, "property"));
+ miget.return_val.name = "Variant";
+ miget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ BIND_VMETHOD(miget);
+
+ MethodInfo plget("_get_property_list");
+
+ plget.return_val.type = Variant::ARRAY;
+ BIND_VMETHOD(plget);
+
+#endif
+ BIND_VMETHOD(MethodInfo("_init"));
+ BIND_VMETHOD(MethodInfo(Variant::STRING, "_to_string"));
+
+ BIND_CONSTANT(NOTIFICATION_POSTINITIALIZE);
+ BIND_CONSTANT(NOTIFICATION_PREDELETE);
+
+ BIND_ENUM_CONSTANT(CONNECT_DEFERRED);
+ BIND_ENUM_CONSTANT(CONNECT_PERSIST);
+ BIND_ENUM_CONSTANT(CONNECT_ONESHOT);
+ BIND_ENUM_CONSTANT(CONNECT_REFERENCE_COUNTED);
+}
+
+void Object::call_deferred(const StringName &p_method, VARIANT_ARG_DECLARE) {
+ MessageQueue::get_singleton()->push_call(this, p_method, VARIANT_ARG_PASS);
+}
+
+void Object::set_deferred(const StringName &p_property, const Variant &p_value) {
+ MessageQueue::get_singleton()->push_set(this, p_property, p_value);
+}
+
+void Object::set_block_signals(bool p_block) {
+ _block_signals = p_block;
+}
+
+bool Object::is_blocking_signals() const {
+ return _block_signals;
+}
+
+void Object::get_translatable_strings(List<String> *p_strings) const {
+ List<PropertyInfo> plist;
+ get_property_list(&plist);
+
+ for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
+ if (!(E->get().usage & PROPERTY_USAGE_INTERNATIONALIZED)) {
+ continue;
+ }
+
+ String text = get(E->get().name);
+
+ if (text == "") {
+ continue;
+ }
+
+ p_strings->push_back(text);
+ }
+}
+
+Variant::Type Object::get_static_property_type(const StringName &p_property, bool *r_valid) const {
+ bool valid;
+ Variant::Type t = ClassDB::get_property_type(get_class_name(), p_property, &valid);
+ if (valid) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return t;
+ }
+
+ if (get_script_instance()) {
+ return get_script_instance()->get_property_type(p_property, r_valid);
+ }
+ if (r_valid) {
+ *r_valid = false;
+ }
+
+ return Variant::NIL;
+}
+
+Variant::Type Object::get_static_property_type_indexed(const Vector<StringName> &p_path, bool *r_valid) const {
+ if (p_path.size() == 0) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+
+ return Variant::NIL;
+ }
+
+ bool valid = false;
+ Variant::Type t = get_static_property_type(p_path[0], &valid);
+ if (!valid) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+
+ return Variant::NIL;
+ }
+
+ Callable::CallError ce;
+ Variant check;
+ Variant::construct(t, check, nullptr, 0, ce);
+
+ for (int i = 1; i < p_path.size(); i++) {
+ if (check.get_type() == Variant::OBJECT || check.get_type() == Variant::DICTIONARY || check.get_type() == Variant::ARRAY) {
+ // We cannot be sure about the type of properties this types can have
+ if (r_valid) {
+ *r_valid = false;
+ }
+ return Variant::NIL;
+ }
+
+ check = check.get_named(p_path[i], valid);
+
+ if (!valid) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+ return Variant::NIL;
+ }
+ }
+
+ if (r_valid) {
+ *r_valid = true;
+ }
+
+ return check.get_type();
+}
+
+bool Object::is_queued_for_deletion() const {
+ return _is_queued_for_deletion;
+}
+
+#ifdef TOOLS_ENABLED
+void Object::set_edited(bool p_edited) {
+ _edited = p_edited;
+ _edited_version++;
+}
+
+bool Object::is_edited() const {
+ return _edited;
+}
+
+uint32_t Object::get_edited_version() const {
+ return _edited_version;
+}
+#endif
+
+void *Object::get_script_instance_binding(int p_script_language_index) {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(p_script_language_index, MAX_SCRIPT_INSTANCE_BINDINGS, nullptr);
+#endif
+
+ //it's up to the script language to make this thread safe, if the function is called twice due to threads being out of syncro
+ //just return the same pointer.
+ //if you want to put a big lock in the entire function and keep allocated pointers in a map or something, feel free to do it
+ //as it should not really affect performance much (won't be called too often), as in far most caes the condition below will be false afterwards
+
+ if (!_script_instance_bindings[p_script_language_index]) {
+ void *script_data = ScriptServer::get_language(p_script_language_index)->alloc_instance_binding_data(this);
+ if (script_data) {
+ atomic_increment(&instance_binding_count);
+ _script_instance_bindings[p_script_language_index] = script_data;
+ }
+ }
+
+ return _script_instance_bindings[p_script_language_index];
+}
+
+bool Object::has_script_instance_binding(int p_script_language_index) {
+ return _script_instance_bindings[p_script_language_index] != nullptr;
+}
+
+void Object::set_script_instance_binding(int p_script_language_index, void *p_data) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(_script_instance_bindings[p_script_language_index] != nullptr);
+#endif
+ _script_instance_bindings[p_script_language_index] = p_data;
+}
+
+void Object::_construct_object(bool p_reference) {
+ type_is_reference = p_reference;
+ _instance_id = ObjectDB::add_instance(this);
+ memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS);
+
+#ifdef DEBUG_ENABLED
+ _lock_index.init(1);
+#endif
+}
+
+Object::Object(bool p_reference) {
+ _construct_object(p_reference);
+}
+
+Object::Object() {
+ _construct_object(false);
+}
+
+Object::~Object() {
+ if (script_instance) {
+ memdelete(script_instance);
+ }
+ script_instance = nullptr;
+
+ const StringName *S = nullptr;
+
+ if (_emitting) {
+ //@todo this may need to actually reach the debugger prioritarily somehow because it may crash before
+ ERR_PRINT("Object " + to_string() + " was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes.");
+ }
+
+ while ((S = signal_map.next(nullptr))) {
+ SignalData *s = &signal_map[*S];
+
+ //brute force disconnect for performance
+ int slot_count = s->slot_map.size();
+ const VMap<Callable, SignalData::Slot>::Pair *slot_list = s->slot_map.get_array();
+
+ for (int i = 0; i < slot_count; i++) {
+ slot_list[i].value.conn.callable.get_object()->connections.erase(slot_list[i].value.cE);
+ }
+
+ signal_map.erase(*S);
+ }
+
+ //signals from nodes that connect to this node
+ while (connections.size()) {
+ Connection c = connections.front()->get();
+ c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true);
+ }
+
+ ObjectDB::remove_instance(this);
+ _instance_id = ObjectID();
+ _predelete_ok = 2;
+
+ if (!ScriptServer::are_languages_finished()) {
+ for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
+ if (_script_instance_bindings[i]) {
+ ScriptServer::get_language(i)->free_instance_binding_data(_script_instance_bindings[i]);
+ }
+ }
+ }
+}
+
+bool predelete_handler(Object *p_object) {
+ return p_object->_predelete();
+}
+
+void postinitialize_handler(Object *p_object) {
+ p_object->_postinitialize();
+}
+
+void ObjectDB::debug_objects(DebugFunc p_func) {
+ spin_lock.lock();
+ for (uint32_t i = 0; i < slot_count; i++) {
+ uint32_t slot = object_slots[i].next_free;
+ p_func(object_slots[slot].object);
+ }
+ spin_lock.unlock();
+}
+
+void Object::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+}
+
+SpinLock ObjectDB::spin_lock;
+uint32_t ObjectDB::slot_count = 0;
+uint32_t ObjectDB::slot_max = 0;
+ObjectDB::ObjectSlot *ObjectDB::object_slots = nullptr;
+uint64_t ObjectDB::validator_counter = 0;
+
+int ObjectDB::get_object_count() {
+ return slot_count;
+}
+
+ObjectID ObjectDB::add_instance(Object *p_object) {
+ spin_lock.lock();
+ if (unlikely(slot_count == slot_max)) {
+ CRASH_COND(slot_count == (1 << OBJECTDB_SLOT_MAX_COUNT_BITS));
+
+ uint32_t new_slot_max = slot_max > 0 ? slot_max * 2 : 1;
+ object_slots = (ObjectSlot *)memrealloc(object_slots, sizeof(ObjectSlot) * new_slot_max);
+ for (uint32_t i = slot_max; i < new_slot_max; i++) {
+ object_slots[i].object = nullptr;
+ object_slots[i].is_reference = false;
+ object_slots[i].next_free = i;
+ object_slots[i].validator = 0;
+ }
+ slot_max = new_slot_max;
+ }
+
+ uint32_t slot = object_slots[slot_count].next_free;
+ if (object_slots[slot].object != nullptr) {
+ spin_lock.unlock();
+ ERR_FAIL_COND_V(object_slots[slot].object != nullptr, ObjectID());
+ }
+ object_slots[slot].object = p_object;
+ object_slots[slot].is_reference = p_object->is_reference();
+ validator_counter = (validator_counter + 1) & OBJECTDB_VALIDATOR_MASK;
+ if (unlikely(validator_counter == 0)) {
+ validator_counter = 1;
+ }
+ object_slots[slot].validator = validator_counter;
+
+ uint64_t id = validator_counter;
+ id <<= OBJECTDB_SLOT_MAX_COUNT_BITS;
+ id |= uint64_t(slot);
+
+ if (p_object->is_reference()) {
+ id |= OBJECTDB_REFERENCE_BIT;
+ }
+
+ slot_count++;
+
+ spin_lock.unlock();
+
+ return ObjectID(id);
+}
+
+void ObjectDB::remove_instance(Object *p_object) {
+ uint64_t t = p_object->get_instance_id();
+ uint32_t slot = t & OBJECTDB_SLOT_MAX_COUNT_MASK; //slot is always valid on valid object
+
+ spin_lock.lock();
+
+#ifdef DEBUG_ENABLED
+
+ if (object_slots[slot].object != p_object) {
+ spin_lock.unlock();
+ ERR_FAIL_COND(object_slots[slot].object != p_object);
+ }
+ {
+ uint64_t validator = (t >> OBJECTDB_SLOT_MAX_COUNT_BITS) & OBJECTDB_VALIDATOR_MASK;
+ if (object_slots[slot].validator != validator) {
+ spin_lock.unlock();
+ ERR_FAIL_COND(object_slots[slot].validator != validator);
+ }
+ }
+
+#endif
+ //decrease slot count
+ slot_count--;
+ //set the free slot properly
+ object_slots[slot_count].next_free = slot;
+ //invalidate, so checks against it fail
+ object_slots[slot].validator = 0;
+ object_slots[slot].is_reference = false;
+ object_slots[slot].object = nullptr;
+
+ spin_lock.unlock();
+}
+
+void ObjectDB::setup() {
+ //nothing to do now
+}
+
+void ObjectDB::cleanup() {
+ if (slot_count > 0) {
+ spin_lock.lock();
+
+ WARN_PRINT("ObjectDB instances leaked at exit (run with --verbose for details).");
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ // Ensure calling the native classes because if a leaked instance has a script
+ // that overrides any of those methods, it'd not be OK to call them at this point,
+ // now the scripting languages have already been terminated.
+ MethodBind *node_get_name = ClassDB::get_method("Node", "get_name");
+ MethodBind *resource_get_path = ClassDB::get_method("Resource", "get_path");
+ Callable::CallError call_error;
+
+ for (uint32_t i = 0; i < slot_count; i++) {
+ uint32_t slot = object_slots[i].next_free;
+ Object *obj = object_slots[slot].object;
+
+ String extra_info;
+ if (obj->is_class("Node")) {
+ extra_info = " - Node name: " + String(node_get_name->call(obj, nullptr, 0, call_error));
+ }
+ if (obj->is_class("Resource")) {
+ extra_info = " - Resource path: " + String(resource_get_path->call(obj, nullptr, 0, call_error));
+ }
+
+ uint64_t id = uint64_t(slot) | (uint64_t(object_slots[slot].validator) << OBJECTDB_VALIDATOR_BITS) | (object_slots[slot].is_reference ? OBJECTDB_REFERENCE_BIT : 0);
+ print_line("Leaked instance: " + String(obj->get_class()) + ":" + itos(id) + extra_info);
+ }
+ print_line("Hint: Leaked instances typically happen when nodes are removed from the scene tree (with `remove_child()`) but not freed (with `free()` or `queue_free()`).");
+ }
+ spin_lock.unlock();
+ }
+
+ if (object_slots) {
+ memfree(object_slots);
+ }
+}
diff --git a/core/object/object.h b/core/object/object.h
new file mode 100644
index 0000000000..c79745cf74
--- /dev/null
+++ b/core/object/object.h
@@ -0,0 +1,815 @@
+/*************************************************************************/
+/* object.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OBJECT_H
+#define OBJECT_H
+
+#include "core/object/object_id.h"
+#include "core/os/rw_lock.h"
+#include "core/os/spin_lock.h"
+#include "core/templates/hash_map.h"
+#include "core/templates/list.h"
+#include "core/templates/map.h"
+#include "core/templates/set.h"
+#include "core/templates/vmap.h"
+#include "core/variant/callable_bind.h"
+#include "core/variant/variant.h"
+
+#define VARIANT_ARG_LIST const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant()
+#define VARIANT_ARG_PASS p_arg1, p_arg2, p_arg3, p_arg4, p_arg5
+#define VARIANT_ARG_DECLARE const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5
+#define VARIANT_ARG_MAX 5
+#define VARIANT_ARGPTRS const Variant *argptr[5] = { &p_arg1, &p_arg2, &p_arg3, &p_arg4, &p_arg5 };
+#define VARIANT_ARGPTRS_PASS *argptr[0], *argptr[1], *argptr[2], *argptr[3], *argptr[4]
+#define VARIANT_ARGS_FROM_ARRAY(m_arr) m_arr[0], m_arr[1], m_arr[2], m_arr[3], m_arr[4]
+
+/**
+@author Juan Linietsky <reduzio@gmail.com>
+*/
+
+enum PropertyHint {
+ PROPERTY_HINT_NONE, ///< no hint provided.
+ PROPERTY_HINT_RANGE, ///< hint_text = "min,max,step,slider; //slider is optional"
+ PROPERTY_HINT_EXP_RANGE, ///< hint_text = "min,max,step", exponential edit
+ PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc"
+ PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout")
+ PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer)
+ PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer)
+ PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags)
+ PROPERTY_HINT_LAYERS_2D_RENDER,
+ PROPERTY_HINT_LAYERS_2D_PHYSICS,
+ PROPERTY_HINT_LAYERS_3D_RENDER,
+ PROPERTY_HINT_LAYERS_3D_PHYSICS,
+ PROPERTY_HINT_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,"
+ PROPERTY_HINT_DIR, ///< a directory path must be passed
+ PROPERTY_HINT_GLOBAL_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,"
+ PROPERTY_HINT_GLOBAL_DIR, ///< a directory path must be passed
+ PROPERTY_HINT_RESOURCE_TYPE, ///< a resource object type
+ PROPERTY_HINT_MULTILINE_TEXT, ///< used for string properties that can contain multiple lines
+ PROPERTY_HINT_PLACEHOLDER_TEXT, ///< used to set a placeholder text for string properties
+ PROPERTY_HINT_COLOR_NO_ALPHA, ///< used for ignoring alpha component when editing a color
+ PROPERTY_HINT_IMAGE_COMPRESS_LOSSY,
+ PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS,
+ PROPERTY_HINT_OBJECT_ID,
+ PROPERTY_HINT_TYPE_STRING, ///< a type string, the hint is the base type to choose
+ PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE, ///< so something else can provide this (used in scripts)
+ PROPERTY_HINT_METHOD_OF_VARIANT_TYPE, ///< a method of a type
+ PROPERTY_HINT_METHOD_OF_BASE_TYPE, ///< a method of a base type
+ PROPERTY_HINT_METHOD_OF_INSTANCE, ///< a method of an instance
+ PROPERTY_HINT_METHOD_OF_SCRIPT, ///< a method of a script & base
+ PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE, ///< a property of a type
+ PROPERTY_HINT_PROPERTY_OF_BASE_TYPE, ///< a property of a base type
+ PROPERTY_HINT_PROPERTY_OF_INSTANCE, ///< a property of an instance
+ PROPERTY_HINT_PROPERTY_OF_SCRIPT, ///< a property of a script & base
+ PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send
+ PROPERTY_HINT_NODE_PATH_VALID_TYPES,
+ PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog
+ PROPERTY_HINT_INT_IS_OBJECTID,
+ PROPERTY_HINT_ARRAY_TYPE,
+ PROPERTY_HINT_MAX,
+ // When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit
+};
+
+enum PropertyUsageFlags {
+
+ PROPERTY_USAGE_STORAGE = 1,
+ PROPERTY_USAGE_EDITOR = 2,
+ PROPERTY_USAGE_NETWORK = 4,
+ PROPERTY_USAGE_EDITOR_HELPER = 8,
+ PROPERTY_USAGE_CHECKABLE = 16, //used for editing global variables
+ PROPERTY_USAGE_CHECKED = 32, //used for editing global variables
+ PROPERTY_USAGE_INTERNATIONALIZED = 64, //hint for internationalized strings
+ PROPERTY_USAGE_GROUP = 128, //used for grouping props in the editor
+ PROPERTY_USAGE_CATEGORY = 256,
+ PROPERTY_USAGE_SUBGROUP = 512,
+ PROPERTY_USAGE_NO_INSTANCE_STATE = 2048,
+ PROPERTY_USAGE_RESTART_IF_CHANGED = 4096,
+ PROPERTY_USAGE_SCRIPT_VARIABLE = 8192,
+ PROPERTY_USAGE_STORE_IF_NULL = 16384,
+ PROPERTY_USAGE_ANIMATE_AS_TRIGGER = 32768,
+ PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED = 65536,
+ PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE = 1 << 17,
+ PROPERTY_USAGE_CLASS_IS_ENUM = 1 << 18,
+ PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 19,
+ PROPERTY_USAGE_INTERNAL = 1 << 20,
+ PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 21, // If the object is duplicated also this property will be duplicated
+ PROPERTY_USAGE_HIGH_END_GFX = 1 << 22,
+ PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 23,
+ PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 24,
+ PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 25, // Used in inspector to increment property when keyed in animation player
+ PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 26, // when loading, the resource for this property can be set at the end of loading
+ PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 27, // For Object properties, instantiate them when creating in editor.
+
+ PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK,
+ PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED,
+ PROPERTY_USAGE_NOEDITOR = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_NETWORK,
+};
+
+#define ADD_SIGNAL(m_signal) ClassDB::add_signal(get_class_static(), m_signal)
+#define ADD_PROPERTY(m_property, m_setter, m_getter) ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter))
+#define ADD_PROPERTYI(m_property, m_setter, m_getter, m_index) ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter), m_index)
+#define ADD_PROPERTY_DEFAULT(m_property, m_default) ClassDB::set_property_default_value(get_class_static(), m_property, m_default)
+#define ADD_GROUP(m_name, m_prefix) ClassDB::add_property_group(get_class_static(), m_name, m_prefix)
+#define ADD_SUBGROUP(m_name, m_prefix) ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix)
+
+struct PropertyInfo {
+ Variant::Type type = Variant::NIL;
+ String name;
+ StringName class_name; //for classes
+ PropertyHint hint = PROPERTY_HINT_NONE;
+ String hint_string;
+ uint32_t usage = PROPERTY_USAGE_DEFAULT;
+
+ _FORCE_INLINE_ PropertyInfo added_usage(int p_fl) const {
+ PropertyInfo pi = *this;
+ pi.usage |= p_fl;
+ return pi;
+ }
+
+ operator Dictionary() const;
+
+ static PropertyInfo from_dict(const Dictionary &p_dict);
+
+ PropertyInfo() {}
+
+ PropertyInfo(Variant::Type p_type, const String p_name, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = "", uint32_t p_usage = PROPERTY_USAGE_DEFAULT, const StringName &p_class_name = StringName()) :
+ type(p_type),
+ name(p_name),
+ hint(p_hint),
+ hint_string(p_hint_string),
+ usage(p_usage) {
+ if (hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ class_name = hint_string;
+ } else {
+ class_name = p_class_name;
+ }
+ }
+
+ PropertyInfo(const StringName &p_class_name) :
+ type(Variant::OBJECT),
+ class_name(p_class_name) {}
+
+ bool operator==(const PropertyInfo &p_info) const {
+ return ((type == p_info.type) &&
+ (name == p_info.name) &&
+ (class_name == p_info.class_name) &&
+ (hint == p_info.hint) &&
+ (hint_string == p_info.hint_string) &&
+ (usage == p_info.usage));
+ }
+
+ bool operator<(const PropertyInfo &p_info) const {
+ return name < p_info.name;
+ }
+};
+
+Array convert_property_list(const List<PropertyInfo> *p_list);
+
+struct MethodInfo {
+ String name;
+ PropertyInfo return_val;
+ uint32_t flags; // NOLINT - prevent clang-tidy to assign method_bind.h constant here, it should stay in .cpp.
+ int id = 0;
+ List<PropertyInfo> arguments;
+ Vector<Variant> default_arguments;
+
+ inline bool operator==(const MethodInfo &p_method) const { return id == p_method.id; }
+ inline bool operator<(const MethodInfo &p_method) const { return id == p_method.id ? (name < p_method.name) : (id < p_method.id); }
+
+ operator Dictionary() const;
+
+ static MethodInfo from_dict(const Dictionary &p_dict);
+
+ MethodInfo();
+ MethodInfo(const String &p_name);
+ MethodInfo(const String &p_name, const PropertyInfo &p_param1);
+ MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2);
+ MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3);
+ MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4);
+ MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5);
+ MethodInfo(Variant::Type ret);
+ MethodInfo(Variant::Type ret, const String &p_name);
+ MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1);
+ MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2);
+ MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3);
+ MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4);
+ MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5);
+ MethodInfo(const PropertyInfo &p_ret, const String &p_name);
+ MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1);
+ MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2);
+ MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3);
+ MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4);
+ MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5);
+};
+
+// old cast_to
+//if ( is_type(T::get_class_static()) )
+//return static_cast<T*>(this);
+////else
+//return nullptr;
+
+/*
+ the following is an incomprehensible blob of hacks and workarounds to compensate for many of the fallencies in C++. As a plus, this macro pretty much alone defines the object model.
+*/
+
+#define REVERSE_GET_PROPERTY_LIST \
+public: \
+ _FORCE_INLINE_ bool _is_gpl_reversed() const { return true; }; \
+ \
+private:
+
+#define UNREVERSE_GET_PROPERTY_LIST \
+public: \
+ _FORCE_INLINE_ bool _is_gpl_reversed() const { return false; }; \
+ \
+private:
+
+#define GDCLASS(m_class, m_inherits) \
+private: \
+ void operator=(const m_class &p_rval) {} \
+ mutable StringName _class_name; \
+ friend class ClassDB; \
+ \
+public: \
+ virtual String get_class() const override { \
+ return String(#m_class); \
+ } \
+ virtual const StringName *_get_class_namev() const override { \
+ if (!_class_name) { \
+ _class_name = get_class_static(); \
+ } \
+ return &_class_name; \
+ } \
+ static _FORCE_INLINE_ void *get_class_ptr_static() { \
+ static int ptr; \
+ return &ptr; \
+ } \
+ static _FORCE_INLINE_ String get_class_static() { \
+ return String(#m_class); \
+ } \
+ static _FORCE_INLINE_ String get_parent_class_static() { \
+ return m_inherits::get_class_static(); \
+ } \
+ static void get_inheritance_list_static(List<String> *p_inheritance_list) { \
+ m_inherits::get_inheritance_list_static(p_inheritance_list); \
+ p_inheritance_list->push_back(String(#m_class)); \
+ } \
+ static String get_category_static() { \
+ String category = m_inherits::get_category_static(); \
+ if (_get_category != m_inherits::_get_category) { \
+ if (category != "") { \
+ category += "/"; \
+ } \
+ category += _get_category(); \
+ } \
+ return category; \
+ } \
+ static String inherits_static() { \
+ return String(#m_inherits); \
+ } \
+ virtual bool is_class(const String &p_class) const override { return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); } \
+ virtual bool is_class_ptr(void *p_ptr) const override { return (p_ptr == get_class_ptr_static()) ? true : m_inherits::is_class_ptr(p_ptr); } \
+ \
+ static void get_valid_parents_static(List<String> *p_parents) { \
+ if (m_class::_get_valid_parents_static != m_inherits::_get_valid_parents_static) { \
+ m_class::_get_valid_parents_static(p_parents); \
+ } \
+ \
+ m_inherits::get_valid_parents_static(p_parents); \
+ } \
+ \
+protected: \
+ _FORCE_INLINE_ static void (*_get_bind_methods())() { \
+ return &m_class::_bind_methods; \
+ } \
+ \
+public: \
+ static void initialize_class() { \
+ static bool initialized = false; \
+ if (initialized) { \
+ return; \
+ } \
+ m_inherits::initialize_class(); \
+ ClassDB::_add_class<m_class>(); \
+ if (m_class::_get_bind_methods() != m_inherits::_get_bind_methods()) { \
+ _bind_methods(); \
+ } \
+ initialized = true; \
+ } \
+ \
+protected: \
+ virtual void _initialize_classv() override { \
+ initialize_class(); \
+ } \
+ _FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &) const { \
+ return (bool (Object::*)(const StringName &, Variant &) const) & m_class::_get; \
+ } \
+ virtual bool _getv(const StringName &p_name, Variant &r_ret) const override { \
+ if (m_class::_get_get() != m_inherits::_get_get()) { \
+ if (_get(p_name, r_ret)) { \
+ return true; \
+ } \
+ } \
+ return m_inherits::_getv(p_name, r_ret); \
+ } \
+ _FORCE_INLINE_ bool (Object::*_get_set() const)(const StringName &p_name, const Variant &p_property) { \
+ return (bool (Object::*)(const StringName &, const Variant &)) & m_class::_set; \
+ } \
+ virtual bool _setv(const StringName &p_name, const Variant &p_property) override { \
+ if (m_inherits::_setv(p_name, p_property)) { \
+ return true; \
+ } \
+ if (m_class::_get_set() != m_inherits::_get_set()) { \
+ return _set(p_name, p_property); \
+ } \
+ return false; \
+ } \
+ _FORCE_INLINE_ void (Object::*_get_get_property_list() const)(List<PropertyInfo> * p_list) const { \
+ return (void (Object::*)(List<PropertyInfo> *) const) & m_class::_get_property_list; \
+ } \
+ virtual void _get_property_listv(List<PropertyInfo> *p_list, bool p_reversed) const override { \
+ if (!p_reversed) { \
+ m_inherits::_get_property_listv(p_list, p_reversed); \
+ } \
+ p_list->push_back(PropertyInfo(Variant::NIL, get_class_static(), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); \
+ if (!_is_gpl_reversed()) { \
+ ClassDB::get_property_list(#m_class, p_list, true, this); \
+ } \
+ if (m_class::_get_get_property_list() != m_inherits::_get_get_property_list()) { \
+ _get_property_list(p_list); \
+ } \
+ if (_is_gpl_reversed()) { \
+ ClassDB::get_property_list(#m_class, p_list, true, this); \
+ } \
+ if (p_reversed) { \
+ m_inherits::_get_property_listv(p_list, p_reversed); \
+ } \
+ } \
+ _FORCE_INLINE_ void (Object::*_get_notification() const)(int) { \
+ return (void (Object::*)(int)) & m_class::_notification; \
+ } \
+ virtual void _notificationv(int p_notification, bool p_reversed) override { \
+ if (!p_reversed) { \
+ m_inherits::_notificationv(p_notification, p_reversed); \
+ } \
+ if (m_class::_get_notification() != m_inherits::_get_notification()) { \
+ _notification(p_notification); \
+ } \
+ if (p_reversed) { \
+ m_inherits::_notificationv(p_notification, p_reversed); \
+ } \
+ } \
+ \
+private:
+
+#define OBJ_CATEGORY(m_category) \
+protected: \
+ _FORCE_INLINE_ static String _get_category() { return m_category; } \
+ \
+private:
+
+#define OBJ_SAVE_TYPE(m_class) \
+public: \
+ virtual String get_save_class() const override { return #m_class; } \
+ \
+private:
+
+class ScriptInstance;
+
+class Object {
+public:
+ enum ConnectFlags {
+
+ CONNECT_DEFERRED = 1,
+ CONNECT_PERSIST = 2, // hint for scene to save this connection
+ CONNECT_ONESHOT = 4,
+ CONNECT_REFERENCE_COUNTED = 8,
+ };
+
+ struct Connection {
+ ::Signal signal;
+ Callable callable;
+
+ uint32_t flags = 0;
+ Vector<Variant> binds;
+ bool operator<(const Connection &p_conn) const;
+
+ operator Variant() const;
+
+ Connection() {}
+ Connection(const Variant &p_variant);
+ };
+
+private:
+ enum {
+ MAX_SCRIPT_INSTANCE_BINDINGS = 8
+ };
+
+#ifdef DEBUG_ENABLED
+ friend struct _ObjectDebugLock;
+#endif
+ friend bool predelete_handler(Object *);
+ friend void postinitialize_handler(Object *);
+
+ struct SignalData {
+ struct Slot {
+ int reference_count = 0;
+ Connection conn;
+ List<Connection>::Element *cE = nullptr;
+ };
+
+ MethodInfo user;
+ VMap<Callable, Slot> slot_map;
+ };
+
+ HashMap<StringName, SignalData> signal_map;
+ List<Connection> connections;
+#ifdef DEBUG_ENABLED
+ SafeRefCount _lock_index;
+#endif
+ bool _block_signals = false;
+ int _predelete_ok = 0;
+ Set<Object *> change_receptors;
+ ObjectID _instance_id;
+ bool _predelete();
+ void _postinitialize();
+ bool _can_translate = true;
+ bool _emitting = false;
+#ifdef TOOLS_ENABLED
+ bool _edited = false;
+ uint32_t _edited_version = 0;
+ Set<String> editor_section_folding;
+#endif
+ ScriptInstance *script_instance = nullptr;
+ Variant script; //reference does not yet exist, store it in a
+ Dictionary metadata;
+ mutable StringName _class_name;
+ mutable const StringName *_class_ptr = nullptr;
+
+ void _add_user_signal(const String &p_name, const Array &p_args = Array());
+ bool _has_user_signal(const StringName &p_name) const;
+ Variant _emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ Array _get_signal_list() const;
+ Array _get_signal_connection_list(const String &p_signal) const;
+ Array _get_incoming_connections() const;
+ void _set_bind(const String &p_set, const Variant &p_value);
+ Variant _get_bind(const String &p_name) const;
+ void _set_indexed_bind(const NodePath &p_name, const Variant &p_value);
+ Variant _get_indexed_bind(const NodePath &p_name) const;
+
+ void property_list_changed_notify();
+
+ _FORCE_INLINE_ void _construct_object(bool p_reference);
+
+ friend class Reference;
+ bool type_is_reference = false;
+ uint32_t instance_binding_count = 0;
+ void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS];
+
+ Object(bool p_reference);
+
+protected:
+ virtual void _initialize_classv() { initialize_class(); }
+ virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; };
+ virtual bool _getv(const StringName &p_name, Variant &r_property) const { return false; };
+ virtual void _get_property_listv(List<PropertyInfo> *p_list, bool p_reversed) const {};
+ virtual void _notificationv(int p_notification, bool p_reversed) {}
+
+ static String _get_category() { return ""; }
+ static void _bind_methods();
+ bool _set(const StringName &p_name, const Variant &p_property) { return false; };
+ bool _get(const StringName &p_name, Variant &r_property) const { return false; };
+ void _get_property_list(List<PropertyInfo> *p_list) const {};
+ void _notification(int p_notification) {}
+
+ _FORCE_INLINE_ static void (*_get_bind_methods())() {
+ return &Object::_bind_methods;
+ }
+ _FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &r_ret) const {
+ return &Object::_get;
+ }
+ _FORCE_INLINE_ bool (Object::*_get_set() const)(const StringName &p_name, const Variant &p_property) {
+ return &Object::_set;
+ }
+ _FORCE_INLINE_ void (Object::*_get_get_property_list() const)(List<PropertyInfo> *p_list) const {
+ return &Object::_get_property_list;
+ }
+ _FORCE_INLINE_ void (Object::*_get_notification() const)(int) {
+ return &Object::_notification;
+ }
+ static void get_valid_parents_static(List<String> *p_parents);
+ static void _get_valid_parents_static(List<String> *p_parents);
+
+ void cancel_delete();
+
+ virtual void _changed_callback(Object *p_changed, const char *p_prop);
+
+ //Variant _call_bind(const StringName& p_name, const Variant& p_arg1 = Variant(), const Variant& p_arg2 = Variant(), const Variant& p_arg3 = Variant(), const Variant& p_arg4 = Variant());
+ //void _call_deferred_bind(const StringName& p_name, const Variant& p_arg1 = Variant(), const Variant& p_arg2 = Variant(), const Variant& p_arg3 = Variant(), const Variant& p_arg4 = Variant());
+
+ Variant _call_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ Variant _call_deferred_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+
+ virtual const StringName *_get_class_namev() const {
+ if (!_class_name) {
+ _class_name = get_class_static();
+ }
+ return &_class_name;
+ }
+
+ Vector<String> _get_meta_list_bind() const;
+ Array _get_property_list_bind() const;
+ Array _get_method_list_bind() const;
+
+ void _clear_internal_resource_paths(const Variant &p_var);
+
+ friend class ClassDB;
+ virtual void _validate_property(PropertyInfo &property) const;
+
+ void _disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force = false);
+
+public: //should be protected, but bug in clang++
+ static void initialize_class();
+ _FORCE_INLINE_ static void register_custom_data_to_otdb() {}
+
+public:
+#ifdef TOOLS_ENABLED
+ _FORCE_INLINE_ void _change_notify(const char *p_property = "") {
+ _edited = true;
+ for (Set<Object *>::Element *E = change_receptors.front(); E; E = E->next()) {
+ ((Object *)(E->get()))->_changed_callback(this, p_property);
+ }
+ }
+#else
+ _FORCE_INLINE_ void _change_notify(const char *p_what = "") {}
+#endif
+ static void *get_class_ptr_static() {
+ static int ptr;
+ return &ptr;
+ }
+
+ bool _is_gpl_reversed() const { return false; }
+
+ _FORCE_INLINE_ ObjectID get_instance_id() const { return _instance_id; }
+
+ // this is used for editors
+ void add_change_receptor(Object *p_receptor);
+ void remove_change_receptor(Object *p_receptor);
+
+ template <class T>
+ static T *cast_to(Object *p_object) {
+#ifndef NO_SAFE_CAST
+ return dynamic_cast<T *>(p_object);
+#else
+ if (!p_object) {
+ return nullptr;
+ }
+ if (p_object->is_class_ptr(T::get_class_ptr_static())) {
+ return static_cast<T *>(p_object);
+ } else {
+ return nullptr;
+ }
+#endif
+ }
+
+ template <class T>
+ static const T *cast_to(const Object *p_object) {
+#ifndef NO_SAFE_CAST
+ return dynamic_cast<const T *>(p_object);
+#else
+ if (!p_object) {
+ return nullptr;
+ }
+ if (p_object->is_class_ptr(T::get_class_ptr_static())) {
+ return static_cast<const T *>(p_object);
+ } else {
+ return nullptr;
+ }
+#endif
+ }
+
+ enum {
+ NOTIFICATION_POSTINITIALIZE = 0,
+ NOTIFICATION_PREDELETE = 1
+ };
+
+ /* TYPE API */
+ static void get_inheritance_list_static(List<String> *p_inheritance_list) { p_inheritance_list->push_back("Object"); }
+
+ static String get_class_static() { return "Object"; }
+ static String get_parent_class_static() { return String(); }
+ static String get_category_static() { return String(); }
+
+ virtual String get_class() const { return "Object"; }
+ virtual String get_save_class() const { return get_class(); } //class stored when saving
+
+ virtual bool is_class(const String &p_class) const { return (p_class == "Object"); }
+ virtual bool is_class_ptr(void *p_ptr) const { return get_class_ptr_static() == p_ptr; }
+
+ _FORCE_INLINE_ const StringName &get_class_name() const {
+ if (!_class_ptr) {
+ return *_get_class_namev();
+ } else {
+ return *_class_ptr;
+ }
+ }
+
+ /* IAPI */
+ //void set(const String& p_name, const Variant& p_value);
+ //Variant get(const String& p_name) const;
+
+ void set(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr);
+ Variant get(const StringName &p_name, bool *r_valid = nullptr) const;
+ void set_indexed(const Vector<StringName> &p_names, const Variant &p_value, bool *r_valid = nullptr);
+ Variant get_indexed(const Vector<StringName> &p_names, bool *r_valid = nullptr) const;
+
+ void get_property_list(List<PropertyInfo> *p_list, bool p_reversed = false) const;
+
+ bool has_method(const StringName &p_method) const;
+ void get_method_list(List<MethodInfo> *p_list) const;
+ Variant callv(const StringName &p_method, const Array &p_args);
+ virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ Variant call(const StringName &p_name, VARIANT_ARG_LIST); // C++ helper
+
+ void notification(int p_notification, bool p_reversed = false);
+ virtual String to_string();
+
+ //used mainly by script, get and set all INCLUDING string
+ virtual Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const;
+ virtual void setvar(const Variant &p_key, const Variant &p_value, bool *r_valid = nullptr);
+
+ /* SCRIPT */
+
+ void set_script(const Variant &p_script);
+ Variant get_script() const;
+
+ /* SCRIPT */
+
+ bool has_meta(const String &p_name) const;
+ void set_meta(const String &p_name, const Variant &p_value);
+ void remove_meta(const String &p_name);
+ Variant get_meta(const String &p_name) const;
+ void get_meta_list(List<String> *p_list) const;
+
+#ifdef TOOLS_ENABLED
+ void set_edited(bool p_edited);
+ bool is_edited() const;
+ uint32_t get_edited_version() const; //this function is used to check when something changed beyond a point, it's used mainly for generating previews
+#endif
+
+ void set_script_instance(ScriptInstance *p_instance);
+ _FORCE_INLINE_ ScriptInstance *get_script_instance() const { return script_instance; }
+
+ void set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance); //some script languages can't control instance creation, so this function eases the process
+
+ void add_user_signal(const MethodInfo &p_signal);
+ Error emit_signal(const StringName &p_name, VARIANT_ARG_LIST);
+ Error emit_signal(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;
+
+ Error connect_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, const Vector<Variant> &p_binds = Vector<Variant>(), uint32_t p_flags = 0);
+ void disconnect_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method);
+ bool is_connected_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) const;
+
+ Error connect(const StringName &p_signal, const Callable &p_callable, const Vector<Variant> &p_binds = Vector<Variant>(), 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;
+
+ void call_deferred(const StringName &p_method, VARIANT_ARG_LIST);
+ void set_deferred(const StringName &p_property, const Variant &p_value);
+
+ void set_block_signals(bool p_block);
+ bool is_blocking_signals() const;
+
+ Variant::Type get_static_property_type(const StringName &p_property, bool *r_valid = nullptr) const;
+ Variant::Type get_static_property_type_indexed(const Vector<StringName> &p_path, bool *r_valid = nullptr) const;
+
+ virtual void get_translatable_strings(List<String> *p_strings) const;
+
+ virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const;
+
+ String tr(const StringName &p_message, const StringName &p_context = "") const; // translate message (internationalization)
+ String tr_n(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
+
+ bool _is_queued_for_deletion = false; // set to true by SceneTree::queue_delete()
+ bool is_queued_for_deletion() const;
+
+ _FORCE_INLINE_ void set_message_translation(bool p_enable) { _can_translate = p_enable; }
+ _FORCE_INLINE_ bool can_translate_messages() const { return _can_translate; }
+
+#ifdef TOOLS_ENABLED
+ void editor_set_section_unfold(const String &p_section, bool p_unfolded);
+ bool editor_is_section_unfolded(const String &p_section);
+ const Set<String> &editor_get_section_folding() const { return editor_section_folding; }
+ void editor_clear_section_folding() { editor_section_folding.clear(); }
+
+#endif
+
+ //used by script languages to store binding data
+ void *get_script_instance_binding(int p_script_language_index);
+ bool has_script_instance_binding(int p_script_language_index);
+ void set_script_instance_binding(int p_script_language_index, void *p_data);
+
+ void clear_internal_resource_paths();
+
+ _ALWAYS_INLINE_ bool is_reference() const { return type_is_reference; }
+
+ Object();
+ virtual ~Object();
+};
+
+bool predelete_handler(Object *p_object);
+void postinitialize_handler(Object *p_object);
+
+class ObjectDB {
+//this needs to add up to 63, 1 bit is for reference
+#define OBJECTDB_VALIDATOR_BITS 39
+#define OBJECTDB_VALIDATOR_MASK ((uint64_t(1) << OBJECTDB_VALIDATOR_BITS) - 1)
+#define OBJECTDB_SLOT_MAX_COUNT_BITS 24
+#define OBJECTDB_SLOT_MAX_COUNT_MASK ((uint64_t(1) << OBJECTDB_SLOT_MAX_COUNT_BITS) - 1)
+#define OBJECTDB_REFERENCE_BIT (uint64_t(1) << (OBJECTDB_SLOT_MAX_COUNT_BITS + OBJECTDB_VALIDATOR_BITS))
+
+ struct ObjectSlot { //128 bits per slot
+ uint64_t validator : OBJECTDB_VALIDATOR_BITS;
+ uint64_t next_free : OBJECTDB_SLOT_MAX_COUNT_BITS;
+ uint64_t is_reference : 1;
+ Object *object;
+ };
+
+ static SpinLock spin_lock;
+ static uint32_t slot_count;
+ static uint32_t slot_max;
+ static ObjectSlot *object_slots;
+ static uint64_t validator_counter;
+
+ friend class Object;
+ friend void unregister_core_types();
+ static void cleanup();
+
+ static ObjectID add_instance(Object *p_object);
+ static void remove_instance(Object *p_object);
+
+ friend void register_core_types();
+ static void setup();
+
+public:
+ typedef void (*DebugFunc)(Object *p_obj);
+
+ _ALWAYS_INLINE_ static Object *get_instance(ObjectID p_instance_id) {
+ uint64_t id = p_instance_id;
+ uint32_t slot = id & OBJECTDB_SLOT_MAX_COUNT_MASK;
+
+ ERR_FAIL_COND_V(slot >= slot_max, nullptr); //this should never happen unless RID is corrupted
+
+ spin_lock.lock();
+
+ uint64_t validator = (id >> OBJECTDB_SLOT_MAX_COUNT_BITS) & OBJECTDB_VALIDATOR_MASK;
+
+ if (unlikely(object_slots[slot].validator != validator)) {
+ spin_lock.unlock();
+ return nullptr;
+ }
+
+ Object *object = object_slots[slot].object;
+
+ spin_lock.unlock();
+
+ return object;
+ }
+ static void debug_objects(DebugFunc p_func);
+ static int get_object_count();
+};
+
+#endif // OBJECT_H
diff --git a/core/object/object_id.h b/core/object/object_id.h
new file mode 100644
index 0000000000..63b0c27af8
--- /dev/null
+++ b/core/object/object_id.h
@@ -0,0 +1,63 @@
+/*************************************************************************/
+/* object_id.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OBJECT_ID_H
+#define OBJECT_ID_H
+
+#include "core/typedefs.h"
+
+// Class to store an object ID (int64)
+// needs to be compatile with int64 because this is what Variant uses
+// Also, need to be explicitly only castable to 64 bits integer types
+// to avoid bugs due to loss of precision
+
+class ObjectID {
+ uint64_t id = 0;
+
+public:
+ _ALWAYS_INLINE_ bool is_reference() const { return (id & (uint64_t(1) << 63)) != 0; }
+ _ALWAYS_INLINE_ bool is_valid() const { return id != 0; }
+ _ALWAYS_INLINE_ bool is_null() const { return id == 0; }
+ _ALWAYS_INLINE_ operator uint64_t() const { return id; }
+ _ALWAYS_INLINE_ operator int64_t() const { return id; }
+
+ _ALWAYS_INLINE_ bool operator==(const ObjectID &p_id) const { return id == p_id.id; }
+ _ALWAYS_INLINE_ bool operator!=(const ObjectID &p_id) const { return id != p_id.id; }
+ _ALWAYS_INLINE_ bool operator<(const ObjectID &p_id) const { return id < p_id.id; }
+
+ _ALWAYS_INLINE_ void operator=(int64_t p_int64) { id = p_int64; }
+ _ALWAYS_INLINE_ void operator=(uint64_t p_uint64) { id = p_uint64; }
+
+ _ALWAYS_INLINE_ ObjectID() {}
+ _ALWAYS_INLINE_ explicit ObjectID(const uint64_t p_id) { id = p_id; }
+ _ALWAYS_INLINE_ explicit ObjectID(const int64_t p_id) { id = p_id; }
+};
+
+#endif // OBJECT_ID_H
diff --git a/core/object/reference.cpp b/core/object/reference.cpp
new file mode 100644
index 0000000000..ce95d83dfc
--- /dev/null
+++ b/core/object/reference.cpp
@@ -0,0 +1,132 @@
+/*************************************************************************/
+/* reference.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "reference.h"
+
+#include "core/object/script_language.h"
+
+bool Reference::init_ref() {
+ if (reference()) {
+ if (!is_referenced() && refcount_init.unref()) {
+ unreference(); // first referencing is already 1, so compensate for the ref above
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void Reference::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("init_ref"), &Reference::init_ref);
+ ClassDB::bind_method(D_METHOD("reference"), &Reference::reference);
+ ClassDB::bind_method(D_METHOD("unreference"), &Reference::unreference);
+}
+
+int Reference::reference_get_count() const {
+ return refcount.get();
+}
+
+bool Reference::reference() {
+ uint32_t rc_val = refcount.refval();
+ bool success = rc_val != 0;
+
+ if (success && rc_val <= 2 /* higher is not relevant */) {
+ if (get_script_instance()) {
+ get_script_instance()->refcount_incremented();
+ }
+ if (instance_binding_count > 0 && !ScriptServer::are_languages_finished()) {
+ for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
+ if (_script_instance_bindings[i]) {
+ ScriptServer::get_language(i)->refcount_incremented_instance_binding(this);
+ }
+ }
+ }
+ }
+
+ return success;
+}
+
+bool Reference::unreference() {
+ uint32_t rc_val = refcount.unrefval();
+ bool die = rc_val == 0;
+
+ if (rc_val <= 1 /* higher is not relevant */) {
+ if (get_script_instance()) {
+ bool script_ret = get_script_instance()->refcount_decremented();
+ die = die && script_ret;
+ }
+ if (instance_binding_count > 0 && !ScriptServer::are_languages_finished()) {
+ for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
+ if (_script_instance_bindings[i]) {
+ bool script_ret = ScriptServer::get_language(i)->refcount_decremented_instance_binding(this);
+ die = die && script_ret;
+ }
+ }
+ }
+ }
+
+ return die;
+}
+
+Reference::Reference() :
+ Object(true) {
+ refcount.init();
+ refcount_init.init();
+}
+
+Variant WeakRef::get_ref() const {
+ if (ref.is_null()) {
+ return Variant();
+ }
+
+ Object *obj = ObjectDB::get_instance(ref);
+ if (!obj) {
+ return Variant();
+ }
+ Reference *r = cast_to<Reference>(obj);
+ if (r) {
+ return REF(r);
+ }
+
+ return obj;
+}
+
+void WeakRef::set_obj(Object *p_object) {
+ ref = p_object ? p_object->get_instance_id() : ObjectID();
+}
+
+void WeakRef::set_ref(const REF &p_ref) {
+ ref = p_ref.is_valid() ? p_ref->get_instance_id() : ObjectID();
+}
+
+void WeakRef::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_ref"), &WeakRef::get_ref);
+}
diff --git a/core/object/reference.h b/core/object/reference.h
new file mode 100644
index 0000000000..575f1cd914
--- /dev/null
+++ b/core/object/reference.h
@@ -0,0 +1,301 @@
+/*************************************************************************/
+/* reference.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef REFERENCE_H
+#define REFERENCE_H
+
+#include "core/object/class_db.h"
+#include "core/templates/safe_refcount.h"
+
+class Reference : public Object {
+ GDCLASS(Reference, Object);
+ SafeRefCount refcount;
+ SafeRefCount refcount_init;
+
+protected:
+ static void _bind_methods();
+
+public:
+ _FORCE_INLINE_ bool is_referenced() const { return refcount_init.get() != 1; }
+ bool init_ref();
+ bool reference(); // returns false if refcount is at zero and didn't get increased
+ bool unreference();
+ int reference_get_count() const;
+
+ Reference();
+ ~Reference() {}
+};
+
+template <class T>
+class Ref {
+ T *reference = nullptr;
+
+ void ref(const Ref &p_from) {
+ if (p_from.reference == reference) {
+ return;
+ }
+
+ unref();
+
+ reference = p_from.reference;
+ if (reference) {
+ reference->reference();
+ }
+ }
+
+ void ref_pointer(T *p_ref) {
+ ERR_FAIL_COND(!p_ref);
+
+ if (p_ref->init_ref()) {
+ reference = p_ref;
+ }
+ }
+
+ //virtual Reference * get_reference() const { return reference; }
+public:
+ _FORCE_INLINE_ bool operator==(const T *p_ptr) const {
+ return reference == p_ptr;
+ }
+ _FORCE_INLINE_ bool operator!=(const T *p_ptr) const {
+ return reference != p_ptr;
+ }
+
+ _FORCE_INLINE_ bool operator<(const Ref<T> &p_r) const {
+ return reference < p_r.reference;
+ }
+ _FORCE_INLINE_ bool operator==(const Ref<T> &p_r) const {
+ return reference == p_r.reference;
+ }
+ _FORCE_INLINE_ bool operator!=(const Ref<T> &p_r) const {
+ return reference != p_r.reference;
+ }
+
+ _FORCE_INLINE_ T *operator->() {
+ return reference;
+ }
+
+ _FORCE_INLINE_ T *operator*() {
+ 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 {
+ return reference;
+ }
+
+ operator Variant() const {
+ return Variant(reference);
+ }
+
+ void operator=(const Ref &p_from) {
+ ref(p_from);
+ }
+
+ template <class T_Other>
+ void operator=(const Ref<T_Other> &p_from) {
+ Reference *refb = const_cast<Reference *>(static_cast<const Reference *>(p_from.ptr()));
+ if (!refb) {
+ unref();
+ return;
+ }
+ Ref r;
+ r.reference = Object::cast_to<T>(refb);
+ ref(r);
+ r.reference = nullptr;
+ }
+
+ void operator=(const Variant &p_variant) {
+ Object *object = p_variant.get_validated_object();
+
+ if (object == reference) {
+ return;
+ }
+
+ unref();
+
+ if (!object) {
+ return;
+ }
+
+ T *r = Object::cast_to<T>(object);
+ if (r && r->reference()) {
+ reference = r;
+ }
+ }
+
+ template <class T_Other>
+ void reference_ptr(T_Other *p_ptr) {
+ if (reference == p_ptr) {
+ return;
+ }
+ unref();
+
+ T *r = Object::cast_to<T>(p_ptr);
+ if (r) {
+ ref_pointer(r);
+ }
+ }
+
+ Ref(const Ref &p_from) {
+ ref(p_from);
+ }
+
+ template <class T_Other>
+ Ref(const Ref<T_Other> &p_from) {
+ Reference *refb = const_cast<Reference *>(static_cast<const Reference *>(p_from.ptr()));
+ if (!refb) {
+ unref();
+ return;
+ }
+ Ref r;
+ r.reference = Object::cast_to<T>(refb);
+ ref(r);
+ r.reference = nullptr;
+ }
+
+ Ref(T *p_reference) {
+ if (p_reference) {
+ ref_pointer(p_reference);
+ }
+ }
+
+ Ref(const Variant &p_variant) {
+ Object *object = p_variant.get_validated_object();
+
+ if (!object) {
+ return;
+ }
+
+ T *r = Object::cast_to<T>(object);
+ if (r && r->reference()) {
+ reference = r;
+ }
+ }
+
+ inline bool is_valid() const { return reference != nullptr; }
+ inline bool is_null() const { return reference == nullptr; }
+
+ void unref() {
+ //TODO this should be moved to mutexes, since this engine does not really
+ // do a lot of referencing on references and stuff
+ // mutexes will avoid more crashes?
+
+ if (reference && reference->unreference()) {
+ memdelete(reference);
+ }
+ reference = nullptr;
+ }
+
+ void instance() {
+ ref(memnew(T));
+ }
+
+ Ref() {}
+
+ ~Ref() {
+ unref();
+ }
+};
+
+typedef Ref<Reference> REF;
+
+class WeakRef : public Reference {
+ GDCLASS(WeakRef, Reference);
+
+ ObjectID ref;
+
+protected:
+ static void _bind_methods();
+
+public:
+ Variant get_ref() const;
+ void set_obj(Object *p_object);
+ void set_ref(const REF &p_ref);
+
+ WeakRef() {}
+};
+
+#ifdef PTRCALL_ENABLED
+
+template <class T>
+struct PtrToArg<Ref<T>> {
+ _FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) {
+ return Ref<T>(const_cast<T *>(reinterpret_cast<const T *>(p_ptr)));
+ }
+
+ _FORCE_INLINE_ static void encode(Ref<T> p_val, const void *p_ptr) {
+ *(Ref<Reference> *)p_ptr = p_val;
+ }
+};
+
+template <class T>
+struct PtrToArg<const Ref<T> &> {
+ _FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) {
+ return Ref<T>((T *)p_ptr);
+ }
+};
+
+#endif // PTRCALL_ENABLED
+
+#ifdef DEBUG_METHODS_ENABLED
+
+template <class T>
+struct GetTypeInfo<Ref<T>> {
+ static const Variant::Type VARIANT_TYPE = Variant::OBJECT;
+ static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;
+
+ static inline PropertyInfo get_class_info() {
+ return PropertyInfo(Variant::OBJECT, String(), PROPERTY_HINT_RESOURCE_TYPE, T::get_class_static());
+ }
+};
+
+template <class T>
+struct GetTypeInfo<const Ref<T> &> {
+ static const Variant::Type VARIANT_TYPE = Variant::OBJECT;
+ static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;
+
+ static inline PropertyInfo get_class_info() {
+ return PropertyInfo(Variant::OBJECT, String(), PROPERTY_HINT_RESOURCE_TYPE, T::get_class_static());
+ }
+};
+
+#endif // DEBUG_METHODS_ENABLED
+
+#endif // REFERENCE_H
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
new file mode 100644
index 0000000000..17ac75e19f
--- /dev/null
+++ b/core/object/script_language.cpp
@@ -0,0 +1,598 @@
+/*************************************************************************/
+/* script_language.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "script_language.h"
+
+#include "core/config/project_settings.h"
+#include "core/core_string_names.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
+
+#include <stdint.h>
+
+ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES];
+int ScriptServer::_language_count = 0;
+
+bool ScriptServer::scripting_enabled = true;
+bool ScriptServer::reload_scripts_on_save = false;
+bool ScriptServer::languages_finished = false;
+ScriptEditRequestFunction ScriptServer::edit_request_func = nullptr;
+
+void Script::_notification(int p_what) {
+ if (p_what == NOTIFICATION_POSTINITIALIZE) {
+ if (EngineDebugger::is_active()) {
+ EngineDebugger::get_script_debugger()->set_break_language(get_language());
+ }
+ }
+}
+
+Variant Script::_get_property_default_value(const StringName &p_property) {
+ Variant ret;
+ get_property_default_value(p_property, ret);
+ return ret;
+}
+
+Array Script::_get_script_property_list() {
+ Array ret;
+ List<PropertyInfo> list;
+ get_script_property_list(&list);
+ for (List<PropertyInfo>::Element *E = list.front(); E; E = E->next()) {
+ ret.append(E->get().operator Dictionary());
+ }
+ return ret;
+}
+
+Array Script::_get_script_method_list() {
+ Array ret;
+ List<MethodInfo> list;
+ get_script_method_list(&list);
+ for (List<MethodInfo>::Element *E = list.front(); E; E = E->next()) {
+ ret.append(E->get().operator Dictionary());
+ }
+ return ret;
+}
+
+Array Script::_get_script_signal_list() {
+ Array ret;
+ List<MethodInfo> list;
+ get_script_signal_list(&list);
+ for (List<MethodInfo>::Element *E = list.front(); E; E = E->next()) {
+ ret.append(E->get().operator Dictionary());
+ }
+ return ret;
+}
+
+Dictionary Script::_get_script_constant_map() {
+ Dictionary ret;
+ Map<StringName, Variant> map;
+ get_constants(&map);
+ for (Map<StringName, Variant>::Element *E = map.front(); E; E = E->next()) {
+ ret[E->key()] = E->value();
+ }
+ return ret;
+}
+
+void Script::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("can_instance"), &Script::can_instance);
+ //ClassDB::bind_method(D_METHOD("instance_create","base_object"),&Script::instance_create);
+ ClassDB::bind_method(D_METHOD("instance_has", "base_object"), &Script::instance_has);
+ ClassDB::bind_method(D_METHOD("has_source_code"), &Script::has_source_code);
+ ClassDB::bind_method(D_METHOD("get_source_code"), &Script::get_source_code);
+ ClassDB::bind_method(D_METHOD("set_source_code", "source"), &Script::set_source_code);
+ ClassDB::bind_method(D_METHOD("reload", "keep_state"), &Script::reload, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_base_script"), &Script::get_base_script);
+ ClassDB::bind_method(D_METHOD("get_instance_base_type"), &Script::get_instance_base_type);
+
+ ClassDB::bind_method(D_METHOD("has_script_signal", "signal_name"), &Script::has_script_signal);
+
+ ClassDB::bind_method(D_METHOD("get_script_property_list"), &Script::_get_script_property_list);
+ ClassDB::bind_method(D_METHOD("get_script_method_list"), &Script::_get_script_method_list);
+ ClassDB::bind_method(D_METHOD("get_script_signal_list"), &Script::_get_script_signal_list);
+ ClassDB::bind_method(D_METHOD("get_script_constant_map"), &Script::_get_script_constant_map);
+ ClassDB::bind_method(D_METHOD("get_property_default_value", "property"), &Script::_get_property_default_value);
+
+ ClassDB::bind_method(D_METHOD("is_tool"), &Script::is_tool);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_code", PROPERTY_HINT_NONE, "", 0), "set_source_code", "get_source_code");
+}
+
+void ScriptServer::set_scripting_enabled(bool p_enabled) {
+ scripting_enabled = p_enabled;
+}
+
+bool ScriptServer::is_scripting_enabled() {
+ return scripting_enabled;
+}
+
+ScriptLanguage *ScriptServer::get_language(int p_idx) {
+ ERR_FAIL_INDEX_V(p_idx, _language_count, nullptr);
+
+ return _languages[p_idx];
+}
+
+void ScriptServer::register_language(ScriptLanguage *p_language) {
+ ERR_FAIL_COND(_language_count >= MAX_LANGUAGES);
+ _languages[_language_count++] = p_language;
+}
+
+void ScriptServer::unregister_language(ScriptLanguage *p_language) {
+ for (int i = 0; i < _language_count; i++) {
+ if (_languages[i] == p_language) {
+ _language_count--;
+ if (i < _language_count) {
+ SWAP(_languages[i], _languages[_language_count]);
+ }
+ return;
+ }
+ }
+}
+
+void ScriptServer::init_languages() {
+ { //load global classes
+ global_classes_clear();
+ if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) {
+ Array script_classes = ProjectSettings::get_singleton()->get("_global_script_classes");
+
+ for (int i = 0; i < script_classes.size(); i++) {
+ Dictionary c = script_classes[i];
+ if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) {
+ continue;
+ }
+ add_global_class(c["class"], c["base"], c["language"], c["path"]);
+ }
+ }
+ }
+
+ for (int i = 0; i < _language_count; i++) {
+ _languages[i]->init();
+ }
+}
+
+void ScriptServer::finish_languages() {
+ for (int i = 0; i < _language_count; i++) {
+ _languages[i]->finish();
+ }
+ global_classes_clear();
+ languages_finished = true;
+}
+
+void ScriptServer::set_reload_scripts_on_save(bool p_enable) {
+ reload_scripts_on_save = p_enable;
+}
+
+bool ScriptServer::is_reload_scripts_on_save_enabled() {
+ return reload_scripts_on_save;
+}
+
+void ScriptServer::thread_enter() {
+ for (int i = 0; i < _language_count; i++) {
+ _languages[i]->thread_enter();
+ }
+}
+
+void ScriptServer::thread_exit() {
+ for (int i = 0; i < _language_count; i++) {
+ _languages[i]->thread_exit();
+ }
+}
+
+HashMap<StringName, ScriptServer::GlobalScriptClass> ScriptServer::global_classes;
+
+void ScriptServer::global_classes_clear() {
+ global_classes.clear();
+}
+
+void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path) {
+ ERR_FAIL_COND_MSG(p_class == p_base || (global_classes.has(p_base) && get_global_class_native_base(p_base) == p_class), "Cyclic inheritance in script class.");
+ GlobalScriptClass g;
+ g.language = p_language;
+ g.path = p_path;
+ g.base = p_base;
+ global_classes[p_class] = g;
+}
+
+void ScriptServer::remove_global_class(const StringName &p_class) {
+ global_classes.erase(p_class);
+}
+
+bool ScriptServer::is_global_class(const StringName &p_class) {
+ return global_classes.has(p_class);
+}
+
+StringName ScriptServer::get_global_class_language(const StringName &p_class) {
+ ERR_FAIL_COND_V(!global_classes.has(p_class), StringName());
+ return global_classes[p_class].language;
+}
+
+String ScriptServer::get_global_class_path(const String &p_class) {
+ ERR_FAIL_COND_V(!global_classes.has(p_class), String());
+ return global_classes[p_class].path;
+}
+
+StringName ScriptServer::get_global_class_base(const String &p_class) {
+ ERR_FAIL_COND_V(!global_classes.has(p_class), String());
+ return global_classes[p_class].base;
+}
+
+StringName ScriptServer::get_global_class_native_base(const String &p_class) {
+ ERR_FAIL_COND_V(!global_classes.has(p_class), String());
+ String base = global_classes[p_class].base;
+ while (global_classes.has(base)) {
+ base = global_classes[base].base;
+ }
+ return base;
+}
+
+void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) {
+ const StringName *K = nullptr;
+ List<StringName> classes;
+ while ((K = global_classes.next(K))) {
+ classes.push_back(*K);
+ }
+ classes.sort_custom<StringName::AlphCompare>();
+ for (List<StringName>::Element *E = classes.front(); E; E = E->next()) {
+ r_global_classes->push_back(E->get());
+ }
+}
+
+void ScriptServer::save_global_classes() {
+ List<StringName> gc;
+ get_global_class_list(&gc);
+ Array gcarr;
+ for (List<StringName>::Element *E = gc.front(); E; E = E->next()) {
+ Dictionary d;
+ d["class"] = E->get();
+ d["language"] = global_classes[E->get()].language;
+ d["path"] = global_classes[E->get()].path;
+ d["base"] = global_classes[E->get()].base;
+ gcarr.push_back(d);
+ }
+
+ if (gcarr.empty()) {
+ if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) {
+ ProjectSettings::get_singleton()->clear("_global_script_classes");
+ }
+ } else {
+ ProjectSettings::get_singleton()->set("_global_script_classes", gcarr);
+ }
+ ProjectSettings::get_singleton()->save();
+}
+
+////////////////////
+void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state) {
+ List<PropertyInfo> pinfo;
+ get_property_list(&pinfo);
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+ if (E->get().usage & PROPERTY_USAGE_STORAGE) {
+ Pair<StringName, Variant> p;
+ p.first = E->get().name;
+ if (get(p.first, p.second)) {
+ state.push_back(p);
+ }
+ }
+ }
+}
+
+Variant ScriptInstance::call(const StringName &p_method, VARIANT_ARG_DECLARE) {
+ VARIANT_ARGPTRS;
+ int argc = 0;
+ for (int i = 0; i < VARIANT_ARG_MAX; i++) {
+ if (argptr[i]->get_type() == Variant::NIL) {
+ break;
+ }
+ argc++;
+ }
+
+ Callable::CallError error;
+ return call(p_method, argptr, argc, error);
+}
+
+void ScriptInstance::property_set_fallback(const StringName &, const Variant &, bool *r_valid) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+}
+
+Variant ScriptInstance::property_get_fallback(const StringName &, bool *r_valid) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+ return Variant();
+}
+
+ScriptInstance::~ScriptInstance() {
+}
+
+ScriptCodeCompletionCache *ScriptCodeCompletionCache::singleton = nullptr;
+ScriptCodeCompletionCache::ScriptCodeCompletionCache() {
+ singleton = this;
+}
+
+void ScriptLanguage::get_core_type_words(List<String> *p_core_type_words) const {
+ p_core_type_words->push_back("String");
+ p_core_type_words->push_back("Vector2");
+ p_core_type_words->push_back("Vector2i");
+ p_core_type_words->push_back("Rect2");
+ p_core_type_words->push_back("Rect2i");
+ p_core_type_words->push_back("Vector3");
+ p_core_type_words->push_back("Vector3i");
+ p_core_type_words->push_back("Transform2D");
+ p_core_type_words->push_back("Plane");
+ p_core_type_words->push_back("Quat");
+ p_core_type_words->push_back("AABB");
+ p_core_type_words->push_back("Basis");
+ p_core_type_words->push_back("Transform");
+ p_core_type_words->push_back("Color");
+ p_core_type_words->push_back("StringName");
+ p_core_type_words->push_back("NodePath");
+ p_core_type_words->push_back("RID");
+ p_core_type_words->push_back("Callable");
+ p_core_type_words->push_back("Signal");
+ p_core_type_words->push_back("Dictionary");
+ p_core_type_words->push_back("Array");
+ p_core_type_words->push_back("PackedByteArray");
+ p_core_type_words->push_back("PackedInt32Array");
+ p_core_type_words->push_back("PackedInt64Array");
+ p_core_type_words->push_back("PackedFloat32Array");
+ p_core_type_words->push_back("PackedFloat64Array");
+ p_core_type_words->push_back("PackedStringArray");
+ p_core_type_words->push_back("PackedVector2Array");
+ p_core_type_words->push_back("PackedVector3Array");
+ p_core_type_words->push_back("PackedColorArray");
+}
+
+void ScriptLanguage::frame() {
+}
+
+bool PlaceHolderScriptInstance::set(const StringName &p_name, const Variant &p_value) {
+ if (script->is_placeholder_fallback_enabled()) {
+ return false;
+ }
+
+ if (values.has(p_name)) {
+ Variant defval;
+ if (script->get_property_default_value(p_name, defval)) {
+ if (defval == p_value) {
+ values.erase(p_name);
+ return true;
+ }
+ }
+ values[p_name] = p_value;
+ return true;
+ } else {
+ Variant defval;
+ if (script->get_property_default_value(p_name, defval)) {
+ if (defval != p_value) {
+ values[p_name] = p_value;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PlaceHolderScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
+ if (values.has(p_name)) {
+ r_ret = values[p_name];
+ return true;
+ }
+
+ if (constants.has(p_name)) {
+ r_ret = constants[p_name];
+ return true;
+ }
+
+ if (!script->is_placeholder_fallback_enabled()) {
+ Variant defval;
+ if (script->get_property_default_value(p_name, defval)) {
+ r_ret = defval;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void PlaceHolderScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const {
+ if (script->is_placeholder_fallback_enabled()) {
+ for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
+ p_properties->push_back(E->get());
+ }
+ } else {
+ for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
+ PropertyInfo pinfo = E->get();
+ if (!values.has(pinfo.name)) {
+ pinfo.usage |= PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE;
+ }
+ p_properties->push_back(E->get());
+ }
+ }
+}
+
+Variant::Type PlaceHolderScriptInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const {
+ if (values.has(p_name)) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return values[p_name].get_type();
+ }
+
+ if (constants.has(p_name)) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return constants[p_name].get_type();
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+
+ return Variant::NIL;
+}
+
+void PlaceHolderScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
+ if (script->is_placeholder_fallback_enabled()) {
+ return;
+ }
+
+ if (script.is_valid()) {
+ script->get_script_method_list(p_list);
+ }
+}
+
+bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const {
+ if (script->is_placeholder_fallback_enabled()) {
+ return false;
+ }
+
+ if (script.is_valid()) {
+ return script->has_method(p_method);
+ }
+ return false;
+}
+
+void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const Map<StringName, Variant> &p_values) {
+ Set<StringName> new_values;
+ for (const List<PropertyInfo>::Element *E = p_properties.front(); E; E = E->next()) {
+ StringName n = E->get().name;
+ new_values.insert(n);
+
+ if (!values.has(n) || values[n].get_type() != E->get().type) {
+ if (p_values.has(n)) {
+ values[n] = p_values[n];
+ }
+ }
+ }
+
+ properties = p_properties;
+ List<StringName> to_remove;
+
+ for (Map<StringName, Variant>::Element *E = values.front(); E; E = E->next()) {
+ if (!new_values.has(E->key())) {
+ to_remove.push_back(E->key());
+ }
+
+ Variant defval;
+ if (script->get_property_default_value(E->key(), defval)) {
+ //remove because it's the same as the default value
+ if (defval == E->get()) {
+ to_remove.push_back(E->key());
+ }
+ }
+ }
+
+ while (to_remove.size()) {
+ values.erase(to_remove.front()->get());
+ to_remove.pop_front();
+ }
+
+ if (owner && owner->get_script_instance() == this) {
+ owner->_change_notify();
+ }
+ //change notify
+
+ constants.clear();
+ script->get_constants(&constants);
+}
+
+void PlaceHolderScriptInstance::property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid) {
+ if (script->is_placeholder_fallback_enabled()) {
+ Map<StringName, Variant>::Element *E = values.find(p_name);
+
+ if (E) {
+ E->value() = p_value;
+ } else {
+ values.insert(p_name, p_value);
+ }
+
+ bool found = false;
+ for (const List<PropertyInfo>::Element *F = properties.front(); F; F = F->next()) {
+ if (F->get().name == p_name) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ properties.push_back(PropertyInfo(p_value.get_type(), p_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_SCRIPT_VARIABLE));
+ }
+ }
+
+ if (r_valid) {
+ *r_valid = false; // Cannot change the value in either case
+ }
+}
+
+Variant PlaceHolderScriptInstance::property_get_fallback(const StringName &p_name, bool *r_valid) {
+ if (script->is_placeholder_fallback_enabled()) {
+ const Map<StringName, Variant>::Element *E = values.find(p_name);
+
+ if (E) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return E->value();
+ }
+
+ E = constants.find(p_name);
+ if (E) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return E->value();
+ }
+ }
+
+ if (r_valid) {
+ *r_valid = false;
+ }
+
+ return Variant();
+}
+
+uint16_t PlaceHolderScriptInstance::get_rpc_method_id(const StringName &p_method) const {
+ return UINT16_MAX;
+}
+
+uint16_t PlaceHolderScriptInstance::get_rset_property_id(const StringName &p_method) const {
+ return UINT16_MAX;
+}
+
+PlaceHolderScriptInstance::PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner) :
+ owner(p_owner),
+ language(p_language),
+ script(p_script) {
+}
+
+PlaceHolderScriptInstance::~PlaceHolderScriptInstance() {
+ if (script.is_valid()) {
+ script->_placeholder_erased(this);
+ }
+}
diff --git a/core/object/script_language.h b/core/object/script_language.h
new file mode 100644
index 0000000000..447216f14f
--- /dev/null
+++ b/core/object/script_language.h
@@ -0,0 +1,460 @@
+/*************************************************************************/
+/* script_language.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCRIPT_LANGUAGE_H
+#define SCRIPT_LANGUAGE_H
+
+#include "core/io/multiplayer_api.h"
+#include "core/io/resource.h"
+#include "core/templates/map.h"
+#include "core/templates/pair.h"
+
+class ScriptLanguage;
+
+typedef void (*ScriptEditRequestFunction)(const String &p_path);
+
+struct ScriptNetData {
+ StringName name;
+ MultiplayerAPI::RPCMode mode;
+ bool operator==(ScriptNetData const &p_other) const {
+ return name == p_other.name;
+ }
+};
+
+struct SortNetData {
+ StringName::AlphCompare compare;
+ bool operator()(const ScriptNetData &p_a, const ScriptNetData &p_b) const {
+ return compare(p_a.name, p_b.name);
+ }
+};
+
+class ScriptServer {
+ enum {
+
+ MAX_LANGUAGES = 16
+ };
+
+ static ScriptLanguage *_languages[MAX_LANGUAGES];
+ static int _language_count;
+ static bool scripting_enabled;
+ static bool reload_scripts_on_save;
+ static bool languages_finished;
+
+ struct GlobalScriptClass {
+ StringName language;
+ String path;
+ String base;
+ };
+
+ static HashMap<StringName, GlobalScriptClass> global_classes;
+
+public:
+ static ScriptEditRequestFunction edit_request_func;
+
+ static void set_scripting_enabled(bool p_enabled);
+ static bool is_scripting_enabled();
+ _FORCE_INLINE_ static int get_language_count() { return _language_count; }
+ static ScriptLanguage *get_language(int p_idx);
+ static void register_language(ScriptLanguage *p_language);
+ static void unregister_language(ScriptLanguage *p_language);
+
+ static void set_reload_scripts_on_save(bool p_enable);
+ static bool is_reload_scripts_on_save_enabled();
+
+ static void thread_enter();
+ static void thread_exit();
+
+ static void global_classes_clear();
+ static void add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path);
+ static void remove_global_class(const StringName &p_class);
+ static bool is_global_class(const StringName &p_class);
+ static StringName get_global_class_language(const StringName &p_class);
+ static String get_global_class_path(const String &p_class);
+ static StringName get_global_class_base(const String &p_class);
+ static StringName get_global_class_native_base(const String &p_class);
+ static void get_global_class_list(List<StringName> *r_global_classes);
+ static void save_global_classes();
+
+ static void init_languages();
+ static void finish_languages();
+
+ static bool are_languages_finished() { return languages_finished; }
+};
+
+class ScriptInstance;
+class PlaceHolderScriptInstance;
+
+class Script : public Resource {
+ GDCLASS(Script, Resource);
+ OBJ_SAVE_TYPE(Script);
+
+protected:
+ virtual bool editor_can_reload_from_file() override { return false; } // this is handled by editor better
+ void _notification(int p_what);
+ static void _bind_methods();
+
+ friend class PlaceHolderScriptInstance;
+ virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {}
+
+ Variant _get_property_default_value(const StringName &p_property);
+ Array _get_script_property_list();
+ Array _get_script_method_list();
+ Array _get_script_signal_list();
+ Dictionary _get_script_constant_map();
+
+public:
+ virtual bool can_instance() const = 0;
+
+ virtual Ref<Script> get_base_script() const = 0; //for script inheritance
+
+ virtual bool inherits_script(const Ref<Script> &p_script) const = 0;
+
+ virtual StringName get_instance_base_type() const = 0; // this may not work in all scripts, will return empty if so
+ virtual ScriptInstance *instance_create(Object *p_this) = 0;
+ virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this) { return nullptr; }
+ virtual bool instance_has(const Object *p_this) const = 0;
+
+ virtual bool has_source_code() const = 0;
+ virtual String get_source_code() const = 0;
+ virtual void set_source_code(const String &p_code) = 0;
+ virtual Error reload(bool p_keep_state = false) = 0;
+
+ virtual bool has_method(const StringName &p_method) const = 0;
+ virtual MethodInfo get_method_info(const StringName &p_method) const = 0;
+
+ virtual bool is_tool() const = 0;
+ virtual bool is_valid() const = 0;
+
+ virtual ScriptLanguage *get_language() const = 0;
+
+ virtual bool has_script_signal(const StringName &p_signal) const = 0;
+ virtual void get_script_signal_list(List<MethodInfo> *r_signals) const = 0;
+
+ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const = 0;
+
+ virtual void update_exports() {} //editor tool
+ virtual void get_script_method_list(List<MethodInfo> *p_list) const = 0;
+ virtual void get_script_property_list(List<PropertyInfo> *p_list) const = 0;
+
+ virtual int get_member_line(const StringName &p_member) const { return -1; }
+
+ virtual void get_constants(Map<StringName, Variant> *p_constants) {}
+ virtual void get_members(Set<StringName> *p_constants) {}
+
+ virtual bool is_placeholder_fallback_enabled() const { return false; }
+
+ virtual Vector<ScriptNetData> get_rpc_methods() const = 0;
+ virtual uint16_t get_rpc_method_id(const StringName &p_method) const = 0;
+ virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0;
+
+ virtual Vector<ScriptNetData> get_rset_properties() const = 0;
+ virtual uint16_t get_rset_property_id(const StringName &p_property) const = 0;
+ virtual StringName get_rset_property(const uint16_t p_rset_property_id) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0;
+
+ Script() {}
+};
+
+class ScriptInstance {
+public:
+ virtual bool set(const StringName &p_name, const Variant &p_value) = 0;
+ virtual bool get(const StringName &p_name, Variant &r_ret) const = 0;
+ virtual void get_property_list(List<PropertyInfo> *p_properties) const = 0;
+ virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const = 0;
+
+ virtual Object *get_owner() { return nullptr; }
+ virtual void get_property_state(List<Pair<StringName, Variant>> &state);
+
+ virtual void get_method_list(List<MethodInfo> *p_list) const = 0;
+ virtual bool has_method(const StringName &p_method) const = 0;
+ virtual Variant call(const StringName &p_method, VARIANT_ARG_LIST);
+ virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = 0;
+ virtual void notification(int p_notification) = 0;
+ virtual String to_string(bool *r_valid) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+ return String();
+ }
+
+ //this is used by script languages that keep a reference counter of their own
+ //you can make make Ref<> not die when it reaches zero, so deleting the reference
+ //depends entirely from the script
+
+ virtual void refcount_incremented() {}
+ virtual bool refcount_decremented() { return true; } //return true if it can die
+
+ virtual Ref<Script> get_script() const = 0;
+
+ virtual bool is_placeholder() const { return false; }
+
+ virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid);
+ virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid);
+
+ virtual Vector<ScriptNetData> get_rpc_methods() const = 0;
+ virtual uint16_t get_rpc_method_id(const StringName &p_method) const = 0;
+ virtual StringName get_rpc_method(uint16_t p_id) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0;
+
+ virtual Vector<ScriptNetData> get_rset_properties() const = 0;
+ virtual uint16_t get_rset_property_id(const StringName &p_variable) const = 0;
+ virtual StringName get_rset_property(uint16_t p_id) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const = 0;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0;
+
+ virtual ScriptLanguage *get_language() = 0;
+ virtual ~ScriptInstance();
+};
+
+struct ScriptCodeCompletionOption {
+ enum Kind {
+ KIND_CLASS,
+ KIND_FUNCTION,
+ KIND_SIGNAL,
+ KIND_VARIABLE,
+ KIND_MEMBER,
+ KIND_ENUM,
+ KIND_CONSTANT,
+ KIND_NODE_PATH,
+ KIND_FILE_PATH,
+ KIND_PLAIN_TEXT,
+ };
+ Kind kind = KIND_PLAIN_TEXT;
+ String display;
+ String insert_text;
+ Color font_color;
+ RES icon;
+ Variant default_value;
+
+ ScriptCodeCompletionOption() {}
+
+ ScriptCodeCompletionOption(const String &p_text, Kind p_kind) {
+ display = p_text;
+ insert_text = p_text;
+ kind = p_kind;
+ }
+};
+
+class ScriptCodeCompletionCache {
+ static ScriptCodeCompletionCache *singleton;
+
+public:
+ virtual RES get_cached_resource(const String &p_path) = 0;
+
+ static ScriptCodeCompletionCache *get_singleton() { return singleton; }
+
+ ScriptCodeCompletionCache();
+
+ virtual ~ScriptCodeCompletionCache() {}
+};
+
+class ScriptLanguage {
+public:
+ virtual String get_name() const = 0;
+
+ /* LANGUAGE FUNCTIONS */
+ virtual void init() = 0;
+ virtual String get_type() const = 0;
+ virtual String get_extension() const = 0;
+ virtual Error execute_file(const String &p_path) = 0;
+ virtual void finish() = 0;
+
+ /* EDITOR FUNCTIONS */
+ struct Warning {
+ int start_line = -1, end_line = -1;
+ int leftmost_column = -1, rightmost_column = -1;
+ int code;
+ String string_code;
+ String message;
+ };
+
+ void get_core_type_words(List<String> *p_core_type_words) const;
+ virtual void get_reserved_words(List<String> *p_words) const = 0;
+ virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0;
+ virtual void get_string_delimiters(List<String> *p_delimiters) const = 0;
+ virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const = 0;
+ virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {}
+ virtual bool is_using_templates() { return false; }
+ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = nullptr, List<Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const = 0;
+ virtual String validate_path(const String &p_path) const { return ""; }
+ virtual Script *create_script() const = 0;
+ virtual bool has_named_classes() const = 0;
+ virtual bool supports_builtin_mode() const = 0;
+ virtual bool can_inherit_from_file() { return false; }
+ virtual int find_function(const String &p_function, const String &p_code) const = 0;
+ virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const = 0;
+ virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return ERR_UNAVAILABLE; }
+ virtual bool overrides_external_editor() { return false; }
+
+ virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) { return ERR_UNAVAILABLE; }
+
+ struct LookupResult {
+ enum Type {
+ RESULT_SCRIPT_LOCATION,
+ RESULT_CLASS,
+ RESULT_CLASS_CONSTANT,
+ RESULT_CLASS_PROPERTY,
+ RESULT_CLASS_METHOD,
+ RESULT_CLASS_ENUM,
+ RESULT_CLASS_TBD_GLOBALSCOPE
+ };
+ Type type;
+ Ref<Script> script;
+ String class_name;
+ String class_member;
+ int location;
+ };
+
+ virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) { return ERR_UNAVAILABLE; }
+
+ virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const = 0;
+ virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) = 0;
+ virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value) {}
+ virtual void remove_named_global_constant(const StringName &p_name) {}
+
+ /* MULTITHREAD FUNCTIONS */
+
+ //some VMs need to be notified of thread creation/exiting to allocate a stack
+ virtual void thread_enter() {}
+ virtual void thread_exit() {}
+
+ /* DEBUGGER FUNCTIONS */
+ struct StackInfo {
+ String file;
+ String func;
+ int line;
+ };
+
+ virtual String debug_get_error() const = 0;
+ virtual int debug_get_stack_level_count() const = 0;
+ virtual int debug_get_stack_level_line(int p_level) const = 0;
+ virtual String debug_get_stack_level_function(int p_level) const = 0;
+ virtual String debug_get_stack_level_source(int p_level) const = 0;
+ virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) = 0;
+ virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) = 0;
+ virtual ScriptInstance *debug_get_stack_level_instance(int p_level) { return nullptr; }
+ virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) = 0;
+ virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1) = 0;
+
+ virtual Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); }
+
+ virtual void reload_all_scripts() = 0;
+ virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) = 0;
+ /* LOADER FUNCTIONS */
+
+ virtual void get_recognized_extensions(List<String> *p_extensions) const = 0;
+ virtual void get_public_functions(List<MethodInfo> *p_functions) const = 0;
+ virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const = 0;
+
+ struct ProfilingInfo {
+ StringName signature;
+ uint64_t call_count;
+ uint64_t total_time;
+ uint64_t self_time;
+ };
+
+ virtual void profiling_start() = 0;
+ virtual void profiling_stop() = 0;
+
+ virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) = 0;
+ virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) = 0;
+
+ virtual void *alloc_instance_binding_data(Object *p_object) { return nullptr; } //optional, not used by all languages
+ virtual void free_instance_binding_data(void *p_data) {} //optional, not used by all languages
+ virtual void refcount_incremented_instance_binding(Object *p_object) {} //optional, not used by all languages
+ virtual bool refcount_decremented_instance_binding(Object *p_object) { return true; } //return true if it can die //optional, not used by all languages
+
+ virtual void frame();
+
+ virtual bool handles_global_class_type(const String &p_type) const { return false; }
+ virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const { return String(); }
+
+ virtual ~ScriptLanguage() {}
+};
+
+extern uint8_t script_encryption_key[32];
+
+class PlaceHolderScriptInstance : public ScriptInstance {
+ Object *owner;
+ List<PropertyInfo> properties;
+ Map<StringName, Variant> values;
+ Map<StringName, Variant> constants;
+ ScriptLanguage *language;
+ Ref<Script> script;
+
+public:
+ virtual bool set(const StringName &p_name, const Variant &p_value);
+ virtual bool get(const StringName &p_name, Variant &r_ret) const;
+ virtual void get_property_list(List<PropertyInfo> *p_properties) const;
+ virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const;
+
+ virtual void get_method_list(List<MethodInfo> *p_list) const;
+ virtual bool has_method(const StringName &p_method) const;
+ virtual Variant call(const StringName &p_method, VARIANT_ARG_LIST) { return Variant(); }
+ virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
+ }
+ virtual void notification(int p_notification) {}
+
+ virtual Ref<Script> get_script() const { return script; }
+
+ virtual ScriptLanguage *get_language() { return language; }
+
+ Object *get_owner() { return owner; }
+
+ void update(const List<PropertyInfo> &p_properties, const Map<StringName, Variant> &p_values); //likely changed in editor
+
+ virtual bool is_placeholder() const { return true; }
+
+ virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr);
+ virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid = nullptr);
+
+ virtual Vector<ScriptNetData> get_rpc_methods() const { return Vector<ScriptNetData>(); }
+ virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
+ virtual StringName get_rpc_method(uint16_t p_id) const { return StringName(); }
+ virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
+
+ virtual Vector<ScriptNetData> get_rset_properties() const { return Vector<ScriptNetData>(); }
+ virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
+ virtual StringName get_rset_property(uint16_t p_id) const { return StringName(); }
+ virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
+
+ PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner);
+ ~PlaceHolderScriptInstance();
+};
+
+#endif // SCRIPT_LANGUAGE_H
diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp
new file mode 100644
index 0000000000..1dcbb0cd6b
--- /dev/null
+++ b/core/object/undo_redo.cpp
@@ -0,0 +1,520 @@
+/*************************************************************************/
+/* undo_redo.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "undo_redo.h"
+
+#include "core/os/os.h"
+
+void UndoRedo::_discard_redo() {
+ if (current_action == actions.size() - 1) {
+ return;
+ }
+
+ for (int i = current_action + 1; i < actions.size(); i++) {
+ for (List<Operation>::Element *E = actions.write[i].do_ops.front(); E; E = E->next()) {
+ if (E->get().type == Operation::TYPE_REFERENCE) {
+ Object *obj = ObjectDB::get_instance(E->get().object);
+ if (obj) {
+ memdelete(obj);
+ }
+ }
+ }
+ //ERASE do data
+ }
+
+ actions.resize(current_action + 1);
+}
+
+void UndoRedo::create_action(const String &p_name, MergeMode p_mode) {
+ uint32_t ticks = OS::get_singleton()->get_ticks_msec();
+
+ if (action_level == 0) {
+ _discard_redo();
+
+ // Check if the merge operation is valid
+ if (p_mode != MERGE_DISABLE && actions.size() && actions[actions.size() - 1].name == p_name && actions[actions.size() - 1].last_tick + 800 > ticks) {
+ current_action = actions.size() - 2;
+
+ if (p_mode == MERGE_ENDS) {
+ // Clear all do ops from last action, and delete all object references
+ List<Operation>::Element *E = actions.write[current_action + 1].do_ops.front();
+
+ while (E) {
+ if (E->get().type == Operation::TYPE_REFERENCE) {
+ Object *obj = ObjectDB::get_instance(E->get().object);
+
+ if (obj) {
+ memdelete(obj);
+ }
+ }
+
+ E = E->next();
+ actions.write[current_action + 1].do_ops.pop_front();
+ }
+ }
+
+ actions.write[actions.size() - 1].last_tick = ticks;
+
+ merge_mode = p_mode;
+ merging = true;
+ } else {
+ Action new_action;
+ new_action.name = p_name;
+ new_action.last_tick = ticks;
+ actions.push_back(new_action);
+
+ merge_mode = MERGE_DISABLE;
+ }
+ }
+
+ action_level++;
+}
+
+void UndoRedo::add_do_method(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) {
+ VARIANT_ARGPTRS
+ ERR_FAIL_COND(p_object == nullptr);
+ ERR_FAIL_COND(action_level <= 0);
+ ERR_FAIL_COND((current_action + 1) >= actions.size());
+ Operation do_op;
+ do_op.object = p_object->get_instance_id();
+ if (Object::cast_to<Resource>(p_object)) {
+ do_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object));
+ }
+
+ do_op.type = Operation::TYPE_METHOD;
+ do_op.name = p_method;
+
+ for (int i = 0; i < VARIANT_ARG_MAX; i++) {
+ do_op.args[i] = *argptr[i];
+ }
+ actions.write[current_action + 1].do_ops.push_back(do_op);
+}
+
+void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) {
+ VARIANT_ARGPTRS
+ ERR_FAIL_COND(p_object == nullptr);
+ ERR_FAIL_COND(action_level <= 0);
+ ERR_FAIL_COND((current_action + 1) >= actions.size());
+
+ // No undo if the merge mode is MERGE_ENDS
+ if (merge_mode == MERGE_ENDS) {
+ return;
+ }
+
+ Operation undo_op;
+ undo_op.object = p_object->get_instance_id();
+ if (Object::cast_to<Resource>(p_object)) {
+ undo_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object));
+ }
+
+ undo_op.type = Operation::TYPE_METHOD;
+ undo_op.name = p_method;
+
+ for (int i = 0; i < VARIANT_ARG_MAX; i++) {
+ undo_op.args[i] = *argptr[i];
+ }
+ actions.write[current_action + 1].undo_ops.push_back(undo_op);
+}
+
+void UndoRedo::add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value) {
+ ERR_FAIL_COND(p_object == nullptr);
+ ERR_FAIL_COND(action_level <= 0);
+ ERR_FAIL_COND((current_action + 1) >= actions.size());
+ Operation do_op;
+ do_op.object = p_object->get_instance_id();
+ if (Object::cast_to<Resource>(p_object)) {
+ do_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object));
+ }
+
+ do_op.type = Operation::TYPE_PROPERTY;
+ do_op.name = p_property;
+ do_op.args[0] = p_value;
+ actions.write[current_action + 1].do_ops.push_back(do_op);
+}
+
+void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value) {
+ ERR_FAIL_COND(p_object == nullptr);
+ ERR_FAIL_COND(action_level <= 0);
+ ERR_FAIL_COND((current_action + 1) >= actions.size());
+
+ // No undo if the merge mode is MERGE_ENDS
+ if (merge_mode == MERGE_ENDS) {
+ return;
+ }
+
+ Operation undo_op;
+ undo_op.object = p_object->get_instance_id();
+ if (Object::cast_to<Resource>(p_object)) {
+ undo_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object));
+ }
+
+ undo_op.type = Operation::TYPE_PROPERTY;
+ undo_op.name = p_property;
+ undo_op.args[0] = p_value;
+ actions.write[current_action + 1].undo_ops.push_back(undo_op);
+}
+
+void UndoRedo::add_do_reference(Object *p_object) {
+ ERR_FAIL_COND(p_object == nullptr);
+ ERR_FAIL_COND(action_level <= 0);
+ ERR_FAIL_COND((current_action + 1) >= actions.size());
+ Operation do_op;
+ do_op.object = p_object->get_instance_id();
+ if (Object::cast_to<Resource>(p_object)) {
+ do_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object));
+ }
+
+ do_op.type = Operation::TYPE_REFERENCE;
+ actions.write[current_action + 1].do_ops.push_back(do_op);
+}
+
+void UndoRedo::add_undo_reference(Object *p_object) {
+ ERR_FAIL_COND(p_object == nullptr);
+ ERR_FAIL_COND(action_level <= 0);
+ ERR_FAIL_COND((current_action + 1) >= actions.size());
+
+ // No undo if the merge mode is MERGE_ENDS
+ if (merge_mode == MERGE_ENDS) {
+ return;
+ }
+
+ Operation undo_op;
+ undo_op.object = p_object->get_instance_id();
+ if (Object::cast_to<Resource>(p_object)) {
+ undo_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object));
+ }
+
+ undo_op.type = Operation::TYPE_REFERENCE;
+ actions.write[current_action + 1].undo_ops.push_back(undo_op);
+}
+
+void UndoRedo::_pop_history_tail() {
+ _discard_redo();
+
+ if (!actions.size()) {
+ return;
+ }
+
+ for (List<Operation>::Element *E = actions.write[0].undo_ops.front(); E; E = E->next()) {
+ if (E->get().type == Operation::TYPE_REFERENCE) {
+ Object *obj = ObjectDB::get_instance(E->get().object);
+ if (obj) {
+ memdelete(obj);
+ }
+ }
+ }
+
+ actions.remove(0);
+ if (current_action >= 0) {
+ current_action--;
+ }
+}
+
+bool UndoRedo::is_committing_action() const {
+ return committing > 0;
+}
+
+void UndoRedo::commit_action() {
+ ERR_FAIL_COND(action_level <= 0);
+ action_level--;
+ if (action_level > 0) {
+ return; //still nested
+ }
+
+ if (merging) {
+ version--;
+ merging = false;
+ }
+
+ committing++;
+ redo(); // perform action
+ committing--;
+ if (callback && actions.size() > 0) {
+ callback(callback_ud, actions[actions.size() - 1].name);
+ }
+}
+
+void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
+ for (; E; E = E->next()) {
+ Operation &op = E->get();
+
+ Object *obj = ObjectDB::get_instance(op.object);
+ if (!obj) { //may have been deleted and this is fine
+ continue;
+ }
+
+ switch (op.type) {
+ case Operation::TYPE_METHOD: {
+ Vector<const Variant *> argptrs;
+ argptrs.resize(VARIANT_ARG_MAX);
+ int argc = 0;
+
+ for (int i = 0; i < VARIANT_ARG_MAX; i++) {
+ if (op.args[i].get_type() == Variant::NIL) {
+ break;
+ }
+ argptrs.write[i] = &op.args[i];
+ argc++;
+ }
+ argptrs.resize(argc);
+
+ Callable::CallError ce;
+ obj->call(op.name, (const Variant **)argptrs.ptr(), argc, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT("Error calling method from signal '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, (const Variant **)argptrs.ptr(), argc, ce));
+ }
+#ifdef TOOLS_ENABLED
+ Resource *res = Object::cast_to<Resource>(obj);
+ if (res) {
+ res->set_edited(true);
+ }
+
+#endif
+
+ if (method_callback) {
+ method_callback(method_callbck_ud, obj, op.name, VARIANT_ARGS_FROM_ARRAY(op.args));
+ }
+ } break;
+ case Operation::TYPE_PROPERTY: {
+ obj->set(op.name, op.args[0]);
+#ifdef TOOLS_ENABLED
+ Resource *res = Object::cast_to<Resource>(obj);
+ if (res) {
+ res->set_edited(true);
+ }
+#endif
+ if (property_callback) {
+ property_callback(prop_callback_ud, obj, op.name, op.args[0]);
+ }
+ } break;
+ case Operation::TYPE_REFERENCE: {
+ //do nothing
+ } break;
+ }
+ }
+}
+
+bool UndoRedo::redo() {
+ ERR_FAIL_COND_V(action_level > 0, false);
+
+ if ((current_action + 1) >= actions.size()) {
+ return false; //nothing to redo
+ }
+
+ current_action++;
+
+ _process_operation_list(actions.write[current_action].do_ops.front());
+ version++;
+ emit_signal("version_changed");
+
+ return true;
+}
+
+bool UndoRedo::undo() {
+ ERR_FAIL_COND_V(action_level > 0, false);
+ if (current_action < 0) {
+ return false; //nothing to redo
+ }
+ _process_operation_list(actions.write[current_action].undo_ops.front());
+ current_action--;
+ version--;
+ emit_signal("version_changed");
+
+ return true;
+}
+
+void UndoRedo::clear_history(bool p_increase_version) {
+ ERR_FAIL_COND(action_level > 0);
+ _discard_redo();
+
+ while (actions.size()) {
+ _pop_history_tail();
+ }
+
+ if (p_increase_version) {
+ version++;
+ emit_signal("version_changed");
+ }
+}
+
+String UndoRedo::get_current_action_name() const {
+ ERR_FAIL_COND_V(action_level > 0, "");
+ if (current_action < 0) {
+ return "";
+ }
+ return actions[current_action].name;
+}
+
+bool UndoRedo::has_undo() {
+ return current_action >= 0;
+}
+
+bool UndoRedo::has_redo() {
+ return (current_action + 1) < actions.size();
+}
+
+uint64_t UndoRedo::get_version() const {
+ return version;
+}
+
+void UndoRedo::set_commit_notify_callback(CommitNotifyCallback p_callback, void *p_ud) {
+ callback = p_callback;
+ callback_ud = p_ud;
+}
+
+void UndoRedo::set_method_notify_callback(MethodNotifyCallback p_method_callback, void *p_ud) {
+ method_callback = p_method_callback;
+ method_callbck_ud = p_ud;
+}
+
+void UndoRedo::set_property_notify_callback(PropertyNotifyCallback p_property_callback, void *p_ud) {
+ property_callback = p_property_callback;
+ prop_callback_ud = p_ud;
+}
+
+UndoRedo::~UndoRedo() {
+ clear_history();
+}
+
+Variant UndoRedo::_add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ if (p_argcount < 2) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 0;
+ return Variant();
+ }
+
+ if (p_args[0]->get_type() != Variant::OBJECT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ return Variant();
+ }
+
+ if (p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 1;
+ r_error.expected = Variant::STRING_NAME;
+ return Variant();
+ }
+
+ r_error.error = Callable::CallError::CALL_OK;
+
+ Object *object = *p_args[0];
+ StringName method = *p_args[1];
+
+ Variant v[VARIANT_ARG_MAX];
+
+ for (int i = 0; i < MIN(VARIANT_ARG_MAX, p_argcount - 2); ++i) {
+ v[i] = *p_args[i + 2];
+ }
+
+ static_assert(VARIANT_ARG_MAX == 5, "This code needs to be updated if VARIANT_ARG_MAX != 5");
+ add_do_method(object, method, v[0], v[1], v[2], v[3], v[4]);
+ return Variant();
+}
+
+Variant UndoRedo::_add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ if (p_argcount < 2) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 0;
+ return Variant();
+ }
+
+ if (p_args[0]->get_type() != Variant::OBJECT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ return Variant();
+ }
+
+ if (p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 1;
+ r_error.expected = Variant::STRING_NAME;
+ return Variant();
+ }
+
+ r_error.error = Callable::CallError::CALL_OK;
+
+ Object *object = *p_args[0];
+ StringName method = *p_args[1];
+
+ Variant v[VARIANT_ARG_MAX];
+
+ for (int i = 0; i < MIN(VARIANT_ARG_MAX, p_argcount - 2); ++i) {
+ v[i] = *p_args[i + 2];
+ }
+
+ static_assert(VARIANT_ARG_MAX == 5, "This code needs to be updated if VARIANT_ARG_MAX != 5");
+ add_undo_method(object, method, v[0], v[1], v[2], v[3], v[4]);
+ return Variant();
+}
+
+void UndoRedo::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("create_action", "name", "merge_mode"), &UndoRedo::create_action, DEFVAL(MERGE_DISABLE));
+ ClassDB::bind_method(D_METHOD("commit_action"), &UndoRedo::commit_action);
+ ClassDB::bind_method(D_METHOD("is_committing_action"), &UndoRedo::is_committing_action);
+
+ {
+ MethodInfo mi;
+ mi.name = "add_do_method";
+ mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object"));
+ mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method"));
+
+ ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "add_do_method", &UndoRedo::_add_do_method, mi, varray(), false);
+ }
+
+ {
+ MethodInfo mi;
+ mi.name = "add_undo_method";
+ mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object"));
+ mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method"));
+
+ ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "add_undo_method", &UndoRedo::_add_undo_method, mi, varray(), false);
+ }
+
+ ClassDB::bind_method(D_METHOD("add_do_property", "object", "property", "value"), &UndoRedo::add_do_property);
+ ClassDB::bind_method(D_METHOD("add_undo_property", "object", "property", "value"), &UndoRedo::add_undo_property);
+ ClassDB::bind_method(D_METHOD("add_do_reference", "object"), &UndoRedo::add_do_reference);
+ ClassDB::bind_method(D_METHOD("add_undo_reference", "object"), &UndoRedo::add_undo_reference);
+ ClassDB::bind_method(D_METHOD("clear_history", "increase_version"), &UndoRedo::clear_history, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("get_current_action_name"), &UndoRedo::get_current_action_name);
+ ClassDB::bind_method(D_METHOD("has_undo"), &UndoRedo::has_undo);
+ ClassDB::bind_method(D_METHOD("has_redo"), &UndoRedo::has_redo);
+ ClassDB::bind_method(D_METHOD("get_version"), &UndoRedo::get_version);
+ ClassDB::bind_method(D_METHOD("redo"), &UndoRedo::redo);
+ ClassDB::bind_method(D_METHOD("undo"), &UndoRedo::undo);
+
+ ADD_SIGNAL(MethodInfo("version_changed"));
+
+ BIND_ENUM_CONSTANT(MERGE_DISABLE);
+ BIND_ENUM_CONSTANT(MERGE_ENDS);
+ BIND_ENUM_CONSTANT(MERGE_ALL);
+}
diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h
new file mode 100644
index 0000000000..68d78e0d7d
--- /dev/null
+++ b/core/object/undo_redo.h
@@ -0,0 +1,135 @@
+/*************************************************************************/
+/* undo_redo.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef UNDO_REDO_H
+#define UNDO_REDO_H
+
+#include "core/io/resource.h"
+#include "core/object/class_db.h"
+
+class UndoRedo : public Object {
+ GDCLASS(UndoRedo, Object);
+ OBJ_SAVE_TYPE(UndoRedo);
+
+public:
+ enum MergeMode {
+ MERGE_DISABLE,
+ MERGE_ENDS,
+ MERGE_ALL
+ };
+
+ typedef void (*CommitNotifyCallback)(void *p_ud, const String &p_name);
+ Variant _add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ Variant _add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+
+ typedef void (*MethodNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE);
+ typedef void (*PropertyNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value);
+
+private:
+ struct Operation {
+ enum Type {
+ TYPE_METHOD,
+ TYPE_PROPERTY,
+ TYPE_REFERENCE
+ };
+
+ Type type;
+ Ref<Resource> resref;
+ ObjectID object;
+ StringName name;
+ Variant args[VARIANT_ARG_MAX];
+ };
+
+ struct Action {
+ String name;
+ List<Operation> do_ops;
+ List<Operation> undo_ops;
+ uint64_t last_tick;
+ };
+
+ Vector<Action> actions;
+ int current_action = -1;
+ int action_level = 0;
+ MergeMode merge_mode = MERGE_DISABLE;
+ bool merging = false;
+ uint64_t version = 1;
+
+ void _pop_history_tail();
+ void _process_operation_list(List<Operation>::Element *E);
+ void _discard_redo();
+
+ CommitNotifyCallback callback = nullptr;
+ void *callback_ud = nullptr;
+ void *method_callbck_ud = nullptr;
+ void *prop_callback_ud = nullptr;
+
+ MethodNotifyCallback method_callback = nullptr;
+ PropertyNotifyCallback property_callback = nullptr;
+
+ int committing = 0;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void create_action(const String &p_name = "", MergeMode p_mode = MERGE_DISABLE);
+
+ void add_do_method(Object *p_object, const StringName &p_method, VARIANT_ARG_LIST);
+ void add_undo_method(Object *p_object, const StringName &p_method, VARIANT_ARG_LIST);
+ void add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value);
+ void add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value);
+ void add_do_reference(Object *p_object);
+ void add_undo_reference(Object *p_object);
+
+ bool is_committing_action() const;
+ void commit_action();
+
+ bool redo();
+ bool undo();
+ String get_current_action_name() const;
+ void clear_history(bool p_increase_version = true);
+
+ bool has_undo();
+ bool has_redo();
+
+ uint64_t get_version() const;
+
+ void set_commit_notify_callback(CommitNotifyCallback p_callback, void *p_ud);
+
+ void set_method_notify_callback(MethodNotifyCallback p_method_callback, void *p_ud);
+ void set_property_notify_callback(PropertyNotifyCallback p_property_callback, void *p_ud);
+
+ UndoRedo() {}
+ ~UndoRedo();
+};
+
+VARIANT_ENUM_CAST(UndoRedo::MergeMode);
+
+#endif // UNDO_REDO_H