summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/callable.h7
-rw-r--r--core/callable_method_pointer.cpp94
-rw-r--r--core/callable_method_pointer.h279
-rw-r--r--core/class_db.h8
-rw-r--r--core/global_constants.cpp1
-rw-r--r--core/io/marshalls.cpp15
-rw-r--r--core/io/resource_format_binary.cpp12
-rw-r--r--core/io/resource_loader.cpp2
-rw-r--r--core/method_bind.h11
-rw-r--r--core/method_ptrcall.h26
-rw-r--r--core/node_path.h9
-rw-r--r--core/object.cpp26
-rw-r--r--core/os/input_event.cpp2
-rw-r--r--core/packed_data_container.cpp1
-rw-r--r--core/resource.cpp2
-rw-r--r--core/script_debugger_remote.cpp1145
-rw-r--r--core/script_debugger_remote.h316
-rw-r--r--core/type_info.h2
-rw-r--r--core/typedefs.h12
-rw-r--r--core/undo_redo.cpp24
-rw-r--r--core/undo_redo.h10
-rw-r--r--core/variant.cpp62
-rw-r--r--core/variant.h2
-rw-r--r--core/variant_call.cpp14
-rw-r--r--core/variant_op.cpp78
-rw-r--r--core/variant_parser.cpp32
-rw-r--r--core/variant_parser.h1
27 files changed, 2084 insertions, 109 deletions
diff --git a/core/callable.h b/core/callable.h
index 8ea5377ce8..cecf2264a3 100644
--- a/core/callable.h
+++ b/core/callable.h
@@ -39,10 +39,9 @@ class Object;
class Variant;
class CallableCustom;
-// This is an abstraction of things that can be called
-// it is used for signals and other cases where effient
-// calling of functions is required.
-// It is designed for the standard case (object and method)
+// This is an abstraction of things that can be called.
+// It is used for signals and other cases where efficient calling of functions
+// is required. It is designed for the standard case (object and method)
// but can be optimized or customized.
class Callable {
diff --git a/core/callable_method_pointer.cpp b/core/callable_method_pointer.cpp
new file mode 100644
index 0000000000..8774af6add
--- /dev/null
+++ b/core/callable_method_pointer.cpp
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* 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/callable_method_pointer.h b/core/callable_method_pointer.h
new file mode 100644
index 0000000000..fed793dfca
--- /dev/null
+++ b/core/callable_method_pointer.h
@@ -0,0 +1,279 @@
+/*************************************************************************/
+/* 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/callable.h"
+#include "core/hashfuncs.h"
+#include "core/object.h"
+#include "core/simple_type.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;
+};
+
+#ifdef DEBUG_METHODS_ENABLED
+
+template <class T>
+struct VariantCasterAndValidate {
+
+ static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) {
+ Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE;
+ if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype)) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = p_arg_idx;
+ r_error.expected = argtype;
+ }
+
+ return VariantCaster<T>::cast(*p_args[p_arg_idx]);
+ }
+};
+
+template <class T>
+struct VariantCasterAndValidate<T &> {
+
+ static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) {
+ Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE;
+ if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype)) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = p_arg_idx;
+ r_error.expected = argtype;
+ }
+
+ return VariantCaster<T>::cast(*p_args[p_arg_idx]);
+ }
+};
+
+template <class T>
+struct VariantCasterAndValidate<const T &> {
+
+ static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) {
+ Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE;
+ if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype)) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = p_arg_idx;
+ r_error.expected = argtype;
+ }
+
+ return VariantCaster<T>::cast(*p_args[p_arg_idx]);
+ }
+};
+
+#endif // DEBUG_METHODS_ENABLED
+
+// GCC 8 raises "parameter 'p_args' set but not used" here, probably using a
+// template version that does not have arguments and thus sees it unused, but
+// obviously the template can be used for functions with and without them, and
+// the optimizer will get rid of it anyway.
+#if defined(DEBUG_METHODS_ENABLED) && defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
+#endif
+
+template <class T, class... P, size_t... Is>
+void call_with_variant_args_helper(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) {
+ r_error.error = Callable::CallError::CALL_OK;
+
+#ifdef DEBUG_METHODS_ENABLED
+ (p_instance->*p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...);
+#else
+ (p_instance->*p_method)(VariantCaster<P...>::cast(p_args[Is])...);
+#endif
+}
+
+#if defined(DEBUG_METHODS_ENABLED) && defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+template <class T, class... P>
+void call_with_variant_args(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+#ifdef DEBUG_METHODS_ENABLED
+ if ((size_t)p_argcount > sizeof...(P)) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.argument = sizeof...(P);
+ return;
+ }
+
+ if ((size_t)p_argcount < sizeof...(P)) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = sizeof...(P);
+ return;
+ }
+#endif
+ call_with_variant_args_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{});
+}
+
+template <class T, class... P>
+class CallableCustomMethodPointer : public CallableCustomMethodPointerBase {
+
+ struct Data {
+ T *instance;
+ void (T::*method)(P...);
+ } data;
+
+public:
+ virtual ObjectID get_object() const { 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 {
+
+ 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;
+ 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, size_t... Is>
+void call_with_variant_args_ret_helper(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, Variant &r_ret, Callable::CallError &r_error, IndexSequence<Is...>) {
+ r_error.error = Callable::CallError::CALL_OK;
+
+#ifdef DEBUG_METHODS_ENABLED
+ r_ret = (p_instance->*p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...);
+#else
+ (p_instance->*p_method)(VariantCaster<P...>::cast(p_args[Is])...);
+#endif
+}
+
+template <class T, class R, class... P>
+void call_with_variant_args_ret(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) {
+#ifdef DEBUG_METHODS_ENABLED
+ if ((size_t)p_argcount > sizeof...(P)) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.argument = sizeof...(P);
+ return;
+ }
+
+ if ((size_t)p_argcount < sizeof...(P)) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = sizeof...(P);
+ return;
+ }
+#endif
+ call_with_variant_args_ret_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{});
+}
+
+template <class T, class R, class... P>
+class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase {
+
+ struct Data {
+ T *instance;
+ R(T::*method)
+ (P...);
+ } data;
+
+public:
+ virtual ObjectID get_object() const { 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 {
+
+ 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;
+ 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);
+}
+
+#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/class_db.h b/core/class_db.h
index 404b04f2d0..398eca9132 100644
--- a/core/class_db.h
+++ b/core/class_db.h
@@ -35,13 +35,15 @@
#include "core/object.h"
#include "core/print_string.h"
-/** To bind more then 6 parameters include this:
+/** To bind more then 6 parameters include this:
* #include "core/method_bind_ext.gen.inc"
*/
-#define DEFVAL(m_defval) (m_defval)
+// Makes callable_mp readily available in all classes connecting signals.
+// Needs to come after method_bind and object have been included.
+#include "core/callable_method_pointer.h"
-//#define SIMPLE_METHODDEF
+#define DEFVAL(m_defval) (m_defval)
#ifdef DEBUG_METHODS_ENABLED
diff --git a/core/global_constants.cpp b/core/global_constants.cpp
index b990ec9276..3de36dcb9d 100644
--- a/core/global_constants.cpp
+++ b/core/global_constants.cpp
@@ -606,6 +606,7 @@ void register_global_constants() {
BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_BASIS", Variant::BASIS);
BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_TRANSFORM", Variant::TRANSFORM);
BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_COLOR", Variant::COLOR);
+ BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_STRING_NAME", Variant::STRING_NAME); // 15
BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_NODE_PATH", Variant::NODE_PATH); // 15
BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_RID", Variant::_RID);
BIND_GLOBAL_ENUM_CONSTANT_CUSTOM("TYPE_OBJECT", Variant::OBJECT);
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index ab88f4d85c..b77ef40fd3 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -328,6 +328,16 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
(*r_len) += 4 * 4;
} break;
+ case Variant::STRING_NAME: {
+
+ String str;
+ Error err = _decode_string(buf, len, r_len, str);
+ if (err)
+ return err;
+ r_variant = StringName(str);
+
+ } break;
+
case Variant::NODE_PATH: {
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
@@ -939,6 +949,11 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
_encode_string(p_variant, buf, r_len);
} break;
+ case Variant::STRING_NAME: {
+
+ _encode_string(p_variant, buf, r_len);
+
+ } break;
// math types
case Variant::VECTOR2: {
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 991853e86d..144e48d0da 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -75,6 +75,7 @@ enum {
VARIANT_DOUBLE = 41,
VARIANT_CALLABLE = 42,
VARIANT_SIGNAL = 43,
+ VARIANT_STRING_NAME = 44,
OBJECT_EMPTY = 0,
OBJECT_EXTERNAL_RESOURCE = 1,
OBJECT_INTERNAL_RESOURCE = 2,
@@ -260,6 +261,10 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
r_v = v;
} break;
+ case VARIANT_STRING_NAME: {
+
+ r_v = StringName(get_unicode_string());
+ } break;
case VARIANT_NODE_PATH: {
@@ -1394,6 +1399,13 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
f->store_real(val.a);
} break;
+ case Variant::STRING_NAME: {
+
+ f->store_32(VARIANT_STRING_NAME);
+ String val = p_property;
+ save_unicode_string(f, val);
+
+ } break;
case Variant::NODE_PATH: {
f->store_32(VARIANT_NODE_PATH);
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 1d5d8f9280..39bbebefa6 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -248,7 +248,7 @@ void ResourceFormatLoader::_bind_methods() {
}
ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::PACKED_STRING_ARRAY, "get_recognized_extensions"));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "handles_type", PropertyInfo(Variant::STRING, "typename")));
+ ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "handles_type", PropertyInfo(Variant::STRING_NAME, "typename")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_resource_type", PropertyInfo(Variant::STRING, "path")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo("get_dependencies", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "add_types")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::INT, "rename_dependencies", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "renames")));
diff --git a/core/method_bind.h b/core/method_bind.h
index 72be141fd3..726ce512f8 100644
--- a/core/method_bind.h
+++ b/core/method_bind.h
@@ -31,19 +31,18 @@
#ifndef METHOD_BIND_H
#define METHOD_BIND_H
+#ifdef DEBUG_ENABLED
+#define DEBUG_METHODS_ENABLED
+#endif
+
#include "core/list.h"
#include "core/method_ptrcall.h"
#include "core/object.h"
+#include "core/type_info.h"
#include "core/variant.h"
#include <stdio.h>
-#ifdef DEBUG_ENABLED
-#define DEBUG_METHODS_ENABLED
-#endif
-
-#include "core/type_info.h"
-
enum MethodFlags {
METHOD_FLAG_NORMAL = 1,
diff --git a/core/method_ptrcall.h b/core/method_ptrcall.h
index 42d71dea46..7e21c2cc24 100644
--- a/core/method_ptrcall.h
+++ b/core/method_ptrcall.h
@@ -125,6 +125,7 @@ MAKE_PTRARG_BY_REFERENCE(AABB);
MAKE_PTRARG_BY_REFERENCE(Basis);
MAKE_PTRARG_BY_REFERENCE(Transform);
MAKE_PTRARG_BY_REFERENCE(Color);
+MAKE_PTRARG(StringName);
MAKE_PTRARG(NodePath);
MAKE_PTRARG(RID);
MAKE_PTRARG(Dictionary);
@@ -373,29 +374,7 @@ MAKE_VECARR(Plane);
} \
}
-//MAKE_DVECARR(Plane);
-//for special case StringName
-
-#define MAKE_STRINGCONV(m_type) \
- template <> \
- struct PtrToArg<m_type> { \
- _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
- m_type s = *reinterpret_cast<const String *>(p_ptr); \
- return s; \
- } \
- _FORCE_INLINE_ static void encode(m_type p_vec, void *p_ptr) { \
- String *arr = reinterpret_cast<String *>(p_ptr); \
- *arr = p_vec; \
- } \
- }; \
- \
- template <> \
- struct PtrToArg<const m_type &> { \
- _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
- m_type s = *reinterpret_cast<const String *>(p_ptr); \
- return s; \
- } \
- }
+// Special case for IP_Address.
#define MAKE_STRINGCONV_BY_REFERENCE(m_type) \
template <> \
@@ -418,7 +397,6 @@ MAKE_VECARR(Plane);
} \
}
-MAKE_STRINGCONV(StringName);
MAKE_STRINGCONV_BY_REFERENCE(IP_Address);
template <>
diff --git a/core/node_path.h b/core/node_path.h
index 5439658910..05b6d844ff 100644
--- a/core/node_path.h
+++ b/core/node_path.h
@@ -54,15 +54,6 @@ class NodePath {
void _update_hash_cache() const;
public:
- _FORCE_INLINE_ StringName get_sname() const {
-
- if (data && data->path.size() == 1 && data->subpath.empty()) {
- return data->path[0];
- } else {
- return operator String();
- }
- }
-
bool is_absolute() const;
int get_name_count() const;
StringName get_name(int p_idx) const;
diff --git a/core/object.cpp b/core/object.cpp
index d5db383cbc..a61c4ae392 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -646,10 +646,10 @@ Variant Object::_call_bind(const Variant **p_args, int p_argcount, Callable::Cal
return Variant();
}
- if (p_args[0]->get_type() != Variant::STRING) {
+ 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;
+ r_error.expected = Variant::STRING_NAME;
return Variant();
}
@@ -666,10 +666,10 @@ Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Call
return Variant();
}
- if (p_args[0]->get_type() != Variant::STRING) {
+ 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;
+ r_error.expected = Variant::STRING_NAME;
return Variant();
}
@@ -1108,11 +1108,11 @@ Variant Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::C
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) {
+ 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;
- ERR_FAIL_COND_V(p_args[0]->get_type() != Variant::STRING, Variant());
+ 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;
@@ -1207,7 +1207,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
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) + "': " + Variant::get_callable_error_text(c.callable, args, argc, ce) + ".");
+ 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;
}
}
@@ -1663,7 +1663,7 @@ void Object::_bind_methods() {
{
MethodInfo mi;
mi.name = "emit_signal";
- mi.arguments.push_back(PropertyInfo(Variant::STRING, "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);
}
@@ -1671,7 +1671,7 @@ void Object::_bind_methods() {
{
MethodInfo mi;
mi.name = "call";
- mi.arguments.push_back(PropertyInfo(Variant::STRING, "method"));
+ mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method"));
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call", &Object::_call_bind, mi);
}
@@ -1679,7 +1679,7 @@ void Object::_bind_methods() {
{
MethodInfo mi;
mi.name = "call_deferred";
- mi.arguments.push_back(PropertyInfo(Variant::STRING, "method"));
+ 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);
}
@@ -1713,9 +1713,9 @@ void Object::_bind_methods() {
ADD_SIGNAL(MethodInfo("script_changed"));
BIND_VMETHOD(MethodInfo("_notification", PropertyInfo(Variant::INT, "what")));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_set", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value")));
+ BIND_VMETHOD(MethodInfo(Variant::BOOL, "_set", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value")));
#ifdef TOOLS_ENABLED
- MethodInfo miget("_get", PropertyInfo(Variant::STRING, "property"));
+ 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);
diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp
index 2e863c9c76..7d1ae872f4 100644
--- a/core/os/input_event.cpp
+++ b/core/os/input_event.cpp
@@ -1098,7 +1098,7 @@ void InputEventAction::_bind_methods() {
// ClassDB::bind_method(D_METHOD("is_action", "name"), &InputEventAction::is_action);
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "action"), "set_action", "get_action");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "action"), "set_action", "get_action");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "strength", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_strength", "get_strength");
}
diff --git a/core/packed_data_container.cpp b/core/packed_data_container.cpp
index ae5f89e870..f23ee30a27 100644
--- a/core/packed_data_container.cpp
+++ b/core/packed_data_container.cpp
@@ -247,6 +247,7 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd
case Variant::PACKED_VECTOR2_ARRAY:
case Variant::PACKED_VECTOR3_ARRAY:
case Variant::PACKED_COLOR_ARRAY:
+ case Variant::STRING_NAME:
case Variant::NODE_PATH: {
uint32_t pos = tmpdata.size();
diff --git a/core/resource.cpp b/core/resource.cpp
index 30e09716aa..2afc9e4042 100644
--- a/core/resource.cpp
+++ b/core/resource.cpp
@@ -423,7 +423,7 @@ void Resource::_bind_methods() {
ADD_GROUP("Resource", "resource_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resource_local_to_scene"), "set_local_to_scene", "is_local_to_scene");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_path", "get_path");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_name"), "set_name", "get_name");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "resource_name"), "set_name", "get_name");
BIND_VMETHOD(MethodInfo("_setup_local_to_scene"));
}
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp
new file mode 100644
index 0000000000..fdb6135edd
--- /dev/null
+++ b/core/script_debugger_remote.cpp
@@ -0,0 +1,1145 @@
+/*************************************************************************/
+/* script_debugger_remote.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_debugger_remote.h"
+
+#include "core/engine.h"
+#include "core/io/ip.h"
+#include "core/io/marshalls.h"
+#include "core/os/input.h"
+#include "core/os/os.h"
+#include "core/project_settings.h"
+#include "servers/visual_server.h"
+
+#define CHECK_SIZE(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() < (uint32_t)(expected), false, String("Malformed ") + what + " message from script debugger, message too short. Exptected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
+#define CHECK_END(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() > (uint32_t)expected, false, String("Malformed ") + what + " message from script debugger, message too short. Exptected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
+
+Array ScriptDebuggerRemote::ScriptStackDump::serialize() {
+ Array arr;
+ arr.push_back(frames.size() * 3);
+ for (int i = 0; i < frames.size(); i++) {
+ arr.push_back(frames[i].file);
+ arr.push_back(frames[i].line);
+ arr.push_back(frames[i].func);
+ }
+ return arr;
+}
+
+bool ScriptDebuggerRemote::ScriptStackDump::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 1, "ScriptStackDump");
+ uint32_t size = p_arr[0];
+ CHECK_SIZE(p_arr, size, "ScriptStackDump");
+ int idx = 1;
+ for (uint32_t i = 0; i < size / 3; i++) {
+ ScriptLanguage::StackInfo sf;
+ sf.file = p_arr[idx];
+ sf.line = p_arr[idx + 1];
+ sf.func = p_arr[idx + 2];
+ frames.push_back(sf);
+ idx += 3;
+ }
+ CHECK_END(p_arr, idx, "ScriptStackDump");
+ return true;
+}
+
+Array ScriptDebuggerRemote::ScriptStackVariable::serialize(int max_size) {
+ Array arr;
+ arr.push_back(name);
+ arr.push_back(type);
+
+ Variant var = value;
+ if (value.get_type() == Variant::OBJECT && value.get_validated_object() == nullptr) {
+ var = Variant();
+ }
+
+ int len = 0;
+ Error err = encode_variant(var, NULL, len, true);
+ if (err != OK)
+ ERR_PRINT("Failed to encode variant.");
+
+ if (len > max_size) {
+ arr.push_back(Variant());
+ } else {
+ arr.push_back(var);
+ }
+ return arr;
+}
+
+bool ScriptDebuggerRemote::ScriptStackVariable::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 3, "ScriptStackVariable");
+ name = p_arr[0];
+ type = p_arr[1];
+ value = p_arr[2];
+ CHECK_END(p_arr, 3, "ScriptStackVariable");
+ return true;
+}
+
+Array ScriptDebuggerRemote::OutputError::serialize() {
+ Array arr;
+ arr.push_back(hr);
+ arr.push_back(min);
+ arr.push_back(sec);
+ arr.push_back(msec);
+ arr.push_back(source_file);
+ arr.push_back(source_func);
+ arr.push_back(source_line);
+ arr.push_back(error);
+ arr.push_back(error_descr);
+ arr.push_back(warning);
+ unsigned int size = callstack.size();
+ const ScriptLanguage::StackInfo *r = callstack.ptr();
+ arr.push_back(size * 3);
+ for (int i = 0; i < callstack.size(); i++) {
+ arr.push_back(r[i].file);
+ arr.push_back(r[i].func);
+ arr.push_back(r[i].line);
+ }
+ return arr;
+}
+
+bool ScriptDebuggerRemote::OutputError::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 11, "OutputError");
+ hr = p_arr[0];
+ min = p_arr[1];
+ sec = p_arr[2];
+ msec = p_arr[3];
+ source_file = p_arr[4];
+ source_func = p_arr[5];
+ source_line = p_arr[6];
+ error = p_arr[7];
+ error_descr = p_arr[8];
+ warning = p_arr[9];
+ unsigned int stack_size = p_arr[10];
+ CHECK_SIZE(p_arr, stack_size, "OutputError");
+ int idx = 11;
+ callstack.resize(stack_size / 3);
+ ScriptLanguage::StackInfo *w = callstack.ptrw();
+ for (unsigned int i = 0; i < stack_size / 3; i++) {
+ w[i].file = p_arr[idx];
+ w[i].func = p_arr[idx + 1];
+ w[i].line = p_arr[idx + 2];
+ idx += 3;
+ }
+ CHECK_END(p_arr, idx, "OutputError");
+ return true;
+}
+
+Array ScriptDebuggerRemote::ResourceUsage::serialize() {
+ infos.sort();
+
+ Array arr;
+ arr.push_back(infos.size() * 4);
+ for (List<ResourceInfo>::Element *E = infos.front(); E; E = E->next()) {
+ arr.push_back(E->get().path);
+ arr.push_back(E->get().format);
+ arr.push_back(E->get().type);
+ arr.push_back(E->get().vram);
+ }
+ return arr;
+}
+
+bool ScriptDebuggerRemote::ResourceUsage::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 1, "ResourceUsage");
+ uint32_t size = p_arr[0];
+ CHECK_SIZE(p_arr, size, "ResourceUsage");
+ int idx = 1;
+ for (uint32_t i = 0; i < size / 4; i++) {
+ ResourceInfo info;
+ info.path = p_arr[idx];
+ info.format = p_arr[idx + 1];
+ info.type = p_arr[idx + 2];
+ info.vram = p_arr[idx + 3];
+ infos.push_back(info);
+ }
+ CHECK_END(p_arr, idx, "ResourceUsage");
+ return true;
+}
+
+Array ScriptDebuggerRemote::ProfilerSignature::serialize() {
+ Array arr;
+ arr.push_back(name);
+ arr.push_back(id);
+ return arr;
+}
+
+bool ScriptDebuggerRemote::ProfilerSignature::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 2, "ProfilerSignature");
+ name = p_arr[0];
+ id = p_arr[1];
+ CHECK_END(p_arr, 2, "ProfilerSignature");
+ return true;
+}
+
+Array ScriptDebuggerRemote::ProfilerFrame::serialize() {
+ Array arr;
+ arr.push_back(frame_number);
+ arr.push_back(frame_time);
+ arr.push_back(idle_time);
+ arr.push_back(physics_time);
+ arr.push_back(physics_frame_time);
+ arr.push_back(USEC_TO_SEC(script_time));
+
+ arr.push_back(frames_data.size());
+ arr.push_back(frame_functions.size() * 4);
+
+ // Servers profiling info.
+ for (int i = 0; i < frames_data.size(); i++) {
+ arr.push_back(frames_data[i].name); // Type (physics/process/audio/...)
+ arr.push_back(frames_data[i].data.size());
+ for (int j = 0; j < frames_data[i].data.size() / 2; j++) {
+ arr.push_back(frames_data[i].data[2 * j]); // NAME
+ arr.push_back(frames_data[i].data[2 * j + 1]); // TIME
+ }
+ }
+ for (int i = 0; i < frame_functions.size(); i++) {
+ arr.push_back(frame_functions[i].sig_id);
+ arr.push_back(frame_functions[i].call_count);
+ arr.push_back(frame_functions[i].self_time);
+ arr.push_back(frame_functions[i].total_time);
+ }
+ return arr;
+}
+
+bool ScriptDebuggerRemote::ProfilerFrame::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 8, "ProfilerFrame");
+ frame_number = p_arr[0];
+ frame_time = p_arr[1];
+ idle_time = p_arr[2];
+ physics_time = p_arr[3];
+ physics_frame_time = p_arr[4];
+ script_time = p_arr[5];
+ uint32_t frame_data_size = p_arr[6];
+ int frame_func_size = p_arr[7];
+ int idx = 8;
+ while (frame_data_size) {
+ CHECK_SIZE(p_arr, idx + 2, "ProfilerFrame");
+ frame_data_size--;
+ FrameData fd;
+ fd.name = p_arr[idx];
+ int sub_data_size = p_arr[idx + 1];
+ idx += 2;
+ CHECK_SIZE(p_arr, idx + sub_data_size, "ProfilerFrame");
+ for (int j = 0; j < sub_data_size / 2; j++) {
+ fd.data.push_back(p_arr[idx]); // NAME
+ fd.data.push_back(p_arr[idx + 1]); // TIME
+ idx += 2;
+ }
+ frames_data.push_back(fd);
+ }
+ CHECK_SIZE(p_arr, idx + frame_func_size, "ProfilerFrame");
+ for (int i = 0; i < frame_func_size / 4; i++) {
+ FrameFunction ff;
+ ff.sig_id = p_arr[idx];
+ ff.call_count = p_arr[idx + 1];
+ ff.self_time = p_arr[idx + 2];
+ ff.total_time = p_arr[idx + 3];
+ frame_functions.push_back(ff);
+ idx += 4;
+ }
+ CHECK_END(p_arr, idx, "ProfilerFrame");
+ return true;
+}
+
+Array ScriptDebuggerRemote::NetworkProfilerFrame::serialize() {
+ Array arr;
+ arr.push_back(infos.size() * 6);
+ for (int i = 0; i < infos.size(); ++i) {
+ arr.push_back(uint64_t(infos[i].node));
+ arr.push_back(infos[i].node_path);
+ arr.push_back(infos[i].incoming_rpc);
+ arr.push_back(infos[i].incoming_rset);
+ arr.push_back(infos[i].outgoing_rpc);
+ arr.push_back(infos[i].outgoing_rset);
+ }
+ return arr;
+}
+
+bool ScriptDebuggerRemote::NetworkProfilerFrame::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 1, "NetworkProfilerFrame");
+ uint32_t size = p_arr[0];
+ CHECK_SIZE(p_arr, size, "NetworkProfilerFrame");
+ infos.resize(size);
+ int idx = 1;
+ for (uint32_t i = 0; i < size / 6; ++i) {
+ infos.write[i].node = uint64_t(p_arr[idx]);
+ infos.write[i].node_path = p_arr[idx + 1];
+ infos.write[i].incoming_rpc = p_arr[idx + 2];
+ infos.write[i].incoming_rset = p_arr[idx + 3];
+ infos.write[i].outgoing_rpc = p_arr[idx + 4];
+ infos.write[i].outgoing_rset = p_arr[idx + 5];
+ }
+ CHECK_END(p_arr, idx, "NetworkProfilerFrame");
+ return true;
+}
+
+void ScriptDebuggerRemote::_put_msg(String p_message, Array p_data) {
+ Array msg;
+ msg.push_back(p_message);
+ msg.push_back(p_data);
+ packet_peer_stream->put_var(msg);
+}
+
+bool ScriptDebuggerRemote::is_peer_connected() {
+ return tcp_client->is_connected_to_host() && tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED;
+}
+
+void ScriptDebuggerRemote::_send_video_memory() {
+
+ ResourceUsage usage;
+ if (resource_usage_func)
+ resource_usage_func(&usage);
+
+ _put_msg("message:video_mem", usage.serialize());
+}
+
+Error ScriptDebuggerRemote::connect_to_host(const String &p_host, uint16_t p_port) {
+
+ IP_Address ip;
+ if (p_host.is_valid_ip_address())
+ ip = p_host;
+ else
+ ip = IP::get_singleton()->resolve_hostname(p_host);
+
+ int port = p_port;
+
+ const int tries = 6;
+ int waits[tries] = { 1, 10, 100, 1000, 1000, 1000 };
+
+ tcp_client->connect_to_host(ip, port);
+
+ for (int i = 0; i < tries; i++) {
+
+ if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) {
+ print_verbose("Remote Debugger: Connected!");
+ break;
+ } else {
+
+ const int ms = waits[i];
+ OS::get_singleton()->delay_usec(ms * 1000);
+ print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec.");
+ };
+ };
+
+ if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
+
+ ERR_PRINT("Remote Debugger: Unable to connect. Status: " + String::num(tcp_client->get_status()) + ".");
+ return FAILED;
+ };
+
+ packet_peer_stream->set_stream_peer(tcp_client);
+ Array msg;
+ msg.push_back(OS::get_singleton()->get_process_id());
+ send_message("set_pid", msg);
+
+ return OK;
+}
+
+void ScriptDebuggerRemote::_parse_message(const String p_command, const Array &p_data, ScriptLanguage *p_script) {
+
+ if (p_command == "request_video_mem") {
+ _send_video_memory();
+
+ } else if (p_command == "start_profiling") {
+ ERR_FAIL_COND(p_data.size() < 1);
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->profiling_start();
+ }
+
+ max_frame_functions = p_data[0];
+ profiler_function_signature_map.clear();
+ profiling = true;
+ frame_time = 0;
+ idle_time = 0;
+ physics_time = 0;
+ physics_frame_time = 0;
+ print_line("PROFILING ALRIGHT!");
+
+ } else if (p_command == "stop_profiling") {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->profiling_stop();
+ }
+ profiling = false;
+ _send_profiling_data(false);
+ print_line("PROFILING END!");
+
+ } else if (p_command == "start_visual_profiling") {
+
+ visual_profiling = true;
+ VS::get_singleton()->set_frame_profiling_enabled(true);
+ } else if (p_command == "stop_visual_profiling") {
+
+ visual_profiling = false;
+ VS::get_singleton()->set_frame_profiling_enabled(false);
+
+ } else if (p_command == "start_network_profiling") {
+
+ network_profiling = true;
+ multiplayer->profiling_start();
+
+ } else if (p_command == "stop_network_profiling") {
+
+ network_profiling = false;
+ multiplayer->profiling_end();
+
+ } else if (p_command == "reload_scripts") {
+ reload_all_scripts = true;
+
+ } else if (p_command == "breakpoint") {
+ ERR_FAIL_COND(p_data.size() < 3);
+ bool set = p_data[2];
+ if (set)
+ insert_breakpoint(p_data[1], p_data[0]);
+ else
+ remove_breakpoint(p_data[1], p_data[0]);
+
+ } else if (p_command == "set_skip_breakpoints") {
+ ERR_FAIL_COND(p_data.size() < 1);
+ skip_breakpoints = p_data[0];
+
+ } else if (p_command == "get_stack_dump") {
+ ERR_FAIL_COND(!p_script);
+ ScriptStackDump dump;
+ int slc = p_script->debug_get_stack_level_count();
+ for (int i = 0; i < slc; i++) {
+ ScriptLanguage::StackInfo frame;
+ frame.file = p_script->debug_get_stack_level_source(i);
+ frame.line = p_script->debug_get_stack_level_line(i);
+ frame.func = p_script->debug_get_stack_level_function(i);
+ dump.frames.push_back(frame);
+ }
+ _put_msg("stack_dump", dump.serialize());
+
+ } else if (p_command == "get_stack_frame_vars") {
+ ERR_FAIL_COND(p_data.size() != 1);
+ ERR_FAIL_COND(!p_script);
+ int lv = p_data[0];
+
+ List<String> members;
+ List<Variant> member_vals;
+ if (ScriptInstance *inst = p_script->debug_get_stack_level_instance(lv)) {
+ members.push_back("self");
+ member_vals.push_back(inst->get_owner());
+ }
+ p_script->debug_get_stack_level_members(lv, &members, &member_vals);
+ ERR_FAIL_COND(members.size() != member_vals.size());
+
+ List<String> locals;
+ List<Variant> local_vals;
+ p_script->debug_get_stack_level_locals(lv, &locals, &local_vals);
+ ERR_FAIL_COND(locals.size() != local_vals.size());
+
+ List<String> globals;
+ List<Variant> globals_vals;
+ p_script->debug_get_globals(&globals, &globals_vals);
+ ERR_FAIL_COND(globals.size() != globals_vals.size());
+
+ _put_msg("stack_frame_vars", Array());
+
+ ScriptStackVariable stvar;
+ { //locals
+ List<String>::Element *E = locals.front();
+ List<Variant>::Element *F = local_vals.front();
+ while (E) {
+ stvar.name = E->get();
+ stvar.value = F->get();
+ stvar.type = 0;
+ _put_msg("stack_frame_var", stvar.serialize());
+
+ E = E->next();
+ F = F->next();
+ }
+ }
+
+ { //members
+ List<String>::Element *E = members.front();
+ List<Variant>::Element *F = member_vals.front();
+ while (E) {
+ stvar.name = E->get();
+ stvar.value = F->get();
+ stvar.type = 1;
+ _put_msg("stack_frame_var", stvar.serialize());
+
+ E = E->next();
+ F = F->next();
+ }
+ }
+
+ { //globals
+ List<String>::Element *E = globals.front();
+ List<Variant>::Element *F = globals_vals.front();
+ while (E) {
+ stvar.name = E->get();
+ stvar.value = F->get();
+ stvar.type = 2;
+ _put_msg("stack_frame_var", stvar.serialize());
+
+ E = E->next();
+ F = F->next();
+ }
+ }
+
+ } else {
+ if (scene_tree_parse_func) {
+ scene_tree_parse_func(p_command, p_data);
+ }
+ // Unknown message...
+ }
+}
+
+void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint) {
+
+ //this function is called when there is a debugger break (bug on script)
+ //or when execution is paused from editor
+
+ if (skip_breakpoints && !p_is_error_breakpoint)
+ return;
+
+ ERR_FAIL_COND_MSG(!is_peer_connected(), "Script Debugger failed to connect, but being used anyway.");
+
+ Array msg;
+ msg.push_back(p_can_continue);
+ msg.push_back(p_script->debug_get_error());
+ _put_msg("debug_enter", msg);
+
+ skip_profile_frame = true; // to avoid super long frame time for the frame
+
+ Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode();
+ if (mouse_mode != Input::MOUSE_MODE_VISIBLE)
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+
+ uint64_t loop_begin_usec = 0;
+ uint64_t loop_time_sec = 0;
+ while (true) {
+ loop_begin_usec = OS::get_singleton()->get_ticks_usec();
+
+ _get_output();
+
+ if (packet_peer_stream->get_available_packet_count() > 0) {
+
+ Variant var;
+ Error err = packet_peer_stream->get_var(var);
+
+ ERR_CONTINUE(err != OK);
+ ERR_CONTINUE(var.get_type() != Variant::ARRAY);
+
+ Array cmd = var;
+
+ ERR_CONTINUE(cmd.size() != 2);
+ ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
+ ERR_CONTINUE(cmd[1].get_type() != Variant::ARRAY);
+
+ String command = cmd[0];
+ Array data = cmd[1];
+ if (command == "step") {
+
+ set_depth(-1);
+ set_lines_left(1);
+ break;
+ } else if (command == "next") {
+
+ set_depth(0);
+ set_lines_left(1);
+ break;
+
+ } else if (command == "continue") {
+ set_depth(-1);
+ set_lines_left(-1);
+ OS::get_singleton()->move_window_to_foreground();
+ break;
+ } else if (command == "break") {
+ ERR_PRINT("Got break when already broke!");
+ break;
+ }
+
+ _parse_message(command, data, p_script);
+ } else {
+ OS::get_singleton()->delay_usec(10000);
+ OS::get_singleton()->process_and_drop_events();
+ }
+
+ // This is for the camera override to stay live even when the game is paused from the editor
+ loop_time_sec = (OS::get_singleton()->get_ticks_usec() - loop_begin_usec) / 1000000.0f;
+ VisualServer::get_singleton()->sync();
+ if (VisualServer::get_singleton()->has_changed()) {
+ VisualServer::get_singleton()->draw(true, loop_time_sec * Engine::get_singleton()->get_time_scale());
+ }
+ }
+
+ _put_msg("debug_exit", Array());
+
+ if (mouse_mode != Input::MOUSE_MODE_VISIBLE)
+ Input::get_singleton()->set_mouse_mode(mouse_mode);
+}
+
+void ScriptDebuggerRemote::_get_output() {
+
+ mutex->lock();
+ if (output_strings.size()) {
+
+ locking = true;
+
+ while (output_strings.size()) {
+
+ Array arr;
+ arr.push_back(output_strings.front()->get());
+ _put_msg("output", arr);
+ output_strings.pop_front();
+ }
+ locking = false;
+ }
+
+ if (n_messages_dropped > 0) {
+ Message msg;
+ msg.message = "Too many messages! " + String::num_int64(n_messages_dropped) + " messages were dropped.";
+ messages.push_back(msg);
+ n_messages_dropped = 0;
+ }
+
+ while (messages.size()) {
+ locking = true;
+ Message msg = messages.front()->get();
+ _put_msg("message:" + msg.message, msg.data);
+ messages.pop_front();
+ locking = false;
+ }
+
+ if (n_errors_dropped == 1) {
+ // Only print one message about dropping per second
+ OutputError oe;
+ oe.error = "TOO_MANY_ERRORS";
+ oe.error_descr = "Too many errors! Ignoring errors for up to 1 second.";
+ oe.warning = false;
+ uint64_t time = OS::get_singleton()->get_ticks_msec();
+ oe.hr = time / 3600000;
+ oe.min = (time / 60000) % 60;
+ oe.sec = (time / 1000) % 60;
+ oe.msec = time % 1000;
+ errors.push_back(oe);
+ }
+
+ if (n_warnings_dropped == 1) {
+ // Only print one message about dropping per second
+ OutputError oe;
+ oe.error = "TOO_MANY_WARNINGS";
+ oe.error_descr = "Too many warnings! Ignoring warnings for up to 1 second.";
+ oe.warning = true;
+ uint64_t time = OS::get_singleton()->get_ticks_msec();
+ oe.hr = time / 3600000;
+ oe.min = (time / 60000) % 60;
+ oe.sec = (time / 1000) % 60;
+ oe.msec = time % 1000;
+ errors.push_back(oe);
+ }
+
+ while (errors.size()) {
+ locking = true;
+ OutputError oe = errors.front()->get();
+ _put_msg("error", oe.serialize());
+ errors.pop_front();
+ locking = false;
+ }
+ mutex->unlock();
+}
+
+void ScriptDebuggerRemote::line_poll() {
+
+ //the purpose of this is just processing events every now and then when the script might get too busy
+ //otherwise bugs like infinite loops can't be caught
+ if (poll_every % 2048 == 0)
+ _poll_events();
+ poll_every++;
+}
+
+void ScriptDebuggerRemote::_err_handler(void *ud, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type) {
+
+ if (p_type == ERR_HANDLER_SCRIPT)
+ return; //ignore script errors, those go through debugger
+
+ Vector<ScriptLanguage::StackInfo> si;
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ si = ScriptServer::get_language(i)->debug_get_current_stack_info();
+ if (si.size())
+ break;
+ }
+
+ ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)ud;
+ sdr->send_error(p_func, p_file, p_line, p_err, p_descr, p_type, si);
+}
+
+void ScriptDebuggerRemote::_poll_events() {
+
+ //this si called from ::idle_poll, happens only when running the game,
+ //does not get called while on debug break
+
+ while (packet_peer_stream->get_available_packet_count() > 0) {
+
+ _get_output();
+
+ //send over output_strings
+
+ Variant var;
+ Error err = packet_peer_stream->get_var(var);
+
+ ERR_CONTINUE(err != OK);
+ ERR_CONTINUE(var.get_type() != Variant::ARRAY);
+
+ Array cmd = var;
+
+ ERR_CONTINUE(cmd.size() < 2);
+ ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
+ ERR_CONTINUE(cmd[1].get_type() != Variant::ARRAY);
+
+ String command = cmd[0];
+ Array data = cmd[1];
+
+ if (command == "break") {
+
+ if (get_break_language())
+ debug(get_break_language());
+ } else {
+ _parse_message(command, data);
+ }
+ }
+}
+
+void ScriptDebuggerRemote::_send_profiling_data(bool p_for_frame) {
+
+ int ofs = 0;
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ if (p_for_frame)
+ ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&profile_info.write[ofs], profile_info.size() - ofs);
+ else
+ ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&profile_info.write[ofs], profile_info.size() - ofs);
+ }
+
+ for (int i = 0; i < ofs; i++) {
+ profile_info_ptrs.write[i] = &profile_info.write[i];
+ }
+
+ SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa;
+ sa.sort(profile_info_ptrs.ptrw(), ofs);
+
+ int to_send = MIN(ofs, max_frame_functions);
+
+ //check signatures first
+ uint64_t total_script_time = 0;
+
+ for (int i = 0; i < to_send; i++) {
+
+ if (!profiler_function_signature_map.has(profile_info_ptrs[i]->signature)) {
+
+ int idx = profiler_function_signature_map.size();
+ ProfilerSignature sig;
+ sig.name = profile_info_ptrs[i]->signature;
+ sig.id = idx;
+ _put_msg("profile_sig", sig.serialize());
+ profiler_function_signature_map[profile_info_ptrs[i]->signature] = idx;
+ }
+
+ total_script_time += profile_info_ptrs[i]->self_time;
+ }
+
+ //send frames then
+ ProfilerFrame metric;
+ metric.frame_number = Engine::get_singleton()->get_frames_drawn();
+ metric.frame_time = frame_time;
+ metric.idle_time = idle_time;
+ metric.physics_time = physics_time;
+ metric.physics_frame_time = physics_frame_time;
+ metric.script_time = total_script_time;
+
+ // Add script functions information.
+ metric.frame_functions.resize(to_send);
+ FrameFunction *w = metric.frame_functions.ptrw();
+ for (int i = 0; i < to_send; i++) {
+
+ if (profiler_function_signature_map.has(profile_info_ptrs[i]->signature)) {
+ w[i].sig_id = profiler_function_signature_map[profile_info_ptrs[i]->signature];
+ }
+
+ w[i].call_count = profile_info_ptrs[i]->call_count;
+ w[i].total_time = profile_info_ptrs[i]->total_time / 1000000.0;
+ w[i].self_time = profile_info_ptrs[i]->self_time / 1000000.0;
+ }
+ if (p_for_frame) {
+ // Add profile frame data information.
+ metric.frames_data.append_array(profile_frame_data);
+ _put_msg("profile_frame", metric.serialize());
+ profile_frame_data.clear();
+ } else {
+ _put_msg("profile_total", metric.serialize());
+ }
+}
+
+void ScriptDebuggerRemote::idle_poll() {
+
+ // this function is called every frame, except when there is a debugger break (::debug() in this class)
+ // execution stops and remains in the ::debug function
+
+ _get_output();
+
+ if (requested_quit) {
+
+ _put_msg("kill_me", Array());
+ requested_quit = false;
+ }
+
+ if (performance) {
+
+ uint64_t pt = OS::get_singleton()->get_ticks_msec();
+ if (pt - last_perf_time > 1000) {
+
+ last_perf_time = pt;
+ int max = performance->get("MONITOR_MAX");
+ Array arr;
+ arr.resize(max);
+ for (int i = 0; i < max; i++) {
+ arr[i] = performance->call("get_monitor", i);
+ }
+ _put_msg("performance", arr);
+ }
+ }
+
+ if (visual_profiling) {
+ Vector<VS::FrameProfileArea> profile_areas = VS::get_singleton()->get_frame_profile();
+ if (profile_areas.size()) {
+ Vector<String> area_names;
+ Vector<real_t> area_times;
+ area_names.resize(profile_areas.size());
+ area_times.resize(profile_areas.size() * 2);
+ {
+ String *area_namesw = area_names.ptrw();
+ real_t *area_timesw = area_times.ptrw();
+
+ for (int i = 0; i < profile_areas.size(); i++) {
+ area_namesw[i] = profile_areas[i].name;
+ area_timesw[i * 2 + 0] = profile_areas[i].cpu_msec;
+ area_timesw[i * 2 + 1] = profile_areas[i].gpu_msec;
+ }
+ }
+ Array msg;
+ msg.push_back(VS::get_singleton()->get_frame_profile_frame());
+ msg.push_back(area_names);
+ msg.push_back(area_times);
+ _put_msg("visual_profile", msg);
+ }
+ }
+
+ if (profiling) {
+
+ if (skip_profile_frame) {
+ skip_profile_frame = false;
+ } else {
+ //send profiling info normally
+ _send_profiling_data(true);
+ }
+ }
+
+ if (network_profiling) {
+ uint64_t pt = OS::get_singleton()->get_ticks_msec();
+ if (pt - last_net_bandwidth_time > 200) {
+ last_net_bandwidth_time = pt;
+ _send_network_bandwidth_usage();
+ }
+ if (pt - last_net_prof_time > 100) {
+ last_net_prof_time = pt;
+ _send_network_profiling_data();
+ }
+ }
+
+ if (reload_all_scripts) {
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->reload_all_scripts();
+ }
+ reload_all_scripts = false;
+ }
+
+ _poll_events();
+}
+
+void ScriptDebuggerRemote::_send_network_profiling_data() {
+ ERR_FAIL_COND(multiplayer.is_null());
+
+ int n_nodes = multiplayer->get_profiling_frame(&network_profile_info.write[0]);
+
+ NetworkProfilerFrame frame;
+ for (int i = 0; i < n_nodes; i++) {
+ frame.infos.push_back(network_profile_info[i]);
+ }
+ _put_msg("network_profile", frame.serialize());
+}
+
+void ScriptDebuggerRemote::_send_network_bandwidth_usage() {
+ ERR_FAIL_COND(multiplayer.is_null());
+
+ int incoming_bandwidth = multiplayer->get_incoming_bandwidth_usage();
+ int outgoing_bandwidth = multiplayer->get_outgoing_bandwidth_usage();
+
+ Array arr;
+ arr.push_back(incoming_bandwidth);
+ arr.push_back(outgoing_bandwidth);
+ _put_msg("network_bandwidth", arr);
+}
+
+void ScriptDebuggerRemote::send_message(const String &p_message, const Array &p_args) {
+
+ mutex->lock();
+ if (!locking && is_peer_connected()) {
+
+ if (messages.size() >= max_messages_per_frame) {
+ n_messages_dropped++;
+ } else {
+ Message msg;
+ msg.message = p_message;
+ msg.data = p_args;
+ messages.push_back(msg);
+ }
+ }
+ mutex->unlock();
+}
+
+void ScriptDebuggerRemote::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) {
+
+ OutputError oe;
+ oe.error = p_err;
+ oe.error_descr = p_descr;
+ oe.source_file = p_file;
+ oe.source_line = p_line;
+ oe.source_func = p_func;
+ oe.warning = p_type == ERR_HANDLER_WARNING;
+ uint64_t time = OS::get_singleton()->get_ticks_msec();
+ oe.hr = time / 3600000;
+ oe.min = (time / 60000) % 60;
+ oe.sec = (time / 1000) % 60;
+ oe.msec = time % 1000;
+ Array cstack;
+
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000;
+ msec_count += ticks - last_msec;
+ last_msec = ticks;
+
+ if (msec_count > 1000) {
+ msec_count = 0;
+
+ err_count = 0;
+ n_errors_dropped = 0;
+ warn_count = 0;
+ n_warnings_dropped = 0;
+ }
+
+ cstack.resize(p_stack_info.size() * 3);
+ for (int i = 0; i < p_stack_info.size(); i++) {
+ cstack[i * 3 + 0] = p_stack_info[i].file;
+ cstack[i * 3 + 1] = p_stack_info[i].func;
+ cstack[i * 3 + 2] = p_stack_info[i].line;
+ }
+
+ //oe.callstack = cstack;
+ if (oe.warning) {
+ warn_count++;
+ } else {
+ err_count++;
+ }
+
+ mutex->lock();
+
+ if (!locking && is_peer_connected()) {
+
+ if (oe.warning) {
+ if (warn_count > max_warnings_per_second) {
+ n_warnings_dropped++;
+ } else {
+ errors.push_back(oe);
+ }
+ } else {
+ if (err_count > max_errors_per_second) {
+ n_errors_dropped++;
+ } else {
+ errors.push_back(oe);
+ }
+ }
+ }
+
+ mutex->unlock();
+}
+
+void ScriptDebuggerRemote::_print_handler(void *p_this, const String &p_string, bool p_error) {
+
+ ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)p_this;
+
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000;
+ sdr->msec_count += ticks - sdr->last_msec;
+ sdr->last_msec = ticks;
+
+ if (sdr->msec_count > 1000) {
+ sdr->char_count = 0;
+ sdr->msec_count = 0;
+ }
+
+ String s = p_string;
+ int allowed_chars = MIN(MAX(sdr->max_cps - sdr->char_count, 0), s.length());
+
+ if (allowed_chars == 0)
+ return;
+
+ if (allowed_chars < s.length()) {
+ s = s.substr(0, allowed_chars);
+ }
+
+ sdr->char_count += allowed_chars;
+ bool overflowed = sdr->char_count >= sdr->max_cps;
+
+ sdr->mutex->lock();
+ if (!sdr->locking && sdr->is_peer_connected()) {
+
+ if (overflowed)
+ s += "[...]";
+
+ sdr->output_strings.push_back(s);
+
+ if (overflowed) {
+ sdr->output_strings.push_back("[output overflow, print less text!]");
+ }
+ }
+ sdr->mutex->unlock();
+}
+
+void ScriptDebuggerRemote::request_quit() {
+
+ requested_quit = true;
+}
+
+void ScriptDebuggerRemote::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
+ multiplayer = p_multiplayer;
+}
+
+bool ScriptDebuggerRemote::is_profiling() const {
+
+ return profiling;
+}
+void ScriptDebuggerRemote::add_profiling_frame_data(const StringName &p_name, const Array &p_data) {
+
+ int idx = -1;
+ for (int i = 0; i < profile_frame_data.size(); i++) {
+ if (profile_frame_data[i].name == p_name) {
+ idx = i;
+ break;
+ }
+ }
+
+ FrameData fd;
+ fd.name = p_name;
+ fd.data = p_data;
+
+ if (idx == -1) {
+ profile_frame_data.push_back(fd);
+ } else {
+ profile_frame_data.write[idx] = fd;
+ }
+}
+
+void ScriptDebuggerRemote::profiling_start() {
+ //ignores this, uses it via connection
+}
+
+void ScriptDebuggerRemote::profiling_end() {
+ //ignores this, uses it via connection
+}
+
+void ScriptDebuggerRemote::profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+
+ frame_time = p_frame_time;
+ idle_time = p_idle_time;
+ physics_time = p_physics_time;
+ physics_frame_time = p_physics_frame_time;
+}
+
+void ScriptDebuggerRemote::set_skip_breakpoints(bool p_skip_breakpoints) {
+ skip_breakpoints = p_skip_breakpoints;
+}
+
+ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_func = NULL;
+ScriptDebuggerRemote::ParseMessageFunc ScriptDebuggerRemote::scene_tree_parse_func = NULL;
+
+ScriptDebuggerRemote::ScriptDebuggerRemote() :
+ profiling(false),
+ visual_profiling(false),
+ network_profiling(false),
+ max_frame_functions(16),
+ skip_profile_frame(false),
+ reload_all_scripts(false),
+ tcp_client(Ref<StreamPeerTCP>(memnew(StreamPeerTCP))),
+ packet_peer_stream(Ref<PacketPeerStream>(memnew(PacketPeerStream))),
+ last_perf_time(0),
+ last_net_prof_time(0),
+ last_net_bandwidth_time(0),
+ performance(Engine::get_singleton()->get_singleton_object("Performance")),
+ requested_quit(false),
+ mutex(Mutex::create()),
+ max_messages_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_messages_per_frame")),
+ n_messages_dropped(0),
+ max_errors_per_second(GLOBAL_GET("network/limits/debugger_stdout/max_errors_per_second")),
+ max_warnings_per_second(GLOBAL_GET("network/limits/debugger_stdout/max_warnings_per_second")),
+ n_errors_dropped(0),
+ max_cps(GLOBAL_GET("network/limits/debugger_stdout/max_chars_per_second")),
+ char_count(0),
+ err_count(0),
+ warn_count(0),
+ last_msec(0),
+ msec_count(0),
+ locking(false),
+ poll_every(0) {
+
+ packet_peer_stream->set_stream_peer(tcp_client);
+ packet_peer_stream->set_output_buffer_max_size((1024 * 1024 * 8) - 4); // 8 MiB should be way more than enough, minus 4 bytes for separator.
+
+ phl.printfunc = _print_handler;
+ phl.userdata = this;
+ add_print_handler(&phl);
+
+ eh.errfunc = _err_handler;
+ eh.userdata = this;
+ add_error_handler(&eh);
+
+ profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
+ network_profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
+ profile_info_ptrs.resize(profile_info.size());
+}
+
+ScriptDebuggerRemote::~ScriptDebuggerRemote() {
+
+ remove_print_handler(&phl);
+ remove_error_handler(&eh);
+ memdelete(mutex);
+}
diff --git a/core/script_debugger_remote.h b/core/script_debugger_remote.h
new file mode 100644
index 0000000000..682da1d276
--- /dev/null
+++ b/core/script_debugger_remote.h
@@ -0,0 +1,316 @@
+/*************************************************************************/
+/* script_debugger_remote.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_DEBUGGER_REMOTE_H
+#define SCRIPT_DEBUGGER_REMOTE_H
+
+#include "core/io/packet_peer.h"
+#include "core/io/stream_peer_tcp.h"
+#include "core/list.h"
+#include "core/os/os.h"
+#include "core/script_language.h"
+
+class ScriptDebuggerRemote : public ScriptDebugger {
+
+public:
+ class ResourceInfo {
+ public:
+ String path;
+ String format;
+ String type;
+ RID id;
+ int vram;
+ bool operator<(const ResourceInfo &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
+ ResourceInfo() {
+ vram = 0;
+ }
+ };
+
+ class ResourceUsage {
+ public:
+ List<ResourceInfo> infos;
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+ class FrameInfo {
+ public:
+ StringName name;
+ float self_time;
+ float total_time;
+
+ FrameInfo() {
+ self_time = 0;
+ total_time = 0;
+ }
+ };
+
+ class FrameFunction {
+ public:
+ int sig_id;
+ int call_count;
+ StringName name;
+ float self_time;
+ float total_time;
+
+ FrameFunction() {
+ sig_id = -1;
+ call_count = 0;
+ self_time = 0;
+ total_time = 0;
+ }
+ };
+
+ class ScriptStackVariable {
+ public:
+ String name;
+ Variant value;
+ int type;
+ ScriptStackVariable() {
+ type = -1;
+ }
+
+ Array serialize(int max_size = 1 << 20); // 1 MiB default.
+ bool deserialize(const Array &p_arr);
+ };
+
+ class ScriptStackDump {
+ public:
+ List<ScriptLanguage::StackInfo> frames;
+ ScriptStackDump() {}
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+ class Message {
+
+ public:
+ String message;
+ Array data;
+
+ Message() {}
+ };
+
+ class OutputError {
+ public:
+ int hr;
+ int min;
+ int sec;
+ int msec;
+ String source_file;
+ String source_func;
+ int source_line;
+ String error;
+ String error_descr;
+ bool warning;
+ Vector<ScriptLanguage::StackInfo> callstack;
+
+ OutputError() {
+ hr = -1;
+ min = -1;
+ sec = -1;
+ msec = -1;
+ source_line = -1;
+ warning = false;
+ }
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+ struct FrameData {
+
+ StringName name;
+ Array data;
+ };
+
+ class ProfilerSignature {
+ public:
+ StringName name;
+ int id;
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+
+ ProfilerSignature() {
+ id = -1;
+ };
+ };
+
+ class ProfilerFrame {
+ public:
+ int frame_number;
+ float frame_time;
+ float idle_time;
+ float physics_time;
+ float physics_frame_time;
+ float script_time;
+
+ Vector<FrameData> frames_data;
+ Vector<FrameFunction> frame_functions;
+
+ ProfilerFrame() {
+ frame_number = 0;
+ frame_time = 0;
+ idle_time = 0;
+ physics_time = 0;
+ physics_frame_time = 0;
+ }
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+ class NetworkProfilerFrame {
+ public:
+ Vector<MultiplayerAPI::ProfilingInfo> infos;
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+
+ NetworkProfilerFrame(){};
+ };
+
+protected:
+ struct ProfileInfoSort {
+
+ bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {
+ return A->total_time < B->total_time;
+ }
+ };
+
+ Vector<ScriptLanguage::ProfilingInfo> profile_info;
+ Vector<ScriptLanguage::ProfilingInfo *> profile_info_ptrs;
+ Vector<MultiplayerAPI::ProfilingInfo> network_profile_info;
+
+ Map<StringName, int> profiler_function_signature_map;
+ float frame_time, idle_time, physics_time, physics_frame_time;
+
+ bool profiling;
+ bool visual_profiling;
+ bool network_profiling;
+ int max_frame_functions;
+ bool skip_profile_frame;
+ bool reload_all_scripts;
+
+ Ref<StreamPeerTCP> tcp_client;
+ Ref<PacketPeerStream> packet_peer_stream;
+
+ uint64_t last_perf_time;
+ uint64_t last_net_prof_time;
+ uint64_t last_net_bandwidth_time;
+ Object *performance;
+ bool requested_quit;
+ Mutex *mutex;
+
+ List<String> output_strings;
+ List<Message> messages;
+ int max_messages_per_frame;
+ int n_messages_dropped;
+ List<OutputError> errors;
+ int max_errors_per_second;
+ int max_warnings_per_second;
+ int n_errors_dropped;
+ int n_warnings_dropped;
+
+ int max_cps;
+ int char_count;
+ int err_count;
+ int warn_count;
+ uint64_t last_msec;
+ uint64_t msec_count;
+
+ bool locking; //hack to avoid a deadloop
+ static void _print_handler(void *p_this, const String &p_string, bool p_error);
+
+ PrintHandlerList phl;
+
+ void _get_output();
+ void _poll_events();
+ uint32_t poll_every;
+
+ void _parse_message(const String p_command, const Array &p_data, ScriptLanguage *p_script = NULL);
+
+ void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value);
+
+ void _send_object_id(ObjectID p_id);
+ void _send_video_memory();
+
+ Ref<MultiplayerAPI> multiplayer;
+
+ ErrorHandlerList eh;
+ static void _err_handler(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type);
+
+ void _put_msg(String p_message, Array p_data);
+ void _send_profiling_data(bool p_for_frame);
+ void _send_network_profiling_data();
+ void _send_network_bandwidth_usage();
+
+ Vector<FrameData> profile_frame_data;
+
+ bool skip_breakpoints;
+
+public:
+ typedef void (*ResourceUsageFunc)(ResourceUsage *);
+ typedef Error (*ParseMessageFunc)(const String &p_name, const Array &p_msg); // Returns true if something was found (stopping propagation).
+
+ static ResourceUsageFunc resource_usage_func;
+ static ParseMessageFunc scene_tree_parse_func; // Could be made into list, extensible...
+
+ Error connect_to_host(const String &p_host, uint16_t p_port);
+ bool is_peer_connected();
+ virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true, bool p_is_error_breakpoint = false);
+ virtual void idle_poll();
+ virtual void line_poll();
+
+ virtual bool is_remote() const { return true; }
+ virtual void request_quit();
+
+ virtual void send_message(const String &p_message, const Array &p_args);
+ virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info);
+
+ virtual void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
+
+ virtual bool is_profiling() const;
+ virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data);
+
+ virtual void profiling_start();
+ virtual void profiling_end();
+ virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
+
+ virtual void set_skip_breakpoints(bool p_skip_breakpoints);
+
+ ScriptDebuggerRemote();
+ ~ScriptDebuggerRemote();
+};
+
+#endif // SCRIPT_DEBUGGER_REMOTE_H
diff --git a/core/type_info.h b/core/type_info.h
index 8e86567f51..6861531fbd 100644
--- a/core/type_info.h
+++ b/core/type_info.h
@@ -151,6 +151,7 @@ MAKE_TYPE_INFO(AABB, Variant::AABB)
MAKE_TYPE_INFO(Basis, Variant::BASIS)
MAKE_TYPE_INFO(Transform, Variant::TRANSFORM)
MAKE_TYPE_INFO(Color, Variant::COLOR)
+MAKE_TYPE_INFO(StringName, Variant::STRING_NAME)
MAKE_TYPE_INFO(NodePath, Variant::NODE_PATH)
MAKE_TYPE_INFO(RID, Variant::_RID)
MAKE_TYPE_INFO(Callable, Variant::CALLABLE)
@@ -165,7 +166,6 @@ MAKE_TYPE_INFO(PackedVector2Array, Variant::PACKED_VECTOR2_ARRAY)
MAKE_TYPE_INFO(PackedVector3Array, Variant::PACKED_VECTOR3_ARRAY)
MAKE_TYPE_INFO(PackedColorArray, Variant::PACKED_COLOR_ARRAY)
-MAKE_TYPE_INFO(StringName, Variant::STRING)
MAKE_TYPE_INFO(IP_Address, Variant::STRING)
//objectID
diff --git a/core/typedefs.h b/core/typedefs.h
index 0bb80cb2dd..174e65cda7 100644
--- a/core/typedefs.h
+++ b/core/typedefs.h
@@ -357,4 +357,16 @@ struct _GlobalLock {
#define FALLTHROUGH
#endif
+// Home-made index sequence trick, so it can be used everywhere without the costly include of std::tuple.
+// https://stackoverflow.com/questions/15014096/c-index-of-type-during-variadic-template-expansion
+
+template <size_t... Is>
+struct IndexSequence {};
+
+template <size_t N, size_t... Is>
+struct BuildIndexSequence : BuildIndexSequence<N - 1, N - 1, Is...> {};
+
+template <size_t... Is>
+struct BuildIndexSequence<0, Is...> : IndexSequence<Is...> {};
+
#endif // TYPEDEFS_H
diff --git a/core/undo_redo.cpp b/core/undo_redo.cpp
index 0eef75d587..02f460c93d 100644
--- a/core/undo_redo.cpp
+++ b/core/undo_redo.cpp
@@ -105,7 +105,7 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) {
action_level++;
}
-void UndoRedo::add_do_method(Object *p_object, const String &p_method, VARIANT_ARG_DECLARE) {
+void UndoRedo::add_do_method(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) {
VARIANT_ARGPTRS
ERR_FAIL_COND(p_object == NULL);
@@ -125,7 +125,7 @@ void UndoRedo::add_do_method(Object *p_object, const String &p_method, VARIANT_A
actions.write[current_action + 1].do_ops.push_back(do_op);
}
-void UndoRedo::add_undo_method(Object *p_object, const String &p_method, VARIANT_ARG_DECLARE) {
+void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) {
VARIANT_ARGPTRS
ERR_FAIL_COND(p_object == NULL);
@@ -149,7 +149,7 @@ void UndoRedo::add_undo_method(Object *p_object, const String &p_method, VARIANT
}
actions.write[current_action + 1].undo_ops.push_back(undo_op);
}
-void UndoRedo::add_do_property(Object *p_object, const String &p_property, const Variant &p_value) {
+void UndoRedo::add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value) {
ERR_FAIL_COND(p_object == NULL);
ERR_FAIL_COND(action_level <= 0);
@@ -164,7 +164,7 @@ void UndoRedo::add_do_property(Object *p_object, const String &p_property, const
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 String &p_property, const Variant &p_value) {
+void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value) {
ERR_FAIL_COND(p_object == NULL);
ERR_FAIL_COND(action_level <= 0);
@@ -446,17 +446,17 @@ Variant UndoRedo::_add_do_method(const Variant **p_args, int p_argcount, Callabl
return Variant();
}
- if (p_args[1]->get_type() != Variant::STRING) {
+ 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;
+ r_error.expected = Variant::STRING_NAME;
return Variant();
}
r_error.error = Callable::CallError::CALL_OK;
Object *object = *p_args[0];
- String method = *p_args[1];
+ StringName method = *p_args[1];
Variant v[VARIANT_ARG_MAX];
@@ -484,17 +484,17 @@ Variant UndoRedo::_add_undo_method(const Variant **p_args, int p_argcount, Calla
return Variant();
}
- if (p_args[1]->get_type() != Variant::STRING) {
+ 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;
+ r_error.expected = Variant::STRING_NAME;
return Variant();
}
r_error.error = Callable::CallError::CALL_OK;
Object *object = *p_args[0];
- String method = *p_args[1];
+ StringName method = *p_args[1];
Variant v[VARIANT_ARG_MAX];
@@ -518,7 +518,7 @@ void UndoRedo::_bind_methods() {
MethodInfo mi;
mi.name = "add_do_method";
mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object"));
- mi.arguments.push_back(PropertyInfo(Variant::STRING, "method"));
+ 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);
}
@@ -527,7 +527,7 @@ void UndoRedo::_bind_methods() {
MethodInfo mi;
mi.name = "add_undo_method";
mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object"));
- mi.arguments.push_back(PropertyInfo(Variant::STRING, "method"));
+ 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);
}
diff --git a/core/undo_redo.h b/core/undo_redo.h
index e707979291..3b91e9ce36 100644
--- a/core/undo_redo.h
+++ b/core/undo_redo.h
@@ -65,7 +65,7 @@ private:
Type type;
Ref<Resource> resref;
ObjectID object;
- String name;
+ StringName name;
Variant args[VARIANT_ARG_MAX];
};
@@ -103,10 +103,10 @@ protected:
public:
void create_action(const String &p_name = "", MergeMode p_mode = MERGE_DISABLE);
- void add_do_method(Object *p_object, const String &p_method, VARIANT_ARG_LIST);
- void add_undo_method(Object *p_object, const String &p_method, VARIANT_ARG_LIST);
- void add_do_property(Object *p_object, const String &p_property, const Variant &p_value);
- void add_undo_property(Object *p_object, const String &p_property, const Variant &p_value);
+ 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);
diff --git a/core/variant.cpp b/core/variant.cpp
index cdc9e47fc8..8daa359917 100644
--- a/core/variant.cpp
+++ b/core/variant.cpp
@@ -136,6 +136,11 @@ String Variant::get_type_name(Variant::Type p_type) {
return "Signal";
} break;
+ case STRING_NAME: {
+
+ return "StringName";
+
+ } break;
case NODE_PATH: {
return "NodePath";
@@ -325,6 +330,15 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) {
valid_types = valid;
} break;
+ case STRING_NAME: {
+
+ static const Type valid[] = {
+ STRING,
+ NIL
+ };
+
+ valid_types = valid;
+ } break;
case NODE_PATH: {
static const Type valid[] = {
@@ -495,6 +509,7 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type
static const Type valid[] = {
NODE_PATH,
+ STRING_NAME,
NIL
};
@@ -572,6 +587,15 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type
valid_types = valid;
} break;
+ case STRING_NAME: {
+
+ static const Type valid[] = {
+ STRING,
+ NIL
+ };
+
+ valid_types = valid;
+ } break;
case NODE_PATH: {
static const Type valid[] = {
@@ -808,6 +832,11 @@ bool Variant::is_zero() const {
return reinterpret_cast<const Signal *>(_data._mem)->is_null();
} break;
+ case STRING_NAME: {
+
+ return *reinterpret_cast<const StringName *>(_data._mem) != StringName();
+
+ } break;
case NODE_PATH: {
return reinterpret_cast<const NodePath *>(_data._mem)->is_empty();
@@ -1046,6 +1075,11 @@ void Variant::reference(const Variant &p_variant) {
memnew_placement(_data._mem, Signal(*reinterpret_cast<const Signal *>(p_variant._data._mem)));
} break;
+ case STRING_NAME: {
+
+ memnew_placement(_data._mem, StringName(*reinterpret_cast<const StringName *>(p_variant._data._mem)));
+
+ } break;
case NODE_PATH: {
memnew_placement(_data._mem, NodePath(*reinterpret_cast<const NodePath *>(p_variant._data._mem)));
@@ -1152,7 +1186,11 @@ void Variant::clear() {
memdelete(_data._transform);
} break;
- // misc types
+ // misc types
+ case STRING_NAME: {
+
+ reinterpret_cast<StringName *>(_data._mem)->~StringName();
+ } break;
case NODE_PATH: {
reinterpret_cast<NodePath *>(_data._mem)->~NodePath();
@@ -1453,10 +1491,13 @@ Variant::operator double() const {
Variant::operator StringName() const {
- if (type == NODE_PATH) {
- return reinterpret_cast<const NodePath *>(_data._mem)->get_sname();
+ if (type == STRING_NAME) {
+ return *reinterpret_cast<const StringName *>(_data._mem);
+ } else if (type == STRING) {
+ return *reinterpret_cast<const String *>(_data._mem);
}
- return StringName(operator String());
+
+ return StringName();
}
struct _VariantStrPair {
@@ -1523,6 +1564,7 @@ String Variant::stringify(List<const void *> &stack) const {
return mtx + ")";
} break;
case TRANSFORM: return operator Transform();
+ case STRING_NAME: return operator StringName();
case NODE_PATH: return operator NodePath();
case COLOR: return String::num(operator Color().r) + "," + String::num(operator Color().g) + "," + String::num(operator Color().b) + "," + String::num(operator Color().a);
case DICTIONARY: {
@@ -2191,8 +2233,8 @@ Variant::Variant(const ObjectID &p_id) {
Variant::Variant(const StringName &p_string) {
- type = STRING;
- memnew_placement(_data._mem, String(p_string.operator String()));
+ type = STRING_NAME;
+ memnew_placement(_data._mem, StringName(p_string));
}
Variant::Variant(const String &p_string) {
@@ -2549,6 +2591,10 @@ void Variant::operator=(const Variant &p_variant) {
*reinterpret_cast<Signal *>(_data._mem) = *reinterpret_cast<const Signal *>(p_variant._data._mem);
} break;
+ case STRING_NAME: {
+
+ *reinterpret_cast<StringName *>(_data._mem) = *reinterpret_cast<const StringName *>(p_variant._data._mem);
+ } break;
case NODE_PATH: {
*reinterpret_cast<NodePath *>(_data._mem) = *reinterpret_cast<const NodePath *>(p_variant._data._mem);
@@ -2747,6 +2793,10 @@ uint32_t Variant::hash() const {
return hash_djb2_one_64(make_uint64_t(_get_obj().obj));
} break;
+ case STRING_NAME: {
+
+ return reinterpret_cast<const StringName *>(_data._mem)->hash();
+ } break;
case NODE_PATH: {
return reinterpret_cast<const NodePath *>(_data._mem)->hash();
diff --git a/core/variant.h b/core/variant.h
index d41144f4c5..2ad49fe131 100644
--- a/core/variant.h
+++ b/core/variant.h
@@ -46,7 +46,6 @@
#include "core/math/vector3.h"
#include "core/node_path.h"
#include "core/object_id.h"
-
#include "core/rid.h"
#include "core/ustring.h"
@@ -99,6 +98,7 @@ public:
// misc types
COLOR,
+ STRING_NAME,
NODE_PATH, // 15
_RID,
OBJECT,
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index 37c35d7c64..3b82e980bd 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -1215,6 +1215,8 @@ Variant Variant::construct(const Variant::Type p_type, const Variant **p_args, i
// misc types
case COLOR: return Color();
+ case STRING_NAME:
+ return StringName(); // 15
case NODE_PATH:
return NodePath(); // 15
case _RID: return RID();
@@ -1272,10 +1274,14 @@ Variant Variant::construct(const Variant::Type p_type, const Variant **p_args, i
// misc types
case COLOR: return p_args[0]->type == Variant::STRING ? Color::html(*p_args[0]) : Color::hex(*p_args[0]);
+ case STRING_NAME:
+ return (StringName(p_args[0]->operator StringName())); // 15
case NODE_PATH:
return (NodePath(p_args[0]->operator NodePath())); // 15
case _RID: return (RID(*p_args[0]));
case OBJECT: return ((Object *)(p_args[0]->operator Object *()));
+ case CALLABLE: return ((Callable)(p_args[0]->operator Callable()));
+ case SIGNAL: return ((Signal)(p_args[0]->operator Signal()));
case DICTIONARY: return p_args[0]->operator Dictionary();
case ARRAY:
return p_args[0]->operator Array(); // 20
@@ -1840,13 +1846,13 @@ void register_variant_methods() {
ADDFUNC0R(CALLABLE, BOOL, Callable, is_standard, varray());
ADDFUNC0R(CALLABLE, OBJECT, Callable, get_object, varray());
ADDFUNC0R(CALLABLE, INT, Callable, get_object_id, varray());
- ADDFUNC0R(CALLABLE, STRING, Callable, get_method, varray());
+ ADDFUNC0R(CALLABLE, STRING_NAME, Callable, get_method, varray());
ADDFUNC0R(CALLABLE, INT, Callable, hash, varray());
ADDFUNC0R(SIGNAL, BOOL, Signal, is_null, varray());
ADDFUNC0R(SIGNAL, OBJECT, Signal, get_object, varray());
ADDFUNC0R(SIGNAL, INT, Signal, get_object_id, varray());
- ADDFUNC0R(SIGNAL, STRING, Signal, get_name, varray());
+ ADDFUNC0R(SIGNAL, STRING_NAME, Signal, get_name, varray());
ADDFUNC3R(SIGNAL, INT, Signal, connect, CALLABLE, "callable", ARRAY, "binds", INT, "flags", varray(Array(), 0));
@@ -2070,8 +2076,8 @@ void register_variant_methods() {
_VariantCall::add_constructor(_VariantCall::Transform_init1, Variant::TRANSFORM, "x_axis", Variant::VECTOR3, "y_axis", Variant::VECTOR3, "z_axis", Variant::VECTOR3, "origin", Variant::VECTOR3);
_VariantCall::add_constructor(_VariantCall::Transform_init2, Variant::TRANSFORM, "basis", Variant::BASIS, "origin", Variant::VECTOR3);
- _VariantCall::add_constructor(_VariantCall::Callable_init2, Variant::CALLABLE, "object", Variant::OBJECT, "method_name", Variant::STRING);
- _VariantCall::add_constructor(_VariantCall::Signal_init2, Variant::SIGNAL, "object", Variant::OBJECT, "signal_name", Variant::STRING);
+ _VariantCall::add_constructor(_VariantCall::Callable_init2, Variant::CALLABLE, "object", Variant::OBJECT, "method_name", Variant::STRING_NAME);
+ _VariantCall::add_constructor(_VariantCall::Signal_init2, Variant::SIGNAL, "object", Variant::OBJECT, "signal_name", Variant::STRING_NAME);
/* REGISTER CONSTANTS */
diff --git a/core/variant_op.cpp b/core/variant_op.cpp
index 6c98cf4de1..31f36560fd 100644
--- a/core/variant_op.cpp
+++ b/core/variant_op.cpp
@@ -53,6 +53,7 @@
CASE_TYPE(PREFIX, OP, BASIS) \
CASE_TYPE(PREFIX, OP, TRANSFORM) \
CASE_TYPE(PREFIX, OP, COLOR) \
+ CASE_TYPE(PREFIX, OP, STRING_NAME) \
CASE_TYPE(PREFIX, OP, NODE_PATH) \
CASE_TYPE(PREFIX, OP, _RID) \
CASE_TYPE(PREFIX, OP, OBJECT) \
@@ -88,6 +89,7 @@
TYPE(PREFIX, OP, BASIS), \
TYPE(PREFIX, OP, TRANSFORM), \
TYPE(PREFIX, OP, COLOR), \
+ TYPE(PREFIX, OP, STRING_NAME), \
TYPE(PREFIX, OP, NODE_PATH), \
TYPE(PREFIX, OP, _RID), \
TYPE(PREFIX, OP, OBJECT), \
@@ -236,23 +238,35 @@ bool Variant::booleanize() const {
_RETURN_FAIL \
};
-#define DEFAULT_OP_STR_REV(m_prefix, m_op_name, m_name, m_op, m_type) \
- CASE_TYPE(m_prefix, m_op_name, m_name) { \
- if (p_b.type == STRING) _RETURN(*reinterpret_cast<const m_type *>(p_b._data._mem) m_op *reinterpret_cast<const String *>(p_a._data._mem)); \
- if (p_b.type == NODE_PATH) _RETURN(*reinterpret_cast<const m_type *>(p_b._data._mem) m_op *reinterpret_cast<const NodePath *>(p_a._data._mem)); \
- \
- _RETURN_FAIL \
+#define DEFAULT_OP_STR_REV(m_prefix, m_op_name, m_name, m_op, m_type) \
+ CASE_TYPE(m_prefix, m_op_name, m_name) { \
+ if (p_b.type == STRING) _RETURN(*reinterpret_cast<const m_type *>(p_b._data._mem) m_op *reinterpret_cast<const String *>(p_a._data._mem)); \
+ if (p_b.type == STRING_NAME) _RETURN(*reinterpret_cast<const m_type *>(p_b._data._mem) m_op *reinterpret_cast<const StringName *>(p_a._data._mem)); \
+ if (p_b.type == NODE_PATH) _RETURN(*reinterpret_cast<const m_type *>(p_b._data._mem) m_op *reinterpret_cast<const NodePath *>(p_a._data._mem)); \
+ \
+ _RETURN_FAIL \
};
-#define DEFAULT_OP_STR(m_prefix, m_op_name, m_name, m_op, m_type) \
- CASE_TYPE(m_prefix, m_op_name, m_name) { \
- if (p_b.type == STRING) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const String *>(p_b._data._mem)); \
- if (p_b.type == NODE_PATH) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const NodePath *>(p_b._data._mem)); \
- \
- _RETURN_FAIL \
+#define DEFAULT_OP_STR(m_prefix, m_op_name, m_name, m_op, m_type) \
+ CASE_TYPE(m_prefix, m_op_name, m_name) { \
+ if (p_b.type == STRING) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const String *>(p_b._data._mem)); \
+ if (p_b.type == STRING_NAME) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const StringName *>(p_b._data._mem)); \
+ if (p_b.type == NODE_PATH) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const NodePath *>(p_b._data._mem)); \
+ \
+ _RETURN_FAIL \
+ };
+
+#define DEFAULT_OP_STR_NULL(m_prefix, m_op_name, m_name, m_op, m_type) \
+ CASE_TYPE(m_prefix, m_op_name, m_name) { \
+ if (p_b.type == STRING) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const String *>(p_b._data._mem)); \
+ if (p_b.type == STRING_NAME) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const StringName *>(p_b._data._mem)); \
+ if (p_b.type == NODE_PATH) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const NodePath *>(p_b._data._mem)); \
+ if (p_b.type == NIL) _RETURN(!(p_b.type m_op NIL)); \
+ \
+ _RETURN_FAIL \
};
-#define DEFAULT_OP_STR_NULL(m_prefix, m_op_name, m_name, m_op, m_type) \
+#define DEFAULT_OP_STR_NULL_NP(m_prefix, m_op_name, m_name, m_op, m_type) \
CASE_TYPE(m_prefix, m_op_name, m_name) { \
if (p_b.type == STRING) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const String *>(p_b._data._mem)); \
if (p_b.type == NODE_PATH) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const NodePath *>(p_b._data._mem)); \
@@ -261,6 +275,15 @@ bool Variant::booleanize() const {
_RETURN_FAIL \
};
+#define DEFAULT_OP_STR_NULL_SN(m_prefix, m_op_name, m_name, m_op, m_type) \
+ CASE_TYPE(m_prefix, m_op_name, m_name) { \
+ if (p_b.type == STRING) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const String *>(p_b._data._mem)); \
+ if (p_b.type == STRING_NAME) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const StringName *>(p_b._data._mem)); \
+ if (p_b.type == NIL) _RETURN(!(p_b.type m_op NIL)); \
+ \
+ _RETURN_FAIL \
+ };
+
#define DEFAULT_OP_LOCALMEM_REV(m_prefix, m_op_name, m_name, m_op, m_type) \
CASE_TYPE(m_prefix, m_op_name, m_name) { \
if (p_b.type == m_name) \
@@ -477,7 +500,8 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
DEFAULT_OP_PTRREF_NULL(math, OP_EQUAL, BASIS, ==, _basis);
DEFAULT_OP_PTRREF_NULL(math, OP_EQUAL, TRANSFORM, ==, _transform);
DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, COLOR, ==, Color);
- DEFAULT_OP_STR_NULL(math, OP_EQUAL, NODE_PATH, ==, NodePath);
+ DEFAULT_OP_STR_NULL_SN(math, OP_EQUAL, STRING_NAME, ==, StringName);
+ DEFAULT_OP_STR_NULL_NP(math, OP_EQUAL, NODE_PATH, ==, NodePath);
DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, _RID, ==, RID);
DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, PACKED_BYTE_ARRAY, uint8_t);
@@ -570,7 +594,8 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
DEFAULT_OP_PTRREF_NULL(math, OP_NOT_EQUAL, BASIS, !=, _basis);
DEFAULT_OP_PTRREF_NULL(math, OP_NOT_EQUAL, TRANSFORM, !=, _transform);
DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, COLOR, !=, Color);
- DEFAULT_OP_STR_NULL(math, OP_NOT_EQUAL, NODE_PATH, !=, NodePath);
+ DEFAULT_OP_STR_NULL_SN(math, OP_NOT_EQUAL, STRING_NAME, !=, StringName);
+ DEFAULT_OP_STR_NULL_NP(math, OP_NOT_EQUAL, NODE_PATH, !=, NodePath);
DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, _RID, !=, RID);
DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, PACKED_BYTE_ARRAY, uint8_t);
@@ -647,6 +672,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_LESS, BASIS)
CASE_TYPE(math, OP_LESS, TRANSFORM)
CASE_TYPE(math, OP_LESS, COLOR)
+ CASE_TYPE(math, OP_LESS, STRING_NAME)
CASE_TYPE(math, OP_LESS, NODE_PATH)
CASE_TYPE(math, OP_LESS, DICTIONARY)
_RETURN_FAIL;
@@ -676,6 +702,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_LESS_EQUAL, BASIS)
CASE_TYPE(math, OP_LESS_EQUAL, TRANSFORM)
CASE_TYPE(math, OP_LESS_EQUAL, COLOR)
+ CASE_TYPE(math, OP_LESS_EQUAL, STRING_NAME)
CASE_TYPE(math, OP_LESS_EQUAL, NODE_PATH)
CASE_TYPE(math, OP_LESS_EQUAL, CALLABLE)
CASE_TYPE(math, OP_LESS_EQUAL, SIGNAL)
@@ -754,6 +781,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_GREATER, BASIS)
CASE_TYPE(math, OP_GREATER, TRANSFORM)
CASE_TYPE(math, OP_GREATER, COLOR)
+ CASE_TYPE(math, OP_GREATER, STRING_NAME)
CASE_TYPE(math, OP_GREATER, NODE_PATH)
CASE_TYPE(math, OP_GREATER, DICTIONARY)
CASE_TYPE(math, OP_GREATER, CALLABLE)
@@ -786,6 +814,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_GREATER_EQUAL, BASIS)
CASE_TYPE(math, OP_GREATER_EQUAL, TRANSFORM)
CASE_TYPE(math, OP_GREATER_EQUAL, COLOR)
+ CASE_TYPE(math, OP_GREATER_EQUAL, STRING_NAME)
CASE_TYPE(math, OP_GREATER_EQUAL, NODE_PATH)
CASE_TYPE(math, OP_GREATER_EQUAL, CALLABLE)
CASE_TYPE(math, OP_GREATER_EQUAL, SIGNAL)
@@ -844,6 +873,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_ADD, AABB)
CASE_TYPE(math, OP_ADD, BASIS)
CASE_TYPE(math, OP_ADD, TRANSFORM)
+ CASE_TYPE(math, OP_ADD, STRING_NAME)
CASE_TYPE(math, OP_ADD, NODE_PATH)
CASE_TYPE(math, OP_ADD, _RID)
CASE_TYPE(math, OP_ADD, OBJECT)
@@ -871,6 +901,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_SUBTRACT, AABB)
CASE_TYPE(math, OP_SUBTRACT, BASIS)
CASE_TYPE(math, OP_SUBTRACT, TRANSFORM)
+ CASE_TYPE(math, OP_SUBTRACT, STRING_NAME)
CASE_TYPE(math, OP_SUBTRACT, NODE_PATH)
CASE_TYPE(math, OP_SUBTRACT, _RID)
CASE_TYPE(math, OP_SUBTRACT, OBJECT)
@@ -953,6 +984,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_MULTIPLY, RECT2)
CASE_TYPE(math, OP_MULTIPLY, PLANE)
CASE_TYPE(math, OP_MULTIPLY, AABB)
+ CASE_TYPE(math, OP_MULTIPLY, STRING_NAME)
CASE_TYPE(math, OP_MULTIPLY, NODE_PATH)
CASE_TYPE(math, OP_MULTIPLY, _RID)
CASE_TYPE(math, OP_MULTIPLY, OBJECT)
@@ -999,6 +1031,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_DIVIDE, AABB)
CASE_TYPE(math, OP_DIVIDE, BASIS)
CASE_TYPE(math, OP_DIVIDE, TRANSFORM)
+ CASE_TYPE(math, OP_DIVIDE, STRING_NAME)
CASE_TYPE(math, OP_DIVIDE, NODE_PATH)
CASE_TYPE(math, OP_DIVIDE, _RID)
CASE_TYPE(math, OP_DIVIDE, OBJECT)
@@ -1034,6 +1067,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_POSITIVE, BASIS)
CASE_TYPE(math, OP_POSITIVE, TRANSFORM)
CASE_TYPE(math, OP_POSITIVE, COLOR)
+ CASE_TYPE(math, OP_POSITIVE, STRING_NAME)
CASE_TYPE(math, OP_POSITIVE, NODE_PATH)
CASE_TYPE(math, OP_POSITIVE, _RID)
CASE_TYPE(math, OP_POSITIVE, OBJECT)
@@ -1070,6 +1104,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_NEGATE, AABB)
CASE_TYPE(math, OP_NEGATE, BASIS)
CASE_TYPE(math, OP_NEGATE, TRANSFORM)
+ CASE_TYPE(math, OP_NEGATE, STRING_NAME)
CASE_TYPE(math, OP_NEGATE, NODE_PATH)
CASE_TYPE(math, OP_NEGATE, _RID)
CASE_TYPE(math, OP_NEGATE, OBJECT)
@@ -1133,6 +1168,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a,
CASE_TYPE(math, OP_MODULE, BASIS)
CASE_TYPE(math, OP_MODULE, TRANSFORM)
CASE_TYPE(math, OP_MODULE, COLOR)
+ CASE_TYPE(math, OP_MODULE, STRING_NAME)
CASE_TYPE(math, OP_MODULE, NODE_PATH)
CASE_TYPE(math, OP_MODULE, _RID)
CASE_TYPE(math, OP_MODULE, OBJECT)
@@ -2204,6 +2240,8 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid)
}
} break;
+ case STRING_NAME: {
+ } break; // 15
case NODE_PATH: {
} break; // 15
case _RID: {
@@ -2223,7 +2261,7 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid)
}
#endif
- if (p_index.get_type() != Variant::STRING) {
+ if (p_index.get_type() != Variant::STRING_NAME && p_index.get_type() != Variant::STRING) {
obj->setvar(p_index, p_value, r_valid);
return;
}
@@ -2578,6 +2616,8 @@ Variant Variant::get(const Variant &p_index, bool *r_valid) const {
}
} break;
+ case STRING_NAME: {
+ } break; // 15
case NODE_PATH: {
} break; // 15
case _RID: {
@@ -2912,6 +2952,8 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::INT, "a8"));
} break;
+ case STRING_NAME: {
+ } break; // 15
case NODE_PATH: {
} break; // 15
case _RID: {
@@ -3682,6 +3724,10 @@ void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant &
r_dst = reinterpret_cast<const Color *>(a._data._mem)->linear_interpolate(*reinterpret_cast<const Color *>(b._data._mem), c);
}
return;
+ case STRING_NAME: {
+ r_dst = a;
+ }
+ return;
case NODE_PATH: {
r_dst = a;
}
diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp
index f036e00ed5..059dc161c7 100644
--- a/core/variant_parser.cpp
+++ b/core/variant_parser.cpp
@@ -81,6 +81,7 @@ const char *VariantParser::tk_name[TK_MAX] = {
"')'",
"identifier",
"string",
+ "string_name",
"number",
"color",
"':'",
@@ -93,6 +94,8 @@ const char *VariantParser::tk_name[TK_MAX] = {
Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, String &r_err_str) {
+ bool string_name = false;
+
while (true) {
CharType cchar;
@@ -204,6 +207,17 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
r_token.type = TK_COLOR;
return OK;
};
+ case '@': {
+ cchar = p_stream->get_char();
+ if (cchar != '"') {
+ r_err_str = "Expected '\"' after '@'";
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+
+ string_name = true;
+ FALLTHROUGH;
+ }
case '"': {
String str;
@@ -285,8 +299,14 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
if (p_stream->is_utf8()) {
str.parse_utf8(str.ascii(true).get_data());
}
- r_token.type = TK_STRING;
- r_token.value = str;
+ if (string_name) {
+ r_token.type = TK_STRING_NAME;
+ r_token.value = StringName(str);
+ string_name = false; //reset
+ } else {
+ r_token.type = TK_STRING;
+ r_token.value = str;
+ }
return OK;
} break;
@@ -1498,6 +1518,14 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, "Color( " + rtosfix(c.r) + ", " + rtosfix(c.g) + ", " + rtosfix(c.b) + ", " + rtosfix(c.a) + " )");
} break;
+ case Variant::STRING_NAME: {
+
+ String str = p_variant;
+
+ str = "@\"" + str.c_escape() + "\"";
+ p_store_string_func(p_store_string_ud, str);
+
+ } break;
case Variant::NODE_PATH: {
String str = p_variant;
diff --git a/core/variant_parser.h b/core/variant_parser.h
index 89db3ada0d..ad0a4d6682 100644
--- a/core/variant_parser.h
+++ b/core/variant_parser.h
@@ -92,6 +92,7 @@ public:
TK_PARENTHESIS_CLOSE,
TK_IDENTIFIER,
TK_STRING,
+ TK_STRING_NAME,
TK_NUMBER,
TK_COLOR,
TK_COLON,