diff options
Diffstat (limited to 'core/object')
| -rw-r--r-- | core/object/callable_method_pointer.cpp | 58 | ||||
| -rw-r--r-- | core/object/callable_method_pointer.h | 58 | ||||
| -rw-r--r-- | core/object/class_db.cpp | 136 | ||||
| -rw-r--r-- | core/object/class_db.h | 98 | ||||
| -rw-r--r-- | core/object/make_virtuals.py | 24 | ||||
| -rw-r--r-- | core/object/message_queue.cpp | 63 | ||||
| -rw-r--r-- | core/object/message_queue.h | 58 | ||||
| -rw-r--r-- | core/object/method_bind.cpp | 62 | ||||
| -rw-r--r-- | core/object/method_bind.h | 150 | ||||
| -rw-r--r-- | core/object/object.cpp | 262 | ||||
| -rw-r--r-- | core/object/object.h | 339 | ||||
| -rw-r--r-- | core/object/object_id.h | 58 | ||||
| -rw-r--r-- | core/object/ref_counted.cpp | 64 | ||||
| -rw-r--r-- | core/object/ref_counted.h | 63 | ||||
| -rw-r--r-- | core/object/script_language.cpp | 161 | ||||
| -rw-r--r-- | core/object/script_language.h | 81 | ||||
| -rw-r--r-- | core/object/script_language_extension.cpp | 61 | ||||
| -rw-r--r-- | core/object/script_language_extension.h | 191 | ||||
| -rw-r--r-- | core/object/undo_redo.cpp | 222 | ||||
| -rw-r--r-- | core/object/undo_redo.h | 93 | ||||
| -rw-r--r-- | core/object/worker_thread_pool.cpp | 476 | ||||
| -rw-r--r-- | core/object/worker_thread_pool.h | 198 |
22 files changed, 1859 insertions, 1117 deletions
diff --git a/core/object/callable_method_pointer.cpp b/core/object/callable_method_pointer.cpp index 81f8ab6be2..b53985e6b7 100644 --- a/core/object/callable_method_pointer.cpp +++ b/core/object/callable_method_pointer.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* callable_method_pointer.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* callable_method_pointer.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "callable_method_pointer.h" diff --git a/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h index f2a440b49a..1584906710 100644 --- a/core/object/callable_method_pointer.h +++ b/core/object/callable_method_pointer.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* callable_method_pointer.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* callable_method_pointer.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef CALLABLE_METHOD_POINTER_H #define CALLABLE_METHOD_POINTER_H diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index ac008dad88..a602dc9fb8 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -1,36 +1,37 @@ -/*************************************************************************/ -/* class_db.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* class_db.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "class_db.h" #include "core/config/engine.h" +#include "core/object/script_language.h" #include "core/os/mutex.h" #include "core/version.h" @@ -305,12 +306,19 @@ void ClassDB::add_compatibility_class(const StringName &p_class, const StringNam compat_classes[p_class] = p_fallback; } +StringName ClassDB::get_compatibility_class(const StringName &p_class) { + if (compat_classes.has(p_class)) { + return compat_classes[p_class]; + } + return StringName(); +} + Object *ClassDB::instantiate(const StringName &p_class) { ClassInfo *ti; { OBJTYPE_RLOCK; ti = classes.getptr(p_class); - if (!ti || ti->disabled || !ti->creation_func || (ti->native_extension && !ti->native_extension->create_instance)) { + if (!ti || ti->disabled || !ti->creation_func || (ti->gdextension && !ti->gdextension->create_instance)) { if (compat_classes.has(p_class)) { ti = classes.getptr(compat_classes[p_class]); } @@ -325,8 +333,8 @@ Object *ClassDB::instantiate(const StringName &p_class) { return nullptr; } #endif - if (ti->native_extension && ti->native_extension->create_instance) { - return (Object *)ti->native_extension->create_instance(ti->native_extension->class_userdata); + if (ti->gdextension && ti->gdextension->create_instance) { + return (Object *)ti->gdextension->create_instance(ti->gdextension->class_userdata); } else { return ti->creation_func(); } @@ -338,17 +346,17 @@ void ClassDB::set_object_extension_instance(Object *p_object, const StringName & { OBJTYPE_RLOCK; ti = classes.getptr(p_class); - if (!ti || ti->disabled || !ti->creation_func || (ti->native_extension && !ti->native_extension->create_instance)) { + if (!ti || ti->disabled || !ti->creation_func || (ti->gdextension && !ti->gdextension->create_instance)) { if (compat_classes.has(p_class)) { ti = classes.getptr(compat_classes[p_class]); } } ERR_FAIL_COND_MSG(!ti, "Cannot get class '" + String(p_class) + "'."); ERR_FAIL_COND_MSG(ti->disabled, "Class '" + String(p_class) + "' is disabled."); - ERR_FAIL_COND_MSG(!ti->native_extension, "Class '" + String(p_class) + "' has no native extension."); + ERR_FAIL_COND_MSG(!ti->gdextension, "Class '" + String(p_class) + "' has no native extension."); } - p_object->_extension = ti->native_extension; + p_object->_extension = ti->gdextension; p_object->_extension_instance = p_instance; } @@ -362,20 +370,25 @@ bool ClassDB::can_instantiate(const StringName &p_class) { return false; } #endif - return (!ti->disabled && ti->creation_func != nullptr && !(ti->native_extension && !ti->native_extension->create_instance)); + return (!ti->disabled && ti->creation_func != nullptr && !(ti->gdextension && !ti->gdextension->create_instance)); } bool ClassDB::is_virtual(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'."); + if (!ti) { + if (!ScriptServer::is_global_class(p_class)) { + ERR_FAIL_V_MSG(false, "Cannot get class '" + String(p_class) + "'."); + } + return false; + } #ifdef TOOLS_ENABLED if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { return false; } #endif - return (!ti->disabled && ti->creation_func != nullptr && !(ti->native_extension && !ti->native_extension->create_instance) && ti->is_virtual); + return (!ti->disabled && ti->creation_func != nullptr && !(ti->gdextension && !ti->gdextension->create_instance) && ti->is_virtual); } void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) { @@ -956,8 +969,11 @@ void ClassDB::add_linked_property(const StringName &p_class, const String &p_pro ERR_FAIL_COND(!type->property_map.has(p_property)); ERR_FAIL_COND(!type->property_map.has(p_linked_property)); - PropertyInfo &pinfo = type->property_map[p_property]; - pinfo.linked_properties.push_back(p_linked_property); + if (!type->linked_properties.has(p_property)) { + type->linked_properties.insert(p_property, List<StringName>()); + } + type->linked_properties[p_property].push_back(p_linked_property); + #endif } @@ -971,7 +987,7 @@ void ClassDB::get_property_list(const StringName &p_class, List<PropertyInfo> *p if (p_validator) { // Making a copy as we may modify it. PropertyInfo pi_mut = pi; - p_validator->_validate_property(pi_mut); + p_validator->validate_property(pi_mut); p_list->push_back(pi_mut); } else { p_list->push_back(pi); @@ -985,6 +1001,25 @@ void ClassDB::get_property_list(const StringName &p_class, List<PropertyInfo> *p } } +void ClassDB::get_linked_properties_info(const StringName &p_class, const StringName &p_property, List<StringName> *r_properties, bool p_no_inheritance) { +#ifdef TOOLS_ENABLED + ClassInfo *check = classes.getptr(p_class); + while (check) { + if (!check->linked_properties.has(p_property)) { + return; + } + for (const StringName &E : check->linked_properties[p_property]) { + r_properties->push_back(E); + } + + if (p_no_inheritance) { + break; + } + check = check->inherits_ptr; + } +#endif +} + bool ClassDB::get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance, const Object *p_validator) { OBJTYPE_RLOCK; @@ -993,7 +1028,7 @@ bool ClassDB::get_property_info(const StringName &p_class, const StringName &p_p if (check->property_map.has(p_property)) { PropertyInfo pinfo = check->property_map[p_property]; if (p_validator) { - p_validator->_validate_property(pinfo); + p_validator->validate_property(pinfo); } if (r_info) { *r_info = pinfo; @@ -1425,7 +1460,7 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con if (Engine::get_singleton()->has_singleton(p_class)) { c = Engine::get_singleton()->get_singleton_object(p_class); cleanup_c = false; - } else if (ClassDB::can_instantiate(p_class) && !ClassDB::is_virtual(p_class)) { + } else if (ClassDB::can_instantiate(p_class) && !ClassDB::is_virtual(p_class)) { // Keep this condition in sync with doc_tools.cpp get_documentation_default_value. c = ClassDB::instantiate(p_class); cleanup_c = true; } @@ -1487,7 +1522,7 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con return var; } -void ClassDB::register_extension_class(ObjectNativeExtension *p_extension) { +void ClassDB::register_extension_class(ObjectGDExtension *p_extension) { GLOBAL_LOCK_FUNCTION; ERR_FAIL_COND_MSG(classes.has(p_extension->class_name), "Class already registered: " + String(p_extension->class_name)); @@ -1497,9 +1532,12 @@ void ClassDB::register_extension_class(ObjectNativeExtension *p_extension) { ClassInfo c; c.api = p_extension->editor_class ? API_EDITOR_EXTENSION : API_EXTENSION; - c.native_extension = p_extension; + c.gdextension = p_extension; c.name = p_extension->class_name; - c.creation_func = parent->creation_func; + c.is_virtual = p_extension->is_virtual; + if (!p_extension->is_abstract) { + c.creation_func = parent->creation_func; + } c.inherits = parent->name; c.class_ptr = parent->class_ptr; c.inherits_ptr = parent; @@ -1509,7 +1547,11 @@ void ClassDB::register_extension_class(ObjectNativeExtension *p_extension) { } void ClassDB::unregister_extension_class(const StringName &p_class) { - ERR_FAIL_COND(!classes.has(p_class)); + ClassInfo *c = classes.getptr(p_class); + ERR_FAIL_COND_MSG(!c, "Class " + p_class + "does not exist"); + for (KeyValue<StringName, MethodBind *> &F : c->method_map) { + memdelete(F.value); + } classes.erase(p_class); } diff --git a/core/object/class_db.h b/core/object/class_db.h index 1d26eb18f1..0e408e8845 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* class_db.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* class_db.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef CLASS_DB_H #define CLASS_DB_H @@ -100,7 +100,7 @@ public: ClassInfo *inherits_ptr = nullptr; void *class_ptr = nullptr; - ObjectNativeExtension *native_extension = nullptr; + ObjectGDExtension *gdextension = nullptr; HashMap<StringName, MethodBind *> method_map; HashMap<StringName, int64_t> constant_map; @@ -120,6 +120,7 @@ public: List<MethodInfo> virtual_methods; HashMap<StringName, MethodInfo> virtual_methods_map; HashMap<StringName, Vector<Error>> method_error_values; + HashMap<StringName, List<StringName>> linked_properties; #endif HashMap<StringName, PropertySetGet> property_setget; @@ -202,7 +203,7 @@ public: //nothing } - static void register_extension_class(ObjectNativeExtension *p_extension); + static void register_extension_class(ObjectGDExtension *p_extension); static void unregister_extension_class(const StringName &p_class); template <class T> @@ -312,6 +313,7 @@ public: static void add_linked_property(const StringName &p_class, const String &p_property, const String &p_linked_property); static void get_property_list(const StringName &p_class, List<PropertyInfo> *p_list, bool p_no_inheritance = false, const Object *p_validator = nullptr); static bool get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance = false, const Object *p_validator = nullptr); + static void get_linked_properties_info(const StringName &p_class, const StringName &p_property, List<StringName> *r_properties, bool p_no_inheritance = false); static bool set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid = nullptr); static bool get_property(Object *p_object, const StringName &p_property, Variant &r_value); static bool has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance = false); @@ -357,6 +359,7 @@ public: static bool is_resource_extension(const StringName &p_extension); static void add_compatibility_class(const StringName &p_class, const StringName &p_fallback); + static StringName get_compatibility_class(const StringName &p_class); static void set_current_api(APIType p_api); static APIType get_current_api(); @@ -369,17 +372,17 @@ public: static uint64_t get_native_struct_size(const StringName &p_name); // Used for asserting }; -#ifdef DEBUG_METHODS_ENABLED - -#define BIND_CONSTANT(m_constant) \ - ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant); - #define BIND_ENUM_CONSTANT(m_constant) \ ::ClassDB::bind_integer_constant(get_class_static(), __constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant); #define BIND_BITFIELD_FLAG(m_constant) \ ::ClassDB::bind_integer_constant(get_class_static(), __constant_get_bitfield_name(m_constant, #m_constant), #m_constant, m_constant, true); +#define BIND_CONSTANT(m_constant) \ + ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant); + +#ifdef DEBUG_METHODS_ENABLED + _FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr) { } @@ -405,29 +408,20 @@ _FORCE_INLINE_ Vector<Error> errarray(P... p_args) { #else -#define BIND_CONSTANT(m_constant) \ - ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant); - -#define BIND_ENUM_CONSTANT(m_constant) \ - ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant); - -#define BIND_BITFIELD_FLAG(m_constant) \ - ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant, true); - #define BIND_METHOD_ERR_RETURN_DOC(m_method, ...) #endif -#define GDREGISTER_CLASS(m_class) \ - if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \ - ::ClassDB::register_class<m_class>(); \ +#define GDREGISTER_CLASS(m_class) \ + if (m_class::_class_is_enabled) { \ + ::ClassDB::register_class<m_class>(); \ } -#define GDREGISTER_VIRTUAL_CLASS(m_class) \ - if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \ - ::ClassDB::register_class<m_class>(true); \ +#define GDREGISTER_VIRTUAL_CLASS(m_class) \ + if (m_class::_class_is_enabled) { \ + ::ClassDB::register_class<m_class>(true); \ } #define GDREGISTER_ABSTRACT_CLASS(m_class) \ - if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \ + if (m_class::_class_is_enabled) { \ ::ClassDB::register_abstract_class<m_class>(); \ } diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py index c18d70d9f6..0ee95835a6 100644 --- a/core/object/make_virtuals.py +++ b/core/object/make_virtuals.py @@ -2,21 +2,22 @@ proto = """ #define GDVIRTUAL$VER($RET m_name $ARG) \\ StringName _gdvirtual_##m_name##_sn = #m_name;\\ mutable bool _gdvirtual_##m_name##_initialized = false;\\ -mutable GDNativeExtensionClassCallVirtual _gdvirtual_##m_name = nullptr;\\ +mutable GDExtensionClassCallVirtual _gdvirtual_##m_name = nullptr;\\ template<bool required>\\ _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\ - ScriptInstance *script_instance = ((Object*)(this))->get_script_instance();\\ - if (script_instance) {\\ + ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\ + if (_script_instance) {\\ Callable::CallError ce; \\ $CALLSIARGS\\ - $CALLSIBEGINscript_instance->callp(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\ + $CALLSIBEGIN_script_instance->callp(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\ if (ce.error == Callable::CallError::CALL_OK) {\\ $CALLSIRET\\ return true;\\ } \\ }\\ if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ - _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, #m_name) : (GDNativeExtensionClassCallVirtual) nullptr;\\ + /* TODO: C-style cast because GDExtensionStringNamePtr's const qualifier is broken (see https://github.com/godotengine/godot/pull/67751) */\\ + _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, (GDExtensionStringNamePtr)&_gdvirtual_##m_name##_sn) : (GDExtensionClassCallVirtual) nullptr;\\ _gdvirtual_##m_name##_initialized = true;\\ }\\ if (_gdvirtual_##m_name) {\\ @@ -35,12 +36,13 @@ _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\ return false;\\ }\\ _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const { \\ - ScriptInstance *script_instance = ((Object*)(this))->get_script_instance();\\ - if (script_instance) {\\ - return script_instance->has_method(_gdvirtual_##m_name##_sn);\\ + ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\ + if (_script_instance) {\\ + return _script_instance->has_method(_gdvirtual_##m_name##_sn);\\ }\\ if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ - _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, #m_name) : (GDNativeExtensionClassCallVirtual) nullptr;\\ + /* TODO: C-style cast because GDExtensionStringNamePtr's const qualifier is broken (see https://github.com/godotengine/godot/pull/67751) */\\ + _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, (GDExtensionStringNamePtr)&_gdvirtual_##m_name##_sn) : (GDExtensionClassCallVirtual) nullptr;\\ _gdvirtual_##m_name##_initialized = true;\\ }\\ if (_gdvirtual_##m_name) {\\ @@ -92,7 +94,7 @@ def generate_version(argcount, const=False, returns=False): argtext += ", " callsiargs = "Variant vargs[" + str(argcount) + "]={" callsiargptrs = "\t\tconst Variant *vargptrs[" + str(argcount) + "]={" - callptrargsptr = "\t\tconst GDNativeTypePtr argptrs[" + str(argcount) + "]={" + callptrargsptr = "\t\tGDExtensionConstTypePtr argptrs[" + str(argcount) + "]={" callptrargs = "" for i in range(argcount): if i > 0: @@ -119,7 +121,7 @@ def generate_version(argcount, const=False, returns=False): s = s.replace("$CALLSIARGPASS", "(const Variant **)vargptrs," + str(argcount)) callptrargsptr += "};\\\n" s = s.replace("$CALLPTRARGS", callptrargs + callptrargsptr) - s = s.replace("$CALLPTRARGPASS", "(const GDNativeTypePtr*)argptrs") + s = s.replace("$CALLPTRARGPASS", "reinterpret_cast<GDExtensionConstTypePtr*>(argptrs)") else: s = s.replace("$CALLSIARGS", "") s = s.replace("$CALLSIARGPASS", "nullptr, 0") diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp index fa1945cf79..ebed6c21e9 100644 --- a/core/object/message_queue.cpp +++ b/core/object/message_queue.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* message_queue.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* message_queue.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "message_queue.h" @@ -226,7 +226,7 @@ void MessageQueue::_call_function(const Callable &p_callable, const Variant *p_a Callable::CallError ce; Variant ret; - p_callable.call(argptrs, p_argcount, ret, ce); + p_callable.callp(argptrs, p_argcount, ret, ce); if (p_show_error && ce.error != Callable::CallError::CALL_OK) { ERR_PRINT("Error calling deferred method: " + Variant::get_callable_error_text(p_callable, argptrs, p_argcount, ce) + "."); } @@ -314,8 +314,7 @@ MessageQueue::MessageQueue() { ERR_FAIL_COND_MSG(singleton != nullptr, "A MessageQueue singleton already exists."); singleton = this; - buffer_size = GLOBAL_DEF_RST("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB); - ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/message_queue/max_size_kb", PropertyInfo(Variant::INT, "memory/limits/message_queue/max_size_kb", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); + buffer_size = GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "memory/limits/message_queue/max_size_kb", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater"), DEFAULT_QUEUE_SIZE_KB); buffer_size *= 1024; buffer = memnew_arr(uint8_t, buffer_size); } diff --git a/core/object/message_queue.h b/core/object/message_queue.h index 2219cdb8f6..ceb6f2a3f1 100644 --- a/core/object/message_queue.h +++ b/core/object/message_queue.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* message_queue.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* message_queue.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef MESSAGE_QUEUE_H #define MESSAGE_QUEUE_H diff --git a/core/object/method_bind.cpp b/core/object/method_bind.cpp index a4474ea53b..aa0cdedafc 100644 --- a/core/object/method_bind.cpp +++ b/core/object/method_bind.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* method_bind.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* method_bind.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ // object.h needs to be the first include *before* method_bind.h // FIXME: Find out why and fix potential cyclical dependencies. @@ -63,7 +63,9 @@ PropertyInfo MethodBind::get_argument_info(int p_argument) const { PropertyInfo info = _gen_argument_type_info(p_argument); #ifdef DEBUG_METHODS_ENABLED - info.name = p_argument < arg_names.size() ? String(arg_names[p_argument]) : String("arg" + itos(p_argument)); + if (info.name.is_empty()) { + info.name = p_argument < arg_names.size() ? String(arg_names[p_argument]) : String("_unnamed_arg" + itos(p_argument)); + } #endif return info; } diff --git a/core/object/method_bind.h b/core/object/method_bind.h index d60550c899..8334a7eef6 100644 --- a/core/object/method_bind.h +++ b/core/object/method_bind.h @@ -1,39 +1,39 @@ -/*************************************************************************/ -/* method_bind.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* method_bind.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef METHOD_BIND_H #define METHOD_BIND_H #include "core/variant/binder_common.h" -VARIANT_ENUM_CAST(MethodFlags) +VARIANT_BITFIELD_CAST(MethodFlags) // some helpers @@ -110,8 +110,8 @@ public: _FORCE_INLINE_ int get_argument_count() const { return argument_count; }; - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) = 0; - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) = 0; + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const = 0; + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const = 0; StringName get_name() const; void set_name(const StringName &p_name); @@ -158,7 +158,7 @@ public: } #endif - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override { + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override { ERR_FAIL(); // Can't call. } @@ -210,7 +210,7 @@ class MethodBindVarArgT : public MethodBindVarArgBase<MethodBindVarArgT<T>, T, v friend class MethodBindVarArgBase<MethodBindVarArgT<T>, T, void, false>; public: - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { (static_cast<T *>(p_object)->*MethodBindVarArgBase<MethodBindVarArgT<T>, T, void, false>::method)(p_args, p_arg_count, r_error); return {}; } @@ -241,9 +241,17 @@ class MethodBindVarArgTR : public MethodBindVarArgBase<MethodBindVarArgTR<T, R>, friend class MethodBindVarArgBase<MethodBindVarArgTR<T, R>, T, R, true>; public: - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { +#if defined(SANITIZERS_ENABLED) && defined(__GNUC__) && !defined(__clang__) + // Workaround GH-66343 raised only with UBSAN, seems to be a false positive. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { return (static_cast<T *>(p_object)->*MethodBindVarArgBase<MethodBindVarArgTR<T, R>, T, R, true>::method)(p_args, p_arg_count, r_error); } +#if defined(SANITIZERS_ENABLED) && defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif MethodBindVarArgTR( R (T::*p_method)(const Variant **, int, Callable::CallError &), @@ -284,11 +292,6 @@ class MethodBindT : public MethodBind { void (MB_T::*method)(P...); protected: -// GCC raises warnings in the case P = {} as the comparison is always false... -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wlogical-op" -#endif virtual Variant::Type _gen_argument_type(int p_arg) const override { if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { return call_get_argument_type<P...>(p_arg); @@ -296,9 +299,6 @@ protected: return Variant::NIL; } } -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif virtual PropertyInfo _gen_argument_type_info(int p_arg) const override { PropertyInfo pi; @@ -313,7 +313,7 @@ public: } #endif - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { #ifdef TYPED_METHOD_BIND call_with_variant_args_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments()); #else @@ -322,7 +322,7 @@ public: return Variant(); } - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override { + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override { #ifdef TYPED_METHOD_BIND call_with_ptr_args<T, P...>(static_cast<T *>(p_object), method, p_args); #else @@ -359,11 +359,6 @@ class MethodBindTC : public MethodBind { void (MB_T::*method)(P...) const; protected: -// GCC raises warnings in the case P = {} as the comparison is always false... -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wlogical-op" -#endif virtual Variant::Type _gen_argument_type(int p_arg) const override { if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { return call_get_argument_type<P...>(p_arg); @@ -371,9 +366,6 @@ protected: return Variant::NIL; } } -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif virtual PropertyInfo _gen_argument_type_info(int p_arg) const override { PropertyInfo pi; @@ -388,7 +380,7 @@ public: } #endif - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { #ifdef TYPED_METHOD_BIND call_with_variant_argsc_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments()); #else @@ -397,7 +389,7 @@ public: return Variant(); } - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override { + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override { #ifdef TYPED_METHOD_BIND call_with_ptr_argsc<T, P...>(static_cast<T *>(p_object), method, p_args); #else @@ -436,11 +428,6 @@ class MethodBindTR : public MethodBind { (P...); protected: -// GCC raises warnings in the case P = {} as the comparison is always false... -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wlogical-op" -#endif virtual Variant::Type _gen_argument_type(int p_arg) const override { if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { return call_get_argument_type<P...>(p_arg); @@ -458,9 +445,6 @@ protected: return GetTypeInfo<R>::get_class_info(); } } -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif public: #ifdef DEBUG_METHODS_ENABLED @@ -473,7 +457,7 @@ public: } #endif - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { Variant ret; #ifdef TYPED_METHOD_BIND call_with_variant_args_ret_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments()); @@ -483,7 +467,7 @@ public: return ret; } - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override { + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override { #ifdef TYPED_METHOD_BIND call_with_ptr_args_ret<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret); #else @@ -523,11 +507,6 @@ class MethodBindTRC : public MethodBind { (P...) const; protected: -// GCC raises warnings in the case P = {} as the comparison is always false... -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wlogical-op" -#endif virtual Variant::Type _gen_argument_type(int p_arg) const override { if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { return call_get_argument_type<P...>(p_arg); @@ -545,9 +524,6 @@ protected: return GetTypeInfo<R>::get_class_info(); } } -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif public: #ifdef DEBUG_METHODS_ENABLED @@ -560,7 +536,7 @@ public: } #endif - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { Variant ret; #ifdef TYPED_METHOD_BIND call_with_variant_args_retc_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments()); @@ -570,7 +546,7 @@ public: return ret; } - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override { + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override { #ifdef TYPED_METHOD_BIND call_with_ptr_args_retc<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret); #else @@ -607,11 +583,6 @@ class MethodBindTS : public MethodBind { void (*function)(P...); protected: -// GCC raises warnings in the case P = {} as the comparison is always false... -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wlogical-op" -#endif virtual Variant::Type _gen_argument_type(int p_arg) const override { if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { return call_get_argument_type<P...>(p_arg); @@ -619,9 +590,6 @@ protected: return Variant::NIL; } } -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif virtual PropertyInfo _gen_argument_type_info(int p_arg) const override { PropertyInfo pi; @@ -636,13 +604,13 @@ public: } #endif - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { (void)p_object; // unused call_with_variant_args_static_dv(function, p_args, p_arg_count, r_error, get_default_arguments()); return Variant(); } - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override { + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override { (void)p_object; (void)r_ret; call_with_ptr_args_static_method(function, p_args); @@ -670,11 +638,6 @@ class MethodBindTRS : public MethodBind { (P...); protected: -// GCC raises warnings in the case P = {} as the comparison is always false... -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wlogical-op" -#endif virtual Variant::Type _gen_argument_type(int p_arg) const override { if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { return call_get_argument_type<P...>(p_arg); @@ -682,9 +645,6 @@ protected: return GetTypeInfo<R>::VARIANT_TYPE; } } -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif virtual PropertyInfo _gen_argument_type_info(int p_arg) const override { if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { @@ -707,13 +667,13 @@ public: } #endif - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { Variant ret; call_with_variant_args_static_ret_dv(function, p_args, p_arg_count, ret, r_error, get_default_arguments()); return ret; } - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override { + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override { (void)p_object; call_with_ptr_args_static_method_ret(function, p_args, r_ret); } diff --git a/core/object/object.cpp b/core/object/object.cpp index 5f2287c9d3..2cb56dfe6c 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* object.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* object.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "object.h" @@ -38,6 +38,7 @@ #include "core/os/os.h" #include "core/string/print_string.h" #include "core/string/translation.h" +#include "core/variant/typed_array.h" #ifdef DEBUG_ENABLED @@ -102,8 +103,8 @@ PropertyInfo PropertyInfo::from_dict(const Dictionary &p_dict) { return pi; } -Array convert_property_list(const List<PropertyInfo> *p_list) { - Array va; +TypedArray<Dictionary> convert_property_list(const List<PropertyInfo> *p_list) { + TypedArray<Dictionary> va; for (const List<PropertyInfo>::Element *E = p_list->front(); E; E = E->next()) { va.push_back(Dictionary(E->get())); } @@ -166,7 +167,6 @@ Object::Connection::operator Variant() const { d["signal"] = signal; d["callable"] = callable; d["flags"] = flags; - d["binds"] = binds; return d; } @@ -189,9 +189,6 @@ Object::Connection::Connection(const Variant &p_variant) { if (d.has("flags")) { flags = d["flags"]; } - if (d.has("binds")) { - binds = d["binds"]; - } } bool Object::_predelete() { @@ -236,7 +233,7 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wignored-qualifiers" #endif - if (_extension->set(_extension_instance, (const GDNativeStringNamePtr)&p_name, (const GDNativeVariantPtr)&p_value)) { + if (_extension->set(_extension_instance, (const GDExtensionStringNamePtr)&p_name, (const GDExtensionVariantPtr)&p_value)) { if (r_valid) { *r_valid = true; } @@ -324,7 +321,7 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const { #pragma GCC diagnostic ignored "-Wignored-qualifiers" #endif - if (_extension->get(_extension_instance, (const GDNativeStringNamePtr)&p_name, (GDNativeVariantPtr)&ret)) { + if (_extension->get(_extension_instance, (const GDExtensionStringNamePtr)&p_name, (GDExtensionVariantPtr)&ret)) { if (r_valid) { *r_valid = true; } @@ -476,12 +473,11 @@ Variant Object::get_indexed(const Vector<StringName> &p_names, bool *r_valid) co void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) const { if (script_instance && p_reversed) { - p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); script_instance->get_property_list(p_list); } if (_extension) { - const ObjectNativeExtension *current_extension = _extension; + const ObjectGDExtension *current_extension = _extension; while (current_extension) { p_list->push_back(PropertyInfo(Variant::NIL, current_extension->class_name, PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); ClassDB::get_property_list(current_extension->class_name, p_list, true, this); @@ -491,7 +487,7 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons if (_extension && _extension->get_property_list) { uint32_t pcount; - const GDNativePropertyInfo *pinfo = _extension->get_property_list(_extension_instance, &pcount); + const GDExtensionPropertyInfo *pinfo = _extension->get_property_list(_extension_instance, &pcount); for (uint32_t i = 0; i < pcount; i++) { p_list->push_back(PropertyInfo(pinfo[i])); } @@ -507,7 +503,6 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons } if (script_instance && !p_reversed) { - p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); script_instance->get_property_list(p_list); } @@ -521,7 +516,61 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons } } -void Object::_validate_property(PropertyInfo &property) const { +void Object::validate_property(PropertyInfo &p_property) const { + _validate_propertyv(p_property); +} + +bool Object::property_can_revert(const StringName &p_name) const { + if (script_instance) { + if (script_instance->property_can_revert(p_name)) { + return true; + } + } + +// C style pointer casts should never trigger a compiler warning because the risk is assumed by the user, so GCC should keep quiet about it. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-qualifiers" +#endif + if (_extension && _extension->property_can_revert) { + if (_extension->property_can_revert(_extension_instance, (const GDExtensionStringNamePtr)&p_name)) { + return true; + } + } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + return _property_can_revertv(p_name); +} + +Variant Object::property_get_revert(const StringName &p_name) const { + Variant ret; + + if (script_instance) { + if (script_instance->property_get_revert(p_name, ret)) { + return ret; + } + } + +// C style pointer casts should never trigger a compiler warning because the risk is assumed by the user, so GCC should keep quiet about it. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-qualifiers" +#endif + if (_extension && _extension->property_get_revert) { + if (_extension->property_get_revert(_extension_instance, (const GDExtensionStringNamePtr)&p_name, (GDExtensionVariantPtr)&ret)) { + return ret; + } + } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + if (_property_get_revertv(p_name, ret)) { + return ret; + } + return Variant(); } void Object::get_method_list(List<MethodInfo> *p_list) const { @@ -759,10 +808,11 @@ String Object::to_string() { } if (_extension && _extension->to_string) { String ret; - _extension->to_string(_extension_instance, &ret); + GDExtensionBool is_valid; + _extension->to_string(_extension_instance, &is_valid, &ret); return ret; } - return "[" + get_class() + ":" + itos(get_instance_id()) + "]"; + return "<" + get_class() + "#" + itos(get_instance_id()) + ">"; } void Object::set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance) { @@ -864,16 +914,16 @@ void Object::remove_meta(const StringName &p_name) { set_meta(p_name, Variant()); } -Array Object::_get_property_list_bind() const { +TypedArray<Dictionary> Object::_get_property_list_bind() const { List<PropertyInfo> lpi; get_property_list(&lpi); return convert_property_list(&lpi); } -Array Object::_get_method_list_bind() const { +TypedArray<Dictionary> Object::_get_method_list_bind() const { List<MethodInfo> ml; get_method_list(&ml); - Array ret; + TypedArray<Dictionary> ret; for (List<MethodInfo>::Element *E = ml.front(); E; E = E->next()) { Dictionary d = E->get(); @@ -973,8 +1023,6 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int OBJ_DEBUG_LOCK - Vector<const Variant *> bind_mem; - Error err = OK; for (int i = 0; i < ssize; i++) { @@ -989,28 +1037,13 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int const Variant **args = p_args; int argc = p_argcount; - if (c.binds.size()) { - //handle binds - bind_mem.resize(p_argcount + c.binds.size()); - - for (int j = 0; j < p_argcount; j++) { - bind_mem.write[j] = p_args[j]; - } - for (int j = 0; j < c.binds.size(); j++) { - bind_mem.write[p_argcount + j] = &c.binds[j]; - } - - args = (const Variant **)bind_mem.ptr(); - argc = bind_mem.size(); - } - if (c.flags & CONNECT_DEFERRED) { MessageQueue::get_singleton()->push_callablep(c.callable, args, argc, true); } else { Callable::CallError ce; _emitting = true; Variant ret; - c.callable.call(args, argc, ret, ce); + c.callable.callp(args, argc, ret, ce); _emitting = false; if (ce.error != Callable::CallError::CALL_OK) { @@ -1028,7 +1061,7 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int } } - bool disconnect = c.flags & CONNECT_ONESHOT; + bool disconnect = c.flags & CONNECT_ONE_SHOT; #ifdef TOOLS_ENABLED if (disconnect && (c.flags & CONNECT_PERSIST) && Engine::get_singleton()->is_editor_hint()) { //this signal was connected from the editor, and is being edited. just don't disconnect for now @@ -1078,11 +1111,11 @@ void Object::_add_user_signal(const String &p_name, const Array &p_args) { add_user_signal(mi); } -Array Object::_get_signal_list() const { +TypedArray<Dictionary> Object::_get_signal_list() const { List<MethodInfo> signal_list; get_signal_list(&signal_list); - Array ret; + TypedArray<Dictionary> ret; for (const MethodInfo &E : signal_list) { ret.push_back(Dictionary(E)); } @@ -1090,11 +1123,11 @@ Array Object::_get_signal_list() const { return ret; } -Array Object::_get_signal_connection_list(const StringName &p_signal) const { +TypedArray<Dictionary> Object::_get_signal_connection_list(const StringName &p_signal) const { List<Connection> conns; get_all_signal_connections(&conns); - Array ret; + TypedArray<Dictionary> ret; for (const Connection &c : conns) { if (c.signal.get_name() == p_signal) { @@ -1105,8 +1138,8 @@ Array Object::_get_signal_connection_list(const StringName &p_signal) const { return ret; } -Array Object::_get_incoming_connections() const { - Array ret; +TypedArray<Dictionary> Object::_get_incoming_connections() const { + TypedArray<Dictionary> ret; int connections_amount = connections.size(); for (int idx_conn = 0; idx_conn < connections_amount; idx_conn++) { ret.push_back(connections[idx_conn]); @@ -1196,7 +1229,7 @@ void Object::get_signals_connected_to_this(List<Connection> *p_connections) cons } } -Error Object::connect(const StringName &p_signal, const Callable &p_callable, const Vector<Variant> &p_binds, uint32_t p_flags) { +Error Object::connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags) { ERR_FAIL_COND_V_MSG(p_callable.is_null(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is null."); Object *target_object = p_callable.get_object(); @@ -1244,7 +1277,6 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, co conn.callable = target; conn.signal = ::Signal(this, p_signal); conn.flags = p_flags; - conn.binds = p_binds; slot.conn = conn; slot.cE = target_object->connections.push_back(conn); if (p_flags & CONNECT_REFERENCE_COUNTED) { @@ -1437,10 +1469,12 @@ void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("is_class", "class"), &Object::is_class); ClassDB::bind_method(D_METHOD("set", "property", "value"), &Object::_set_bind); ClassDB::bind_method(D_METHOD("get", "property"), &Object::_get_bind); - ClassDB::bind_method(D_METHOD("set_indexed", "property", "value"), &Object::_set_indexed_bind); - ClassDB::bind_method(D_METHOD("get_indexed", "property"), &Object::_get_indexed_bind); + ClassDB::bind_method(D_METHOD("set_indexed", "property_path", "value"), &Object::_set_indexed_bind); + ClassDB::bind_method(D_METHOD("get_indexed", "property_path"), &Object::_get_indexed_bind); ClassDB::bind_method(D_METHOD("get_property_list"), &Object::_get_property_list_bind); ClassDB::bind_method(D_METHOD("get_method_list"), &Object::_get_method_list_bind); + ClassDB::bind_method(D_METHOD("property_can_revert", "property"), &Object::property_can_revert); + ClassDB::bind_method(D_METHOD("property_get_revert", "property"), &Object::property_get_revert); ClassDB::bind_method(D_METHOD("notification", "what", "reversed"), &Object::notification, DEFVAL(false)); ClassDB::bind_method(D_METHOD("to_string"), &Object::to_string); ClassDB::bind_method(D_METHOD("get_instance_id"), &Object::get_instance_id); @@ -1492,7 +1526,7 @@ void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("get_signal_connection_list", "signal"), &Object::_get_signal_connection_list); ClassDB::bind_method(D_METHOD("get_incoming_connections"), &Object::_get_incoming_connections); - ClassDB::bind_method(D_METHOD("connect", "signal", "callable", "binds", "flags"), &Object::connect, DEFVAL(Array()), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("connect", "signal", "callable", "flags"), &Object::connect, DEFVAL(0)); ClassDB::bind_method(D_METHOD("disconnect", "signal", "callable"), &Object::disconnect); ClassDB::bind_method(D_METHOD("is_connected", "signal", "callable"), &Object::is_connected); @@ -1524,10 +1558,17 @@ void Object::_bind_methods() { BIND_OBJ_CORE_METHOD(miget); MethodInfo plget("_get_property_list"); - plget.return_val.type = Variant::ARRAY; + plget.return_val.hint = PROPERTY_HINT_ARRAY_TYPE; + plget.return_val.hint_string = "Dictionary"; BIND_OBJ_CORE_METHOD(plget); + BIND_OBJ_CORE_METHOD(MethodInfo(Variant::BOOL, "_property_can_revert", PropertyInfo(Variant::STRING_NAME, "property"))); + MethodInfo mipgr("_property_get_revert", PropertyInfo(Variant::STRING_NAME, "property")); + mipgr.return_val.name = "Variant"; + mipgr.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + BIND_OBJ_CORE_METHOD(mipgr); + #endif BIND_OBJ_CORE_METHOD(MethodInfo("_init")); BIND_OBJ_CORE_METHOD(MethodInfo(Variant::STRING, "_to_string")); @@ -1537,7 +1578,7 @@ void Object::_bind_methods() { BIND_ENUM_CONSTANT(CONNECT_DEFERRED); BIND_ENUM_CONSTANT(CONNECT_PERSIST); - BIND_ENUM_CONSTANT(CONNECT_ONESHOT); + BIND_ENUM_CONSTANT(CONNECT_ONE_SHOT); BIND_ENUM_CONSTANT(CONNECT_REFERENCE_COUNTED); } @@ -1553,25 +1594,6 @@ bool Object::is_blocking_signals() const { return _block_signals; } -void Object::get_translatable_strings(List<String> *p_strings) const { - List<PropertyInfo> plist; - get_property_list(&plist); - - for (const PropertyInfo &E : plist) { - if (!(E.usage & PROPERTY_USAGE_INTERNATIONALIZED)) { - continue; - } - - String text = get(E.name); - - if (text.is_empty()) { - continue; - } - - p_strings->push_back(text); - } -} - Variant::Type Object::get_static_property_type(const StringName &p_property, bool *r_valid) const { bool valid; Variant::Type t = ClassDB::get_property_type(get_class_name(), p_property, &valid); @@ -1660,7 +1682,7 @@ uint32_t Object::get_edited_version() const { } #endif -void Object::set_instance_binding(void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks) { +void Object::set_instance_binding(void *p_token, void *p_binding, const GDExtensionInstanceBindingCallbacks *p_callbacks) { // This is only meant to be used on creation by the binder. ERR_FAIL_COND(_instance_bindings != nullptr); _instance_bindings = (InstanceBinding *)memalloc(sizeof(InstanceBinding)); @@ -1671,7 +1693,7 @@ void Object::set_instance_binding(void *p_token, void *p_binding, const GDNative _instance_binding_count = 1; } -void *Object::get_instance_binding(void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks) { +void *Object::get_instance_binding(void *p_token, const GDExtensionInstanceBindingCallbacks *p_callbacks) { void *binding = nullptr; _instance_binding_mutex.lock(); for (uint32_t i = 0; i < _instance_binding_count; i++) { @@ -1818,6 +1840,46 @@ void ObjectDB::debug_objects(DebugFunc p_func) { } void Object::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + if (p_idx == 0) { + if (p_function == "connect" || p_function == "is_connected" || p_function == "disconnect" || p_function == "emit_signal" || p_function == "has_signal") { + List<MethodInfo> signals; + get_signal_list(&signals); + for (const MethodInfo &E : signals) { + r_options->push_back(E.name.quote()); + } + } else if (p_function == "call" || p_function == "call_deferred" || p_function == "callv" || p_function == "has_method") { + List<MethodInfo> methods; + get_method_list(&methods); + for (const MethodInfo &E : methods) { + if (E.name.begins_with("_") && !(E.flags & METHOD_FLAG_VIRTUAL)) { + continue; + } + r_options->push_back(E.name.quote()); + } + } else if (p_function == "set" || p_function == "set_deferred" || p_function == "get") { + List<PropertyInfo> properties; + get_property_list(&properties); + for (const PropertyInfo &E : properties) { + if (E.usage & PROPERTY_USAGE_DEFAULT && !(E.usage & PROPERTY_USAGE_INTERNAL)) { + r_options->push_back(E.name.quote()); + } + } + } else if (p_function == "set_meta" || p_function == "get_meta" || p_function == "has_meta" || p_function == "remove_meta") { + for (const KeyValue<StringName, Variant> &K : metadata) { + r_options->push_back(String(K.key).quote()); + } + } + } else if (p_idx == 2) { + if (p_function == "connect") { + // Ideally, the constants should be inferred by the parameter. + // But a parameter's PropertyInfo does not store the enum they come from, so this will do for now. + List<StringName> constants; + ClassDB::get_enum_constants("Object", "ConnectFlags", &constants); + for (const StringName &E : constants) { + r_options->push_back(String(E)); + } + } + } } SpinLock ObjectDB::spin_lock; diff --git a/core/object/object.h b/core/object/object.h index 87d042dd7e..f78c7c34fd 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -1,37 +1,37 @@ -/*************************************************************************/ -/* object.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* object.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef OBJECT_H #define OBJECT_H -#include "core/extension/gdnative_interface.h" +#include "core/extension/gdextension_interface.h" #include "core/object/message_queue.h" #include "core/object/object_id.h" #include "core/os/rw_lock.h" @@ -45,15 +45,16 @@ #include "core/variant/callable_bind.h" #include "core/variant/variant.h" +template <typename T> +class TypedArray; + enum PropertyHint { PROPERTY_HINT_NONE, ///< no hint provided. - PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_lesser][,no_slider][,radians][,degrees][,exp][,suffix:<keyword>] range. + PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_less][,hide_slider][,radians][,degrees][,exp][,suffix:<keyword>] range. PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc" PROPERTY_HINT_ENUM_SUGGESTION, ///< hint_text= "val1,val2,val3,etc" - PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout") - PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer) + PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "positive_only" to exclude in-out and out-in. (ie: "attenuation,positive_only") PROPERTY_HINT_LINK, - PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer) PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags) PROPERTY_HINT_LAYERS_2D_RENDER, PROPERTY_HINT_LAYERS_2D_PHYSICS, @@ -70,70 +71,56 @@ enum PropertyHint { PROPERTY_HINT_EXPRESSION, ///< used for string properties that can contain multiple lines PROPERTY_HINT_PLACEHOLDER_TEXT, ///< used to set a placeholder text for string properties PROPERTY_HINT_COLOR_NO_ALPHA, ///< used for ignoring alpha component when editing a color - PROPERTY_HINT_IMAGE_COMPRESS_LOSSY, - PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS, PROPERTY_HINT_OBJECT_ID, PROPERTY_HINT_TYPE_STRING, ///< a type string, the hint is the base type to choose PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE, ///< so something else can provide this (used in scripts) - PROPERTY_HINT_METHOD_OF_VARIANT_TYPE, ///< a method of a type - PROPERTY_HINT_METHOD_OF_BASE_TYPE, ///< a method of a base type - PROPERTY_HINT_METHOD_OF_INSTANCE, ///< a method of an instance - PROPERTY_HINT_METHOD_OF_SCRIPT, ///< a method of a script & base - PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE, ///< a property of a type - PROPERTY_HINT_PROPERTY_OF_BASE_TYPE, ///< a property of a base type - PROPERTY_HINT_PROPERTY_OF_INSTANCE, ///< a property of an instance - PROPERTY_HINT_PROPERTY_OF_SCRIPT, ///< a property of a script & base PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send PROPERTY_HINT_NODE_PATH_VALID_TYPES, PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog PROPERTY_HINT_GLOBAL_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog PROPERTY_HINT_INT_IS_OBJECTID, - PROPERTY_HINT_ARRAY_TYPE, PROPERTY_HINT_INT_IS_POINTER, + PROPERTY_HINT_ARRAY_TYPE, PROPERTY_HINT_LOCALE_ID, PROPERTY_HINT_LOCALIZABLE_STRING, PROPERTY_HINT_NODE_TYPE, ///< a node object type + PROPERTY_HINT_HIDE_QUATERNION_EDIT, /// Only Node3D::transform should hide the quaternion editor. + PROPERTY_HINT_PASSWORD, PROPERTY_HINT_MAX, - // When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit }; enum PropertyUsageFlags { PROPERTY_USAGE_NONE = 0, - PROPERTY_USAGE_STORAGE = 1, - PROPERTY_USAGE_EDITOR = 2, - PROPERTY_USAGE_NETWORK = 4, - PROPERTY_USAGE_EDITOR_HELPER = 8, - PROPERTY_USAGE_CHECKABLE = 16, //used for editing global variables - PROPERTY_USAGE_CHECKED = 32, //used for editing global variables - PROPERTY_USAGE_INTERNATIONALIZED = 64, //hint for internationalized strings - PROPERTY_USAGE_GROUP = 128, //used for grouping props in the editor - PROPERTY_USAGE_CATEGORY = 256, - PROPERTY_USAGE_SUBGROUP = 512, - PROPERTY_USAGE_CLASS_IS_BITFIELD = 1024, - PROPERTY_USAGE_NO_INSTANCE_STATE = 2048, - PROPERTY_USAGE_RESTART_IF_CHANGED = 4096, - PROPERTY_USAGE_SCRIPT_VARIABLE = 8192, - PROPERTY_USAGE_STORE_IF_NULL = 16384, - PROPERTY_USAGE_ANIMATE_AS_TRIGGER = 32768, - PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED = 65536, - PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE = 1 << 17, - PROPERTY_USAGE_CLASS_IS_ENUM = 1 << 18, - PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 19, - PROPERTY_USAGE_INTERNAL = 1 << 20, - PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 21, // If the object is duplicated also this property will be duplicated - PROPERTY_USAGE_HIGH_END_GFX = 1 << 22, - PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 23, - PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 24, - PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 25, // Used in inspector to increment property when keyed in animation player - PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 26, // when loading, the resource for this property can be set at the end of loading - PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 27, // For Object properties, instantiate them when creating in editor. - PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 28, //for project or editor settings, show when basic settings are selected - PROPERTY_USAGE_READ_ONLY = 1 << 29, // Mark a property as read-only in the inspector. - PROPERTY_USAGE_ARRAY = 1 << 30, // Used in the inspector to group properties as elements of an array. - - PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK, - PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED, - PROPERTY_USAGE_NO_EDITOR = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_NETWORK, + PROPERTY_USAGE_STORAGE = 1 << 1, + PROPERTY_USAGE_EDITOR = 1 << 2, + PROPERTY_USAGE_INTERNAL = 1 << 3, + PROPERTY_USAGE_CHECKABLE = 1 << 4, // Used for editing global variables. + PROPERTY_USAGE_CHECKED = 1 << 5, // Used for editing global variables. + PROPERTY_USAGE_GROUP = 1 << 6, // Used for grouping props in the editor. + PROPERTY_USAGE_CATEGORY = 1 << 7, + PROPERTY_USAGE_SUBGROUP = 1 << 8, + PROPERTY_USAGE_CLASS_IS_BITFIELD = 1 << 9, + PROPERTY_USAGE_NO_INSTANCE_STATE = 1 << 10, + PROPERTY_USAGE_RESTART_IF_CHANGED = 1 << 11, + PROPERTY_USAGE_SCRIPT_VARIABLE = 1 << 12, + PROPERTY_USAGE_STORE_IF_NULL = 1 << 13, + PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED = 1 << 14, + PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE = 1 << 15, + PROPERTY_USAGE_CLASS_IS_ENUM = 1 << 16, + PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 17, + PROPERTY_USAGE_ARRAY = 1 << 18, // Used in the inspector to group properties as elements of an array. + PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 19, // If the object is duplicated also this property will be duplicated. + PROPERTY_USAGE_HIGH_END_GFX = 1 << 20, + PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 21, + PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 22, + PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 23, // Used in inspector to increment property when keyed in animation player. + PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 24, // when loading, the resource for this property can be set at the end of loading. + PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 25, // For Object properties, instantiate them when creating in editor. + PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 26, //for project or editor settings, show when basic settings are selected. + PROPERTY_USAGE_READ_ONLY = 1 << 27, // Mark a property as read-only in the inspector. + + PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR, + PROPERTY_USAGE_NO_EDITOR = PROPERTY_USAGE_STORAGE, }; #define ADD_SIGNAL(m_signal) ::ClassDB::add_signal(get_class_static(), m_signal) @@ -150,6 +137,10 @@ enum PropertyUsageFlags { #define ADD_ARRAY_COUNT_WITH_USAGE_FLAGS(m_label, m_count_property, m_count_property_setter, m_count_property_getter, m_prefix, m_property_usage_flags) ClassDB::add_property_array_count(get_class_static(), m_label, m_count_property, _scs_create(m_count_property_setter), _scs_create(m_count_property_getter), m_prefix, m_property_usage_flags) #define ADD_ARRAY(m_array_path, m_prefix) ClassDB::add_property_array(get_class_static(), m_array_path, m_prefix) +// Helper macro to use with PROPERTY_HINT_ARRAY_TYPE for arrays of specific resources: +// PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font") +#define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type) + struct PropertyInfo { Variant::Type type = Variant::NIL; String name; @@ -158,9 +149,7 @@ struct PropertyInfo { String hint_string; uint32_t usage = PROPERTY_USAGE_DEFAULT; -#ifdef TOOLS_ENABLED - Vector<String> linked_properties; -#endif + // If you are thinking about adding another member to this class, ask the maintainer (Juan) first. _FORCE_INLINE_ PropertyInfo added_usage(uint32_t p_fl) const { PropertyInfo pi = *this; @@ -191,12 +180,12 @@ struct PropertyInfo { type(Variant::OBJECT), class_name(p_class_name) {} - explicit PropertyInfo(const GDNativePropertyInfo &pinfo) : + explicit PropertyInfo(const GDExtensionPropertyInfo &pinfo) : type((Variant::Type)pinfo.type), - name(pinfo.name), - class_name(pinfo.class_name), // can be null + name(*reinterpret_cast<StringName *>(pinfo.name)), + class_name(*reinterpret_cast<StringName *>(pinfo.class_name)), hint((PropertyHint)pinfo.hint), - hint_string(pinfo.hint_string), // can be null + hint_string(*reinterpret_cast<String *>(pinfo.hint_string)), usage(pinfo.usage) {} bool operator==(const PropertyInfo &p_info) const { @@ -213,19 +202,16 @@ struct PropertyInfo { } }; -Array convert_property_list(const List<PropertyInfo> *p_list); +TypedArray<Dictionary> convert_property_list(const List<PropertyInfo> *p_list); enum MethodFlags { METHOD_FLAG_NORMAL = 1, METHOD_FLAG_EDITOR = 2, - METHOD_FLAG_NOSCRIPT = 4, - METHOD_FLAG_CONST = 8, - METHOD_FLAG_REVERSE = 16, // used for events - METHOD_FLAG_VIRTUAL = 32, - METHOD_FLAG_FROM_SCRIPT = 64, - METHOD_FLAG_VARARG = 128, - METHOD_FLAG_STATIC = 256, - METHOD_FLAG_OBJECT_CORE = 512, + METHOD_FLAG_CONST = 4, + METHOD_FLAG_VIRTUAL = 8, + METHOD_FLAG_VARARG = 16, + METHOD_FLAG_STATIC = 32, + METHOD_FLAG_OBJECT_CORE = 64, METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL, }; @@ -246,6 +232,20 @@ struct MethodInfo { MethodInfo() {} + explicit MethodInfo(const GDExtensionMethodInfo &pinfo) : + name(*reinterpret_cast<StringName *>(pinfo.name)), + return_val(PropertyInfo(pinfo.return_value)), + flags(pinfo.flags), + id(pinfo.id) { + for (uint32_t j = 0; j < pinfo.argument_count; j++) { + arguments.push_back(PropertyInfo(pinfo.arguments[j])); + } + const Variant *def_values = (const Variant *)pinfo.default_arguments; + for (uint32_t j = 0; j < pinfo.default_argument_count; j++) { + default_arguments.push_back(def_values[j]); + } + } + void _push_params(const PropertyInfo &p_param) { arguments.push_back(p_param); } @@ -290,27 +290,31 @@ struct MethodInfo { } }; -// API used to extend in GDNative and other C compatible compiled languages. +// API used to extend in GDExtension and other C compatible compiled languages. class MethodBind; -struct ObjectNativeExtension { - ObjectNativeExtension *parent = nullptr; - List<ObjectNativeExtension *> children; +struct ObjectGDExtension { + ObjectGDExtension *parent = nullptr; + List<ObjectGDExtension *> children; StringName parent_class_name; StringName class_name; bool editor_class = false; - GDNativeExtensionClassSet set; - GDNativeExtensionClassGet get; - GDNativeExtensionClassGetPropertyList get_property_list; - GDNativeExtensionClassFreePropertyList free_property_list; - GDNativeExtensionClassNotification notification; - GDNativeExtensionClassToString to_string; - GDNativeExtensionClassReference reference; - GDNativeExtensionClassReference unreference; - GDNativeExtensionClassGetRID get_rid; + bool is_virtual = false; + bool is_abstract = false; + GDExtensionClassSet set; + GDExtensionClassGet get; + GDExtensionClassGetPropertyList get_property_list; + GDExtensionClassFreePropertyList free_property_list; + GDExtensionClassPropertyCanRevert property_can_revert; + GDExtensionClassPropertyGetRevert property_get_revert; + GDExtensionClassNotification notification; + GDExtensionClassToString to_string; + GDExtensionClassReference reference; + GDExtensionClassReference unreference; + GDExtensionClassGetRID get_rid; _FORCE_INLINE_ bool is_class(const String &p_class) const { - const ObjectNativeExtension *e = this; + const ObjectGDExtension *e = this; while (e) { if (p_class == e->class_name.operator String()) { return true; @@ -321,9 +325,9 @@ struct ObjectNativeExtension { } void *class_userdata = nullptr; - GDNativeExtensionClassCreateInstance create_instance; - GDNativeExtensionClassFreeInstance free_instance; - GDNativeExtensionClassGetVirtual get_virtual; + GDExtensionClassCreateInstance create_instance; + GDExtensionClassFreeInstance free_instance; + GDExtensionClassGetVirtual get_virtual; }; #define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call<false>(__VA_ARGS__) @@ -365,6 +369,7 @@ private: friend class ::ClassDB; \ \ public: \ + static constexpr bool _class_is_enabled = !bool(GD_IS_DEFINED(ClassDB_Disable_##m_class)) && m_inherits::_class_is_enabled; \ virtual String get_class() const override { \ if (_get_extension()) { \ return _get_extension()->class_name.operator String(); \ @@ -477,6 +482,37 @@ protected: m_inherits::_get_property_listv(p_list, p_reversed); \ } \ } \ + _FORCE_INLINE_ void (Object::*_get_validate_property() const)(PropertyInfo & p_property) const { \ + return (void(Object::*)(PropertyInfo &) const) & m_class::_validate_property; \ + } \ + virtual void _validate_propertyv(PropertyInfo &p_property) const override { \ + m_inherits::_validate_propertyv(p_property); \ + if (m_class::_get_validate_property() != m_inherits::_get_validate_property()) { \ + _validate_property(p_property); \ + } \ + } \ + _FORCE_INLINE_ bool (Object::*_get_property_can_revert() const)(const StringName &p_name) const { \ + return (bool(Object::*)(const StringName &) const) & m_class::_property_can_revert; \ + } \ + virtual bool _property_can_revertv(const StringName &p_name) const override { \ + if (m_class::_get_property_can_revert() != m_inherits::_get_property_can_revert()) { \ + if (_property_can_revert(p_name)) { \ + return true; \ + } \ + } \ + return m_inherits::_property_can_revertv(p_name); \ + } \ + _FORCE_INLINE_ bool (Object::*_get_property_get_revert() const)(const StringName &p_name, Variant &) const { \ + return (bool(Object::*)(const StringName &, Variant &) const) & m_class::_property_get_revert; \ + } \ + virtual bool _property_get_revertv(const StringName &p_name, Variant &r_ret) const override { \ + if (m_class::_get_property_get_revert() != m_inherits::_get_property_get_revert()) { \ + if (_property_get_revert(p_name, r_ret)) { \ + return true; \ + } \ + } \ + return m_inherits::_property_get_revertv(p_name, r_ret); \ + } \ _FORCE_INLINE_ void (Object::*_get_notification() const)(int) { \ return (void(Object::*)(int)) & m_class::_notification; \ } \ @@ -507,8 +543,9 @@ public: enum ConnectFlags { CONNECT_DEFERRED = 1, CONNECT_PERSIST = 2, // hint for scene to save this connection - CONNECT_ONESHOT = 4, + CONNECT_ONE_SHOT = 4, CONNECT_REFERENCE_COUNTED = 8, + CONNECT_INHERITED = 16, // Used in editor builds. }; struct Connection { @@ -516,7 +553,6 @@ public: Callable callable; uint32_t flags = 0; - Vector<Variant> binds; bool operator<(const Connection &p_conn) const; operator Variant() const; @@ -532,7 +568,7 @@ private: friend bool predelete_handler(Object *); friend void postinitialize_handler(Object *); - ObjectNativeExtension *_extension = nullptr; + ObjectGDExtension *_extension = nullptr; GDExtensionClassInstancePtr _extension_instance = nullptr; struct SignalData { @@ -573,9 +609,9 @@ private: void _add_user_signal(const String &p_name, const Array &p_args = Array()); bool _has_user_signal(const StringName &p_name) const; Error _emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error); - Array _get_signal_list() const; - Array _get_signal_connection_list(const StringName &p_signal) const; - Array _get_incoming_connections() const; + TypedArray<Dictionary> _get_signal_list() const; + TypedArray<Dictionary> _get_signal_connection_list(const StringName &p_signal) const; + TypedArray<Dictionary> _get_incoming_connections() const; void _set_bind(const StringName &p_set, const Variant &p_value); Variant _get_bind(const StringName &p_name) const; void _set_indexed_bind(const NodePath &p_name, const Variant &p_value); @@ -590,8 +626,8 @@ private: struct InstanceBinding { void *binding = nullptr; void *token = nullptr; - GDNativeInstanceBindingFreeCallback free_callback = nullptr; - GDNativeInstanceBindingReferenceCallback reference_callback = nullptr; + GDExtensionInstanceBindingFreeCallback free_callback = nullptr; + GDExtensionInstanceBindingReferenceCallback reference_callback = nullptr; }; InstanceBinding *_instance_bindings = nullptr; uint32_t _instance_binding_count = 0; @@ -615,19 +651,25 @@ protected: return can_die; } - friend class NativeExtensionMethodBind; - _ALWAYS_INLINE_ const ObjectNativeExtension *_get_extension() const { return _extension; } + friend class GDExtensionMethodBind; + _ALWAYS_INLINE_ const ObjectGDExtension *_get_extension() const { return _extension; } _ALWAYS_INLINE_ GDExtensionClassInstancePtr _get_extension_instance() const { return _extension_instance; } virtual void _initialize_classv() { initialize_class(); } virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; }; virtual bool _getv(const StringName &p_name, Variant &r_property) const { return false; }; virtual void _get_property_listv(List<PropertyInfo> *p_list, bool p_reversed) const {}; + virtual void _validate_propertyv(PropertyInfo &p_property) const {}; + virtual bool _property_can_revertv(const StringName &p_name) const { return false; }; + virtual bool _property_get_revertv(const StringName &p_name, Variant &r_property) const { return false; }; virtual void _notificationv(int p_notification, bool p_reversed) {} static void _bind_methods(); bool _set(const StringName &p_name, const Variant &p_property) { return false; }; bool _get(const StringName &p_name, Variant &r_property) const { return false; }; void _get_property_list(List<PropertyInfo> *p_list) const {}; + void _validate_property(PropertyInfo &p_property) const {}; + bool _property_can_revert(const StringName &p_name) const { return false; }; + bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return false; }; void _notification(int p_notification) {} _FORCE_INLINE_ static void (*_get_bind_methods())() { @@ -642,6 +684,15 @@ protected: _FORCE_INLINE_ void (Object::*_get_get_property_list() const)(List<PropertyInfo> *p_list) const { return &Object::_get_property_list; } + _FORCE_INLINE_ void (Object::*_get_validate_property() const)(PropertyInfo &p_property) const { + return &Object::_validate_property; + } + _FORCE_INLINE_ bool (Object::*_get_property_can_revert() const)(const StringName &p_name) const { + return &Object::_property_can_revert; + } + _FORCE_INLINE_ bool (Object::*_get_property_get_revert() const)(const StringName &p_name, Variant &) const { + return &Object::_property_get_revert; + } _FORCE_INLINE_ void (Object::*_get_notification() const)(int) { return &Object::_notification; } @@ -659,13 +710,12 @@ protected: } Vector<StringName> _get_meta_list_bind() const; - Array _get_property_list_bind() const; - Array _get_method_list_bind() const; + TypedArray<Dictionary> _get_property_list_bind() const; + TypedArray<Dictionary> _get_method_list_bind() const; void _clear_internal_resource_paths(const Variant &p_var); friend class ClassDB; - virtual void _validate_property(PropertyInfo &property) const; void _disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force = false); @@ -674,6 +724,8 @@ public: // Should be protected, but bug in clang++. _FORCE_INLINE_ static void register_custom_data_to_otdb() {} public: + static constexpr bool _class_is_enabled = true; + void notify_property_list_changed(); static void *get_class_ptr_static() { @@ -688,34 +740,12 @@ public: template <class T> static T *cast_to(Object *p_object) { -#ifndef NO_SAFE_CAST return dynamic_cast<T *>(p_object); -#else - if (!p_object) { - return nullptr; - } - if (p_object->is_class_ptr(T::get_class_ptr_static())) { - return static_cast<T *>(p_object); - } else { - return nullptr; - } -#endif } template <class T> static const T *cast_to(const Object *p_object) { -#ifndef NO_SAFE_CAST return dynamic_cast<const T *>(p_object); -#else - if (!p_object) { - return nullptr; - } - if (p_object->is_class_ptr(T::get_class_ptr_static())) { - return static_cast<const T *>(p_object); - } else { - return nullptr; - } -#endif } enum { @@ -764,6 +794,9 @@ public: Variant get_indexed(const Vector<StringName> &p_names, bool *r_valid = nullptr) const; void get_property_list(List<PropertyInfo> *p_list, bool p_reversed = false) const; + void validate_property(PropertyInfo &p_property) const; + bool property_can_revert(const StringName &p_name) const; + Variant property_get_revert(const StringName &p_name) const; bool has_method(const StringName &p_method) const; void get_method_list(List<MethodInfo> *p_list) const; @@ -833,7 +866,7 @@ public: int get_persistent_signal_connection_count() const; void get_signals_connected_to_this(List<Connection> *p_connections) const; - Error connect(const StringName &p_signal, const Callable &p_callable, const Vector<Variant> &p_binds = Vector<Variant>(), uint32_t p_flags = 0); + Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0); void disconnect(const StringName &p_signal, const Callable &p_callable); bool is_connected(const StringName &p_signal, const Callable &p_callable) const; @@ -850,8 +883,6 @@ public: Variant::Type get_static_property_type(const StringName &p_property, bool *r_valid = nullptr) const; Variant::Type get_static_property_type_indexed(const Vector<StringName> &p_path, bool *r_valid = nullptr) const; - virtual void get_translatable_strings(List<String> *p_strings) const; - virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const; // Translate message (internationalization). @@ -873,9 +904,9 @@ public: #endif // Used by script languages to store binding data. - void *get_instance_binding(void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks); + void *get_instance_binding(void *p_token, const GDExtensionInstanceBindingCallbacks *p_callbacks); // Used on creation by binding only. - void set_instance_binding(void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks); + void set_instance_binding(void *p_token, void *p_binding, const GDExtensionInstanceBindingCallbacks *p_callbacks); bool has_instance_binding(void *p_token); void clear_internal_resource_paths(); diff --git a/core/object/object_id.h b/core/object/object_id.h index a36997630f..7b677cb05b 100644 --- a/core/object/object_id.h +++ b/core/object/object_id.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* object_id.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* object_id.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef OBJECT_ID_H #define OBJECT_ID_H diff --git a/core/object/ref_counted.cpp b/core/object/ref_counted.cpp index 726e2c012c..1a43c1e18c 100644 --- a/core/object/ref_counted.cpp +++ b/core/object/ref_counted.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* ref_counted.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* ref_counted.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "ref_counted.h" @@ -48,9 +48,10 @@ void RefCounted::_bind_methods() { ClassDB::bind_method(D_METHOD("init_ref"), &RefCounted::init_ref); ClassDB::bind_method(D_METHOD("reference"), &RefCounted::reference); ClassDB::bind_method(D_METHOD("unreference"), &RefCounted::unreference); + ClassDB::bind_method(D_METHOD("get_reference_count"), &RefCounted::get_reference_count); } -int RefCounted::reference_get_count() const { +int RefCounted::get_reference_count() const { return refcount.get(); } @@ -85,7 +86,8 @@ bool RefCounted::unreference() { _get_extension()->unreference(_get_extension_instance()); } - die = die && _instance_binding_reference(false); + bool binding_ret = _instance_binding_reference(false); + die = die && binding_ret; } return die; diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h index bd06a84bd8..ed48f4065c 100644 --- a/core/object/ref_counted.h +++ b/core/object/ref_counted.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* ref_counted.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* ref_counted.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef REF_COUNTED_H #define REF_COUNTED_H @@ -47,7 +47,7 @@ public: bool init_ref(); bool reference(); // returns false if refcount is at zero and didn't get increased bool unreference(); - int reference_get_count() const; + int get_reference_count() const; RefCounted(); ~RefCounted() {} @@ -253,12 +253,14 @@ public: template <class T> struct PtrToArg<Ref<T>> { _FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) { + // p_ptr points to a RefCounted object return Ref<T>(const_cast<T *>(reinterpret_cast<const T *>(p_ptr))); } typedef Ref<T> EncodeT; _FORCE_INLINE_ static void encode(Ref<T> p_val, const void *p_ptr) { + // p_ptr points to an EncodeT object which is a Ref<T> object. *(const_cast<Ref<RefCounted> *>(reinterpret_cast<const Ref<RefCounted> *>(p_ptr))) = p_val; } }; @@ -268,6 +270,7 @@ struct PtrToArg<const Ref<T> &> { typedef Ref<T> EncodeT; _FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) { + // p_ptr points to a RefCounted object return Ref<T>((T *)p_ptr); } }; diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 4623d0e525..66ef418e42 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* script_language.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* script_language.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "script_language.h" @@ -34,6 +34,7 @@ #include "core/core_string_names.h" #include "core/debugger/engine_debugger.h" #include "core/debugger/script_debugger.h" +#include "core/variant/typed_array.h" #include <stdint.h> @@ -61,8 +62,8 @@ Variant Script::_get_property_default_value(const StringName &p_property) { return ret; } -Array Script::_get_script_property_list() { - Array ret; +TypedArray<Dictionary> Script::_get_script_property_list() { + TypedArray<Dictionary> ret; List<PropertyInfo> list; get_script_property_list(&list); for (const PropertyInfo &E : list) { @@ -71,8 +72,8 @@ Array Script::_get_script_property_list() { return ret; } -Array Script::_get_script_method_list() { - Array ret; +TypedArray<Dictionary> Script::_get_script_method_list() { + TypedArray<Dictionary> ret; List<MethodInfo> list; get_script_method_list(&list); for (const MethodInfo &E : list) { @@ -81,8 +82,8 @@ Array Script::_get_script_method_list() { return ret; } -Array Script::_get_script_signal_list() { - Array ret; +TypedArray<Dictionary> Script::_get_script_signal_list() { + TypedArray<Dictionary> ret; List<MethodInfo> list; get_script_signal_list(&list); for (const MethodInfo &E : list) { @@ -101,6 +102,31 @@ Dictionary Script::_get_script_constant_map() { return ret; } +#ifdef TOOLS_ENABLED + +PropertyInfo Script::get_class_category() const { + String path = get_path(); + String scr_name; + + if (is_built_in()) { + if (get_name().is_empty()) { + scr_name = TTR("Built-in script"); + } else { + scr_name = vformat("%s (%s)", get_name(), TTR("Built-in")); + } + } else { + if (get_name().is_empty()) { + scr_name = path.get_file(); + } else { + scr_name = get_name(); + } + } + + return PropertyInfo(Variant::NIL, scr_name, PROPERTY_HINT_NONE, path, PROPERTY_USAGE_CATEGORY); +} + +#endif // TOOLS_ENABLED + void Script::_bind_methods() { ClassDB::bind_method(D_METHOD("can_instantiate"), &Script::can_instantiate); //ClassDB::bind_method(D_METHOD("instance_create","base_object"),&Script::instance_create); @@ -140,6 +166,7 @@ ScriptLanguage *ScriptServer::get_language(int p_idx) { } void ScriptServer::register_language(ScriptLanguage *p_language) { + ERR_FAIL_NULL(p_language); ERR_FAIL_COND(_language_count >= MAX_LANGUAGES); _languages[_language_count++] = p_language; } @@ -157,10 +184,11 @@ void ScriptServer::unregister_language(const ScriptLanguage *p_language) { } void ScriptServer::init_languages() { - { //load global classes + { // Load global classes. global_classes_clear(); +#ifndef DISABLE_DEPRECATED if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) { - Array script_classes = ProjectSettings::get_singleton()->get("_global_script_classes"); + Array script_classes = GLOBAL_GET("_global_script_classes"); for (int i = 0; i < script_classes.size(); i++) { Dictionary c = script_classes[i]; @@ -169,6 +197,17 @@ void ScriptServer::init_languages() { } add_global_class(c["class"], c["base"], c["language"], c["path"]); } + ProjectSettings::get_singleton()->clear("_global_script_classes"); + } +#endif + + Array script_classes = ProjectSettings::get_singleton()->get_global_class_list(); + for (int i = 0; i < script_classes.size(); i++) { + Dictionary c = script_classes[i]; + if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) { + continue; + } + add_global_class(c["class"], c["base"], c["language"], c["path"]); } } @@ -224,6 +263,15 @@ void ScriptServer::remove_global_class(const StringName &p_class) { global_classes.erase(p_class); } +void ScriptServer::remove_global_class_by_path(const String &p_path) { + for (const KeyValue<StringName, GlobalScriptClass> &kv : global_classes) { + if (kv.value.path == p_path) { + global_classes.erase(kv.key); + return; + } + } +} + bool ScriptServer::is_global_class(const StringName &p_class) { return global_classes.has(p_class); } @@ -264,6 +312,17 @@ void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) { } void ScriptServer::save_global_classes() { + Dictionary class_icons; + + Array script_classes = ProjectSettings::get_singleton()->get_global_class_list(); + for (int i = 0; i < script_classes.size(); i++) { + Dictionary d = script_classes[i]; + if (!d.has("name") || !d.has("icon")) { + continue; + } + class_icons[d["name"]] = d["icon"]; + } + List<StringName> gc; get_global_class_list(&gc); Array gcarr; @@ -273,25 +332,10 @@ void ScriptServer::save_global_classes() { d["language"] = global_classes[E].language; d["path"] = global_classes[E].path; d["base"] = global_classes[E].base; + d["icon"] = class_icons.get(E, ""); gcarr.push_back(d); } - - Array old; - if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) { - old = ProjectSettings::get_singleton()->get("_global_script_classes"); - } - if ((!old.is_empty() || gcarr.is_empty()) && gcarr.hash() == old.hash()) { - return; - } - - if (gcarr.is_empty()) { - if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) { - ProjectSettings::get_singleton()->clear("_global_script_classes"); - } - } else { - ProjectSettings::get_singleton()->set("_global_script_classes", gcarr); - } - ProjectSettings::get_singleton()->save(); + ProjectSettings::get_singleton()->store_global_class_list(gcarr); } //////////////////// @@ -344,11 +388,14 @@ void ScriptLanguage::get_core_type_words(List<String> *p_core_type_words) const p_core_type_words->push_back("Vector3"); p_core_type_words->push_back("Vector3i"); p_core_type_words->push_back("Transform2D"); + p_core_type_words->push_back("Vector4"); + p_core_type_words->push_back("Vector4i"); p_core_type_words->push_back("Plane"); p_core_type_words->push_back("Quaternion"); p_core_type_words->push_back("AABB"); p_core_type_words->push_back("Basis"); p_core_type_words->push_back("Transform3D"); + p_core_type_words->push_back("Projection"); p_core_type_words->push_back("Color"); p_core_type_words->push_back("StringName"); p_core_type_words->push_back("NodePath"); @@ -379,7 +426,9 @@ bool PlaceHolderScriptInstance::set(const StringName &p_name, const Variant &p_v if (values.has(p_name)) { Variant defval; if (script->get_property_default_value(p_name, defval)) { - if (defval == p_value) { + // The evaluate function ensures that a NIL variant is equal to e.g. an empty Resource. + // Simply doing defval == p_value does not do this. + if (Variant::evaluate(Variant::OP_EQUAL, defval, p_value)) { values.erase(p_name); return true; } @@ -389,7 +438,7 @@ bool PlaceHolderScriptInstance::set(const StringName &p_name, const Variant &p_v } else { Variant defval; if (script->get_property_default_value(p_name, defval)) { - if (defval != p_value) { + if (Variant::evaluate(Variant::OP_NOT_EQUAL, defval, p_value)) { values[p_name] = p_value; } return true; diff --git a/core/object/script_language.h b/core/object/script_language.h index 686ab5b8d3..02d1880dc2 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -1,43 +1,44 @@ -/*************************************************************************/ -/* script_language.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* script_language.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef SCRIPT_LANGUAGE_H #define SCRIPT_LANGUAGE_H #include "core/doc_data.h" #include "core/io/resource.h" -#include "core/multiplayer/multiplayer.h" #include "core/templates/pair.h" #include "core/templates/rb_map.h" class ScriptLanguage; +template <typename T> +class TypedArray; typedef void (*ScriptEditRequestFunction)(const String &p_path); @@ -79,6 +80,7 @@ public: static void global_classes_clear(); static void add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path); static void remove_global_class(const StringName &p_class); + static void remove_global_class_by_path(const String &p_path); static bool is_global_class(const StringName &p_class); static StringName get_global_class_language(const StringName &p_class); static String get_global_class_path(const String &p_class); @@ -109,9 +111,9 @@ protected: virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {} Variant _get_property_default_value(const StringName &p_property); - Array _get_script_property_list(); - Array _get_script_method_list(); - Array _get_script_signal_list(); + TypedArray<Dictionary> _get_script_property_list(); + TypedArray<Dictionary> _get_script_method_list(); + TypedArray<Dictionary> _get_script_signal_list(); Dictionary _get_script_constant_map(); public: @@ -133,6 +135,7 @@ public: #ifdef TOOLS_ENABLED virtual Vector<DocData::ClassDoc> get_documentation() const = 0; + virtual PropertyInfo get_class_category() const; #endif // TOOLS_ENABLED virtual bool has_method(const StringName &p_method) const = 0; @@ -159,7 +162,7 @@ public: virtual bool is_placeholder_fallback_enabled() const { return false; } - virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const = 0; + virtual const Variant get_rpc_config() const = 0; Script() {} }; @@ -171,6 +174,9 @@ public: virtual void get_property_list(List<PropertyInfo> *p_properties) const = 0; virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const = 0; + virtual bool property_can_revert(const StringName &p_name) const = 0; + virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const = 0; + virtual Object *get_owner() { return nullptr; } virtual void get_property_state(List<Pair<StringName, Variant>> &state); @@ -213,7 +219,7 @@ public: virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid); virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid); - virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const { return get_script()->get_rpc_methods(); } + virtual const Variant get_rpc_config() const { return get_script()->get_rpc_config(); } virtual ScriptLanguage *get_language() = 0; virtual ~ScriptInstance(); @@ -447,6 +453,9 @@ public: virtual void get_property_list(List<PropertyInfo> *p_properties) const override; virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const override; + virtual bool property_can_revert(const StringName &p_name) const override { return false; }; + virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const override { return false; }; + virtual void get_method_list(List<MethodInfo> *p_list) const override; virtual bool has_method(const StringName &p_method) const override; @@ -469,7 +478,7 @@ public: virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr) override; virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid = nullptr) override; - virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override { return Vector<Multiplayer::RPCConfig>(); } + virtual const Variant get_rpc_config() const override { return Variant(); } PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner); ~PlaceHolderScriptInstance(); diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp index ab8dd6d1ee..19c6fc3383 100644 --- a/core/object/script_language_extension.cpp +++ b/core/object/script_language_extension.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* script_language_extension.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* script_language_extension.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "script_language_extension.h" @@ -62,6 +62,7 @@ void ScriptExtension::_bind_methods() { GDVIRTUAL_BIND(_has_script_signal, "signal"); GDVIRTUAL_BIND(_get_script_signal_list); + GDVIRTUAL_BIND(_has_property_default_value, "property"); GDVIRTUAL_BIND(_get_property_default_value, "property"); GDVIRTUAL_BIND(_update_exports); @@ -74,7 +75,7 @@ void ScriptExtension::_bind_methods() { GDVIRTUAL_BIND(_get_members); GDVIRTUAL_BIND(_is_placeholder_fallback_enabled); - GDVIRTUAL_BIND(_get_rpc_methods); + GDVIRTUAL_BIND(_get_rpc_config); } void ScriptLanguageExtension::_bind_methods() { diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h index 2c53139ec2..912f2218c4 100644 --- a/core/object/script_language_extension.h +++ b/core/object/script_language_extension.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* script_language_extension.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* script_language_extension.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef SCRIPT_LANGUAGE_EXTENSION_H #define SCRIPT_LANGUAGE_EXTENSION_H @@ -43,7 +43,7 @@ class ScriptExtension : public Script { protected: EXBIND0R(bool, editor_can_reload_from_file) - GDVIRTUAL1(_placeholder_erased, GDNativePtr<void>) + GDVIRTUAL1(_placeholder_erased, GDExtensionPtr<void>) virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override { GDVIRTUAL_CALL(_placeholder_erased, p_placeholder); } @@ -56,15 +56,15 @@ public: EXBIND1RC(bool, inherits_script, const Ref<Script> &) EXBIND0RC(StringName, get_instance_base_type) - GDVIRTUAL1RC(GDNativePtr<void>, _instance_create, Object *) + GDVIRTUAL1RC(GDExtensionPtr<void>, _instance_create, Object *) virtual ScriptInstance *instance_create(Object *p_this) override { - GDNativePtr<void> ret = nullptr; + GDExtensionPtr<void> ret = nullptr; GDVIRTUAL_REQUIRED_CALL(_instance_create, p_this, ret); return reinterpret_cast<ScriptInstance *>(ret.operator void *()); } - GDVIRTUAL1RC(GDNativePtr<void>, _placeholder_instance_create, Object *) + GDVIRTUAL1RC(GDExtensionPtr<void>, _placeholder_instance_create, Object *) PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this) override { - GDNativePtr<void> ret = nullptr; + GDExtensionPtr<void> ret = nullptr; GDVIRTUAL_REQUIRED_CALL(_placeholder_instance_create, p_this, ret); return reinterpret_cast<PlaceHolderScriptInstance *>(ret.operator void *()); } @@ -82,9 +82,10 @@ public: GDVIRTUAL_REQUIRED_CALL(_get_documentation, doc); Vector<DocData::ClassDoc> class_doc; -#ifndef _MSC_VER -#warning missing conversion from documentation to ClassDoc -#endif + for (int i = 0; i < doc.size(); i++) { + class_doc.append(DocData::ClassDoc::from_dict(doc[i])); + } + return class_doc; } #endif // TOOLS_ENABLED @@ -124,7 +125,8 @@ public: } Variant ret; GDVIRTUAL_REQUIRED_CALL(_get_property_default_value, p_property, ret); - return ret; + r_value = ret; + return true; } EXBIND0(update_exports) @@ -173,28 +175,12 @@ public: EXBIND0RC(bool, is_placeholder_fallback_enabled) - GDVIRTUAL0RC(TypedArray<Dictionary>, _get_rpc_methods) + GDVIRTUAL0RC(Variant, _get_rpc_config) - virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override { - TypedArray<Dictionary> ret; - GDVIRTUAL_REQUIRED_CALL(_get_rpc_methods, ret); - Vector<Multiplayer::RPCConfig> rpcret; - for (int i = 0; i < ret.size(); i++) { - Dictionary d = ret[i]; - Multiplayer::RPCConfig rpc; - ERR_CONTINUE(!d.has("name")); - rpc.name = d["name"]; - ERR_CONTINUE(!d.has("rpc_mode")); - rpc.rpc_mode = Multiplayer::RPCMode(int(d["rpc_mode"])); - ERR_CONTINUE(!d.has("call_local")); - rpc.call_local = d["call_local"]; - ERR_CONTINUE(!d.has("transfer_mode")); - rpc.transfer_mode = Multiplayer::TransferMode(int(d["transfer_mode"])); - ERR_CONTINUE(!d.has("channel")); - rpc.channel = d["channel"]; - rpcret.push_back(rpc); - } - return rpcret; + virtual const Variant get_rpc_config() const override { + Variant ret; + GDVIRTUAL_REQUIRED_CALL(_get_rpc_config, ret); + return ret; } ScriptExtension() {} @@ -497,10 +483,10 @@ public: } } } - GDVIRTUAL1R(GDNativePtr<void>, _debug_get_stack_level_instance, int) + GDVIRTUAL1R(GDExtensionPtr<void>, _debug_get_stack_level_instance, int) virtual ScriptInstance *debug_get_stack_level_instance(int p_level) override { - GDNativePtr<void> ret = nullptr; + GDExtensionPtr<void> ret = nullptr; GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_instance, p_level, ret); return reinterpret_cast<ScriptInstance *>(ret.operator void *()); } @@ -593,7 +579,7 @@ public: EXBIND0(profiling_start) EXBIND0(profiling_stop) - GDVIRTUAL2R(int, _profiling_get_accumulated_data, GDNativePtr<ScriptLanguageExtensionProfilingInfo>, int) + GDVIRTUAL2R(int, _profiling_get_accumulated_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int) virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override { int ret = 0; @@ -601,7 +587,7 @@ public: return ret; } - GDVIRTUAL2R(int, _profiling_get_frame_data, GDNativePtr<ScriptLanguageExtensionProfilingInfo>, int) + GDVIRTUAL2R(int, _profiling_get_frame_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int) virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override { int ret = 0; @@ -609,15 +595,15 @@ public: return ret; } - GDVIRTUAL1R(GDNativePtr<void>, _alloc_instance_binding_data, Object *) + GDVIRTUAL1R(GDExtensionPtr<void>, _alloc_instance_binding_data, Object *) virtual void *alloc_instance_binding_data(Object *p_object) override { - GDNativePtr<void> ret = nullptr; + GDExtensionPtr<void> ret = nullptr; GDVIRTUAL_REQUIRED_CALL(_alloc_instance_binding_data, p_object, ret); return ret.operator void *(); } - GDVIRTUAL1(_free_instance_binding_data, GDNativePtr<void>) + GDVIRTUAL1(_free_instance_binding_data, GDExtensionPtr<void>) virtual void free_instance_binding_data(void *p_data) override { GDVIRTUAL_REQUIRED_CALL(_free_instance_binding_data, p_data); @@ -654,8 +640,8 @@ VARIANT_ENUM_CAST(ScriptLanguageExtension::CodeCompletionLocation) class ScriptInstanceExtension : public ScriptInstance { public: - const GDNativeExtensionScriptInstanceInfo *native_info; - GDNativeExtensionScriptInstanceDataPtr instance = nullptr; + const GDExtensionScriptInstanceInfo *native_info; + GDExtensionScriptInstanceDataPtr instance = nullptr; // There should not be warnings on explicit casts. #if defined(__GNUC__) && !defined(__clang__) @@ -665,20 +651,28 @@ public: virtual bool set(const StringName &p_name, const Variant &p_value) override { if (native_info->set_func) { - return native_info->set_func(instance, (const GDNativeStringNamePtr)&p_name, (const GDNativeVariantPtr)&p_value); + return native_info->set_func(instance, (GDExtensionConstStringNamePtr)&p_name, (GDExtensionConstVariantPtr)&p_value); } return false; } virtual bool get(const StringName &p_name, Variant &r_ret) const override { if (native_info->get_func) { - return native_info->get_func(instance, (const GDNativeStringNamePtr)&p_name, (GDNativeVariantPtr)&r_ret); + return native_info->get_func(instance, (GDExtensionConstStringNamePtr)&p_name, (GDExtensionVariantPtr)&r_ret); } return false; } virtual void get_property_list(List<PropertyInfo> *p_list) const override { if (native_info->get_property_list_func) { uint32_t pcount; - const GDNativePropertyInfo *pinfo = native_info->get_property_list_func(instance, &pcount); + const GDExtensionPropertyInfo *pinfo = native_info->get_property_list_func(instance, &pcount); + +#ifdef TOOLS_ENABLED + Ref<Script> script = get_script(); + if (script->is_valid() && pcount > 0) { + p_list->push_back(script->get_class_category()); + } +#endif // TOOLS_ENABLED + for (uint32_t i = 0; i < pcount; i++) { p_list->push_back(PropertyInfo(pinfo[i])); } @@ -689,24 +683,36 @@ public: } virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const override { if (native_info->get_property_type_func) { - GDNativeBool is_valid = 0; - GDNativeVariantType type = native_info->get_property_type_func(instance, (const GDNativeStringNamePtr)&p_name, &is_valid); + GDExtensionBool is_valid = 0; + GDExtensionVariantType type = native_info->get_property_type_func(instance, (GDExtensionConstStringNamePtr)&p_name, &is_valid); if (r_is_valid) { *r_is_valid = is_valid != 0; } - return Variant::Type(type); } return Variant::NIL; } + virtual bool property_can_revert(const StringName &p_name) const override { + if (native_info->property_can_revert_func) { + return native_info->property_can_revert_func(instance, (GDExtensionConstStringNamePtr)&p_name); + } + return false; + } + virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const override { + if (native_info->property_get_revert_func) { + return native_info->property_get_revert_func(instance, (GDExtensionConstStringNamePtr)&p_name, (GDExtensionVariantPtr)&r_ret); + } + return false; + } + virtual Object *get_owner() override { if (native_info->get_owner_func) { return (Object *)native_info->get_owner_func(instance); } return nullptr; } - static void _add_property_with_state(const GDNativeStringNamePtr p_name, const GDNativeVariantPtr p_value, void *p_userdata) { + static void _add_property_with_state(GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value, void *p_userdata) { List<Pair<StringName, Variant>> *state = (List<Pair<StringName, Variant>> *)p_userdata; state->push_back(Pair<StringName, Variant>(*(const StringName *)p_name, *(const Variant *)p_value)); } @@ -719,21 +725,9 @@ public: virtual void get_method_list(List<MethodInfo> *p_list) const override { if (native_info->get_method_list_func) { uint32_t mcount; - const GDNativeMethodInfo *minfo = native_info->get_method_list_func(instance, &mcount); + const GDExtensionMethodInfo *minfo = native_info->get_method_list_func(instance, &mcount); for (uint32_t i = 0; i < mcount; i++) { - MethodInfo m; - m.name = minfo[i].name; - m.flags = minfo[i].flags; - m.id = minfo[i].id; - m.return_val = PropertyInfo(minfo[i].return_value); - for (uint32_t j = 0; j < minfo[i].argument_count; j++) { - m.arguments.push_back(PropertyInfo(minfo[i].arguments[j])); - } - const Variant *def_values = (const Variant *)minfo[i].default_arguments; - for (uint32_t j = 0; j < minfo[i].default_argument_count; j++) { - m.default_arguments.push_back(def_values[j]); - } - p_list->push_back(m); + p_list->push_back(MethodInfo(minfo[i])); } if (native_info->free_method_list_func) { native_info->free_method_list_func(instance, minfo); @@ -742,7 +736,7 @@ public: } virtual bool has_method(const StringName &p_method) const override { if (native_info->has_method_func) { - return native_info->has_method_func(instance, (GDNativeStringNamePtr)&p_method); + return native_info->has_method_func(instance, (GDExtensionStringNamePtr)&p_method); } return false; } @@ -750,8 +744,8 @@ public: virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override { Variant ret; if (native_info->call_func) { - GDNativeCallError ce; - native_info->call_func(instance, (const GDNativeStringNamePtr)&p_method, (const GDNativeVariantPtr *)p_args, p_argcount, (GDNativeVariantPtr)&ret, &ce); + GDExtensionCallError ce; + native_info->call_func(instance, (GDExtensionConstStringNamePtr)&p_method, (GDExtensionConstVariantPtr *)p_args, p_argcount, (GDExtensionVariantPtr)&ret, &ce); r_error.error = Callable::CallError::Error(ce.error); r_error.argument = ce.argument; r_error.expected = ce.expected; @@ -766,8 +760,9 @@ public: } virtual String to_string(bool *r_valid) override { if (native_info->to_string_func) { - GDNativeBool valid; - String ret = native_info->to_string_func(instance, &valid); + GDExtensionBool valid; + String ret; + native_info->to_string_func(instance, &valid, reinterpret_cast<GDExtensionStringPtr>(&ret)); if (r_valid) { *r_valid = valid != 0; } @@ -790,7 +785,7 @@ public: virtual Ref<Script> get_script() const override { if (native_info->get_script_func) { - GDNativeObjectPtr script = native_info->get_script_func(instance); + GDExtensionObjectPtr script = native_info->get_script_func(instance); return Ref<Script>(reinterpret_cast<Script *>(script)); } return Ref<Script>(); @@ -805,7 +800,7 @@ public: virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid) override { if (native_info->set_fallback_func) { - bool ret = native_info->set_fallback_func(instance, (const GDNativeStringNamePtr)&p_name, (const GDNativeVariantPtr)&p_value); + bool ret = native_info->set_fallback_func(instance, (GDExtensionConstStringNamePtr)&p_name, (GDExtensionConstVariantPtr)&p_value); if (r_valid) { *r_valid = ret; } @@ -814,7 +809,7 @@ public: virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid) override { Variant ret; if (native_info->get_fallback_func) { - bool valid = native_info->get_fallback_func(instance, (const GDNativeStringNamePtr)&p_name, (GDNativeVariantPtr)&ret); + bool valid = native_info->get_fallback_func(instance, (GDExtensionConstStringNamePtr)&p_name, (GDExtensionVariantPtr)&ret); if (r_valid) { *r_valid = valid; } @@ -824,7 +819,7 @@ public: virtual ScriptLanguage *get_language() override { if (native_info->get_language_func) { - GDNativeExtensionScriptLanguagePtr lang = native_info->get_language_func(instance); + GDExtensionScriptLanguagePtr lang = native_info->get_language_func(instance); return reinterpret_cast<ScriptLanguage *>(lang); } return nullptr; diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index d3c48853f1..e84933eb69 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* undo_redo.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* undo_redo.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "undo_redo.h" @@ -126,27 +126,28 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { force_keep_in_merge_ends = false; } -void UndoRedo::add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) { - ERR_FAIL_COND(p_object == nullptr); +void UndoRedo::add_do_method(const Callable &p_callable) { + ERR_FAIL_COND(p_callable.is_null()); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); + + Object *object = p_callable.get_object(); + ERR_FAIL_NULL(object); + Operation do_op; - do_op.object = p_object->get_instance_id(); - if (Object::cast_to<RefCounted>(p_object)) { - do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object)); + do_op.callable = p_callable; + do_op.object = p_callable.get_object_id(); + if (Object::cast_to<RefCounted>(object)) { + do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object)); } - do_op.type = Operation::TYPE_METHOD; - do_op.name = p_method; + do_op.name = p_callable.get_method(); - for (int i = 0; i < p_argcount; i++) { - do_op.args.push_back(*p_args[i]); - } actions.write[current_action + 1].do_ops.push_back(do_op); } -void UndoRedo::add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) { - ERR_FAIL_COND(p_object == nullptr); +void UndoRedo::add_undo_method(const Callable &p_callable) { + ERR_FAIL_COND(p_callable.is_null()); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); @@ -155,19 +156,19 @@ void UndoRedo::add_undo_methodp(Object *p_object, const StringName &p_method, co return; } + Object *object = p_callable.get_object(); + ERR_FAIL_NULL(object); + Operation undo_op; - undo_op.object = p_object->get_instance_id(); - if (Object::cast_to<RefCounted>(p_object)) { - undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object)); + undo_op.callable = p_callable; + undo_op.object = p_callable.get_object_id(); + if (Object::cast_to<RefCounted>(object)) { + undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object)); } - undo_op.type = Operation::TYPE_METHOD; undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; - undo_op.name = p_method; + undo_op.name = p_callable.get_method(); - for (int i = 0; i < p_argcount; i++) { - undo_op.args.push_back(*p_args[i]); - } actions.write[current_action + 1].undo_ops.push_back(undo_op); } @@ -183,7 +184,7 @@ void UndoRedo::add_do_property(Object *p_object, const StringName &p_property, c do_op.type = Operation::TYPE_PROPERTY; do_op.name = p_property; - do_op.args.push_back(p_value); + do_op.value = p_value; actions.write[current_action + 1].do_ops.push_back(do_op); } @@ -206,7 +207,7 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property, undo_op.type = Operation::TYPE_PROPERTY; undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; undo_op.name = p_property; - undo_op.args.push_back(p_value); + undo_op.value = p_value; actions.write[current_action + 1].undo_ops.push_back(undo_op); } @@ -312,33 +313,42 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) { switch (op.type) { case Operation::TYPE_METHOD: { - int argc = op.args.size(); - Vector<const Variant *> argptrs; - argptrs.resize(argc); - - for (int i = 0; i < argc; i++) { - argptrs.write[i] = &op.args[i]; - } - Callable::CallError ce; - obj->callp(op.name, (const Variant **)argptrs.ptr(), argc, ce); + Variant ret; + op.callable.callp(nullptr, 0, ret, ce); if (ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, (const Variant **)argptrs.ptr(), argc, ce)); + ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, nullptr, 0, ce)); } #ifdef TOOLS_ENABLED Resource *res = Object::cast_to<Resource>(obj); if (res) { res->set_edited(true); } - #endif if (method_callback) { - method_callback(method_callback_ud, obj, op.name, (const Variant **)argptrs.ptr(), argc); + Vector<Variant> binds; + if (op.callable.is_custom()) { + CallableCustomBind *ccb = dynamic_cast<CallableCustomBind *>(op.callable.get_custom()); + if (ccb) { + binds = ccb->get_binds(); + } + } + + if (binds.is_empty()) { + method_callback(method_callback_ud, obj, op.name, nullptr, 0); + } else { + const Variant **args = (const Variant **)alloca(sizeof(const Variant **) * binds.size()); + for (int i = 0; i < binds.size(); i++) { + args[i] = (const Variant *)&binds[i]; + } + + method_callback(method_callback_ud, obj, op.name, args, binds.size()); + } } } break; case Operation::TYPE_PROPERTY: { - obj->set(op.name, op.args[0]); + obj->set(op.name, op.value); #ifdef TOOLS_ENABLED Resource *res = Object::cast_to<Resource>(obj); if (res) { @@ -346,7 +356,7 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) { } #endif if (property_callback) { - property_callback(prop_callback_ud, obj, op.name, op.args[0]); + property_callback(prop_callback_ud, obj, op.name, op.value); } } break; case Operation::TYPE_REFERENCE: { @@ -413,6 +423,10 @@ String UndoRedo::get_current_action_name() const { return actions[current_action].name; } +int UndoRedo::get_action_level() const { + return action_level; +} + bool UndoRedo::has_undo() const { return current_action >= 0; } @@ -444,87 +458,13 @@ UndoRedo::~UndoRedo() { clear_history(); } -void UndoRedo::_add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 0; - return; - } - - if (p_args[0]->get_type() != Variant::OBJECT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - return; - } - - if (p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; - r_error.expected = Variant::STRING_NAME; - return; - } - - r_error.error = Callable::CallError::CALL_OK; - - Object *object = *p_args[0]; - StringName method = *p_args[1]; - - add_do_methodp(object, method, p_args + 2, p_argcount - 2); -} - -void UndoRedo::_add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 0; - return; - } - - if (p_args[0]->get_type() != Variant::OBJECT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - return; - } - - if (p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; - r_error.expected = Variant::STRING_NAME; - return; - } - - r_error.error = Callable::CallError::CALL_OK; - - Object *object = *p_args[0]; - StringName method = *p_args[1]; - - add_undo_methodp(object, method, p_args + 2, p_argcount - 2); -} - void UndoRedo::_bind_methods() { ClassDB::bind_method(D_METHOD("create_action", "name", "merge_mode"), &UndoRedo::create_action, DEFVAL(MERGE_DISABLE)); ClassDB::bind_method(D_METHOD("commit_action", "execute"), &UndoRedo::commit_action, DEFVAL(true)); ClassDB::bind_method(D_METHOD("is_committing_action"), &UndoRedo::is_committing_action); - { - MethodInfo mi; - mi.name = "add_do_method"; - mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object")); - mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method")); - - ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "add_do_method", &UndoRedo::_add_do_method, mi, varray(), false); - } - - { - MethodInfo mi; - mi.name = "add_undo_method"; - mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object")); - mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method")); - - ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "add_undo_method", &UndoRedo::_add_undo_method, mi, varray(), false); - } - + ClassDB::bind_method(D_METHOD("add_do_method", "callable"), &UndoRedo::add_do_method); + ClassDB::bind_method(D_METHOD("add_undo_method", "callable"), &UndoRedo::add_undo_method); ClassDB::bind_method(D_METHOD("add_do_property", "object", "property", "value"), &UndoRedo::add_do_property); ClassDB::bind_method(D_METHOD("add_undo_property", "object", "property", "value"), &UndoRedo::add_undo_property); ClassDB::bind_method(D_METHOD("add_do_reference", "object"), &UndoRedo::add_do_reference); diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h index 63cf3e5cbe..5dc9f2966c 100644 --- a/core/object/undo_redo.h +++ b/core/object/undo_redo.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* undo_redo.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* undo_redo.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef UNDO_REDO_H #define UNDO_REDO_H @@ -46,8 +46,6 @@ public: }; typedef void (*CommitNotifyCallback)(void *p_ud, const String &p_name); - void _add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error); - void _add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error); typedef void (*MethodNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount); typedef void (*PropertyNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value); @@ -58,14 +56,14 @@ private: TYPE_METHOD, TYPE_PROPERTY, TYPE_REFERENCE - }; + } type; - Type type; bool force_keep_in_merge_ends; Ref<RefCounted> ref; ObjectID object; StringName name; - Vector<Variant> args; + Callable callable; + Variant value; void delete_reference(); }; @@ -106,30 +104,8 @@ protected: public: void create_action(const String &p_name = "", MergeMode p_mode = MERGE_DISABLE); - void add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount); - void add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount); - - template <typename... VarArgs> - void add_do_method(Object *p_object, const StringName &p_method, VarArgs... p_args) { - Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. - const Variant *argptrs[sizeof...(p_args) + 1]; - for (uint32_t i = 0; i < sizeof...(p_args); i++) { - argptrs[i] = &args[i]; - } - - add_do_methodp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); - } - template <typename... VarArgs> - void add_undo_method(Object *p_object, const StringName &p_method, VarArgs... p_args) { - Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. - const Variant *argptrs[sizeof...(p_args) + 1]; - for (uint32_t i = 0; i < sizeof...(p_args); i++) { - argptrs[i] = &args[i]; - } - - add_undo_methodp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); - } - + void add_do_method(const Callable &p_callable); + void add_undo_method(const Callable &p_callable); 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); @@ -144,6 +120,7 @@ public: bool redo(); bool undo(); String get_current_action_name() const; + int get_action_level() const; int get_history_count(); int get_current_action(); diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp new file mode 100644 index 0000000000..fc907b2301 --- /dev/null +++ b/core/object/worker_thread_pool.cpp @@ -0,0 +1,476 @@ +/**************************************************************************/ +/* worker_thread_pool.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "worker_thread_pool.h" + +#include "core/os/os.h" + +void WorkerThreadPool::Task::free_template_userdata() { + ERR_FAIL_COND(!template_userdata); + ERR_FAIL_COND(native_func_userdata == nullptr); + BaseTemplateUserdata *btu = (BaseTemplateUserdata *)native_func_userdata; + memdelete(btu); +} + +WorkerThreadPool *WorkerThreadPool::singleton = nullptr; + +void WorkerThreadPool::_process_task_queue() { + task_mutex.lock(); + Task *task = task_queue.first()->self(); + task_queue.remove(task_queue.first()); + task_mutex.unlock(); + _process_task(task); +} + +void WorkerThreadPool::_process_task(Task *p_task) { + bool low_priority = p_task->low_priority; + + if (p_task->group) { + // Handling a group + bool do_post = false; + Callable::CallError ce; + Variant ret; + Variant arg; + Variant *argptr = &arg; + + while (true) { + uint32_t work_index = p_task->group->index.postincrement(); + + if (work_index >= p_task->group->max) { + break; + } + if (p_task->native_group_func) { + p_task->native_group_func(p_task->native_func_userdata, work_index); + } else if (p_task->template_userdata) { + p_task->template_userdata->callback_indexed(work_index); + } else { + arg = work_index; + p_task->callable.callp((const Variant **)&argptr, 1, ret, ce); + } + + // This is the only way to ensure posting is done when all tasks are really complete. + uint32_t completed_amount = p_task->group->completed_index.increment(); + + if (completed_amount == p_task->group->max) { + do_post = true; + } + } + + if (do_post && p_task->template_userdata) { + memdelete(p_task->template_userdata); // This is no longer needed at this point, so get rid of it. + } + + if (low_priority && use_native_low_priority_threads) { + p_task->completed = true; + p_task->done_semaphore.post(); + if (do_post) { + p_task->group->completed.set_to(true); + } + } else { + if (do_post) { + p_task->group->done_semaphore.post(); + p_task->group->completed.set_to(true); + } + uint32_t max_users = p_task->group->tasks_used + 1; // Add 1 because the thread waiting for it is also user. Read before to avoid another thread freeing task after increment. + uint32_t finished_users = p_task->group->finished.increment(); + + if (finished_users == max_users) { + // Get rid of the group, because nobody else is using it. + task_mutex.lock(); + group_allocator.free(p_task->group); + task_mutex.unlock(); + } + + // For groups, tasks get rid of themselves. + + task_mutex.lock(); + task_allocator.free(p_task); + task_mutex.unlock(); + } + } else { + if (p_task->native_func) { + p_task->native_func(p_task->native_func_userdata); + } else if (p_task->template_userdata) { + p_task->template_userdata->callback(); + memdelete(p_task->template_userdata); + } else { + Callable::CallError ce; + Variant ret; + p_task->callable.callp(nullptr, 0, ret, ce); + } + + p_task->completed = true; + p_task->done_semaphore.post(); + } + + if (!use_native_low_priority_threads && low_priority) { + // A low prioriry task was freed, so see if we can move a pending one to the high priority queue. + bool post = false; + task_mutex.lock(); + if (low_priority_task_queue.first()) { + Task *low_prio_task = low_priority_task_queue.first()->self(); + low_priority_task_queue.remove(low_priority_task_queue.first()); + task_queue.add_last(&low_prio_task->task_elem); + post = true; + } else { + low_priority_threads_used.decrement(); + } + task_mutex.lock(); + if (post) { + task_available_semaphore.post(); + } + } +} + +void WorkerThreadPool::_thread_function(void *p_user) { + while (true) { + singleton->task_available_semaphore.wait(); + if (singleton->exit_threads.is_set()) { + break; + } + singleton->_process_task_queue(); + } +} + +void WorkerThreadPool::_native_low_priority_thread_function(void *p_user) { + Task *task = (Task *)p_user; + singleton->_process_task(task); +} + +void WorkerThreadPool::_post_task(Task *p_task, bool p_high_priority) { + task_mutex.lock(); + p_task->low_priority = !p_high_priority; + if (!p_high_priority && use_native_low_priority_threads) { + task_mutex.unlock(); + p_task->low_priority_thread = native_thread_allocator.alloc(); + p_task->low_priority_thread->start(_native_low_priority_thread_function, p_task); // Pask task directly to thread. + + } else if (p_high_priority || low_priority_threads_used.get() < max_low_priority_threads) { + task_queue.add_last(&p_task->task_elem); + if (!p_high_priority) { + low_priority_threads_used.increment(); + } + task_mutex.unlock(); + task_available_semaphore.post(); + } else { + // Too many threads using low priority, must go to queue. + low_priority_task_queue.add_last(&p_task->task_elem); + task_mutex.unlock(); + } +} + +WorkerThreadPool::TaskID WorkerThreadPool::add_native_task(void (*p_func)(void *), void *p_userdata, bool p_high_priority, const String &p_description) { + return _add_task(Callable(), p_func, p_userdata, nullptr, p_high_priority, p_description); +} + +WorkerThreadPool::TaskID WorkerThreadPool::_add_task(const Callable &p_callable, void (*p_func)(void *), void *p_userdata, BaseTemplateUserdata *p_template_userdata, bool p_high_priority, const String &p_description) { + task_mutex.lock(); + // Get a free task + Task *task = task_allocator.alloc(); + TaskID id = last_task++; + task->callable = p_callable; + task->native_func = p_func; + task->native_func_userdata = p_userdata; + task->description = p_description; + task->template_userdata = p_template_userdata; + tasks.insert(id, task); + task_mutex.unlock(); + + _post_task(task, p_high_priority); + + return id; +} + +WorkerThreadPool::TaskID WorkerThreadPool::add_task(const Callable &p_action, bool p_high_priority, const String &p_description) { + return _add_task(p_action, nullptr, nullptr, nullptr, p_high_priority, p_description); +} + +bool WorkerThreadPool::is_task_completed(TaskID p_task_id) const { + task_mutex.lock(); + const Task *const *taskp = tasks.getptr(p_task_id); + if (!taskp) { + task_mutex.unlock(); + ERR_FAIL_V_MSG(false, "Invalid Task ID"); // Invalid task + } + + bool completed = (*taskp)->completed; + task_mutex.unlock(); + + return completed; +} + +void WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) { + task_mutex.lock(); + Task **taskp = tasks.getptr(p_task_id); + if (!taskp) { + task_mutex.unlock(); + ERR_FAIL_MSG("Invalid Task ID"); // Invalid task + } + Task *task = *taskp; + + if (task->waiting) { + String description = task->description; + task_mutex.unlock(); + if (description.is_empty()) { + ERR_FAIL_MSG("Another thread is waiting on this task: " + itos(p_task_id)); // Invalid task + } else { + ERR_FAIL_MSG("Another thread is waiting on this task: " + description + " (" + itos(p_task_id) + ")"); // Invalid task + } + } + + task->waiting = true; + + task_mutex.unlock(); + + if (use_native_low_priority_threads && task->low_priority) { + task->low_priority_thread->wait_to_finish(); + native_thread_allocator.free(task->low_priority_thread); + } else { + int *index = thread_ids.getptr(Thread::get_caller_id()); + + if (index) { + // We are an actual process thread, we must not be blocked so continue processing stuff if available. + while (true) { + if (task->done_semaphore.try_wait()) { + // If done, exit + break; + } + if (task_available_semaphore.try_wait()) { + // Solve tasks while they are around. + _process_task_queue(); + continue; + } + OS::get_singleton()->delay_usec(1); // Microsleep, this could be converted to waiting for multiple objects in supported platforms for a bit more performance. + } + } else { + task->done_semaphore.wait(); + } + } + + task_mutex.lock(); + tasks.erase(p_task_id); + task_allocator.free(task); + task_mutex.unlock(); +} + +WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) { + ERR_FAIL_COND_V(p_elements < 0, INVALID_TASK_ID); + if (p_tasks < 0) { + p_tasks = threads.size(); + } + + task_mutex.lock(); + Group *group = group_allocator.alloc(); + GroupID id = last_task++; + group->max = p_elements; + group->self = id; + + Task **tasks_posted = nullptr; + if (p_elements == 0) { + // Should really not call it with zero Elements, but at least it should work. + group->completed.set_to(true); + group->done_semaphore.post(); + group->tasks_used = 0; + p_tasks = 0; + if (p_template_userdata) { + memdelete(p_template_userdata); + } + + } else { + group->tasks_used = p_tasks; + tasks_posted = (Task **)alloca(sizeof(Task *) * p_tasks); + for (int i = 0; i < p_tasks; i++) { + Task *task = task_allocator.alloc(); + task->native_group_func = p_func; + task->native_func_userdata = p_userdata; + task->description = p_description; + task->group = group; + task->callable = p_callable; + task->template_userdata = p_template_userdata; + tasks_posted[i] = task; + // No task ID is used. + } + } + + groups[id] = group; + task_mutex.unlock(); + + if (!p_high_priority && use_native_low_priority_threads) { + group->low_priority_native_tasks.resize(p_tasks); + } + + for (int i = 0; i < p_tasks; i++) { + _post_task(tasks_posted[i], p_high_priority); + if (!p_high_priority && use_native_low_priority_threads) { + group->low_priority_native_tasks[i] = tasks_posted[i]; + } + } + + return id; +} + +WorkerThreadPool::GroupID WorkerThreadPool::add_native_group_task(void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) { + return _add_group_task(Callable(), p_func, p_userdata, nullptr, p_elements, p_tasks, p_high_priority, p_description); +} + +WorkerThreadPool::GroupID WorkerThreadPool::add_group_task(const Callable &p_action, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) { + return _add_group_task(p_action, nullptr, nullptr, nullptr, p_elements, p_tasks, p_high_priority, p_description); +} + +uint32_t WorkerThreadPool::get_group_processed_element_count(GroupID p_group) const { + task_mutex.lock(); + const Group *const *groupp = groups.getptr(p_group); + if (!groupp) { + task_mutex.unlock(); + ERR_FAIL_V_MSG(0, "Invalid Group ID"); + } + uint32_t elements = (*groupp)->completed_index.get(); + task_mutex.unlock(); + return elements; +} +bool WorkerThreadPool::is_group_task_completed(GroupID p_group) const { + task_mutex.lock(); + const Group *const *groupp = groups.getptr(p_group); + if (!groupp) { + task_mutex.unlock(); + ERR_FAIL_V_MSG(false, "Invalid Group ID"); + } + bool completed = (*groupp)->completed.is_set(); + task_mutex.unlock(); + return completed; +} + +void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) { + task_mutex.lock(); + Group **groupp = groups.getptr(p_group); + task_mutex.unlock(); + if (!groupp) { + ERR_FAIL_MSG("Invalid Group ID"); + } + Group *group = *groupp; + + if (group->low_priority_native_tasks.size() > 0) { + for (uint32_t i = 0; i < group->low_priority_native_tasks.size(); i++) { + group->low_priority_native_tasks[i]->low_priority_thread->wait_to_finish(); + native_thread_allocator.free(group->low_priority_native_tasks[i]->low_priority_thread); + task_mutex.lock(); + task_allocator.free(group->low_priority_native_tasks[i]); + task_mutex.unlock(); + } + + task_mutex.lock(); + group_allocator.free(group); + task_mutex.unlock(); + } else { + group->done_semaphore.wait(); + + uint32_t max_users = group->tasks_used + 1; // Add 1 because the thread waiting for it is also user. Read before to avoid another thread freeing task after increment. + uint32_t finished_users = group->finished.increment(); // fetch happens before inc, so increment later. + + if (finished_users == max_users) { + // All tasks using this group are gone (finished before the group), so clear the group too. + task_mutex.lock(); + group_allocator.free(group); + task_mutex.unlock(); + } + } + + task_mutex.lock(); // This mutex is needed when Physics 2D and/or 3D is selected to run on a separate thread. + groups.erase(p_group); + task_mutex.unlock(); +} + +void WorkerThreadPool::init(int p_thread_count, bool p_use_native_threads_low_priority, float p_low_priority_task_ratio) { + ERR_FAIL_COND(threads.size() > 0); + if (p_thread_count < 0) { + p_thread_count = OS::get_singleton()->get_default_thread_pool_size(); + } + + if (p_use_native_threads_low_priority) { + max_low_priority_threads = 0; + } else { + max_low_priority_threads = CLAMP(p_thread_count * p_low_priority_task_ratio, 1, p_thread_count); + } + + use_native_low_priority_threads = p_use_native_threads_low_priority; + + threads.resize(p_thread_count); + + for (uint32_t i = 0; i < threads.size(); i++) { + threads[i].index = i; + threads[i].thread.start(&WorkerThreadPool::_thread_function, &threads[i]); + thread_ids.insert(threads[i].thread.get_id(), i); + } +} + +void WorkerThreadPool::finish() { + if (threads.size() == 0) { + return; + } + + task_mutex.lock(); + SelfList<Task> *E = low_priority_task_queue.first(); + while (E) { + print_error("Task waiting was never re-claimed: " + E->self()->description); + E = E->next(); + } + task_mutex.unlock(); + + exit_threads.set_to(true); + + for (uint32_t i = 0; i < threads.size(); i++) { + task_available_semaphore.post(); + } + + for (uint32_t i = 0; i < threads.size(); i++) { + threads[i].thread.wait_to_finish(); + } + + threads.clear(); +} + +void WorkerThreadPool::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_task", "action", "high_priority", "description"), &WorkerThreadPool::add_task, DEFVAL(false), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("is_task_completed", "task_id"), &WorkerThreadPool::is_task_completed); + ClassDB::bind_method(D_METHOD("wait_for_task_completion", "task_id"), &WorkerThreadPool::wait_for_task_completion); + + ClassDB::bind_method(D_METHOD("add_group_task", "action", "elements", "tasks_needed", "high_priority", "description"), &WorkerThreadPool::add_group_task, DEFVAL(-1), DEFVAL(false), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("is_group_task_completed", "group_id"), &WorkerThreadPool::is_group_task_completed); + ClassDB::bind_method(D_METHOD("get_group_processed_element_count", "group_id"), &WorkerThreadPool::get_group_processed_element_count); + ClassDB::bind_method(D_METHOD("wait_for_group_task_completion", "group_id"), &WorkerThreadPool::wait_for_group_task_completion); +} + +WorkerThreadPool::WorkerThreadPool() { + singleton = this; +} + +WorkerThreadPool::~WorkerThreadPool() { + finish(); +} diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h new file mode 100644 index 0000000000..cf3f7f0885 --- /dev/null +++ b/core/object/worker_thread_pool.h @@ -0,0 +1,198 @@ +/**************************************************************************/ +/* worker_thread_pool.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef WORKER_THREAD_POOL_H +#define WORKER_THREAD_POOL_H + +#include "core/os/memory.h" +#include "core/os/os.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" +#include "core/templates/local_vector.h" +#include "core/templates/paged_allocator.h" +#include "core/templates/rid.h" +#include "core/templates/safe_refcount.h" + +class WorkerThreadPool : public Object { + GDCLASS(WorkerThreadPool, Object) +public: + enum { + INVALID_TASK_ID = -1 + }; + + typedef int64_t TaskID; + typedef int64_t GroupID; + +private: + struct Task; + + struct BaseTemplateUserdata { + virtual void callback() {} + virtual void callback_indexed(uint32_t p_index) {} + virtual ~BaseTemplateUserdata() {} + }; + + struct Group { + GroupID self; + SafeNumeric<uint32_t> index; + SafeNumeric<uint32_t> completed_index; + uint32_t max = 0; + Semaphore done_semaphore; + SafeFlag completed; + SafeNumeric<uint32_t> finished; + uint32_t tasks_used = 0; + TightLocalVector<Task *> low_priority_native_tasks; + }; + + struct Task { + Callable callable; + void (*native_func)(void *) = nullptr; + void (*native_group_func)(void *, uint32_t) = nullptr; + void *native_func_userdata = nullptr; + String description; + Semaphore done_semaphore; + bool completed = false; + Group *group = nullptr; + SelfList<Task> task_elem; + bool waiting = false; // Waiting for completion + bool low_priority = false; + BaseTemplateUserdata *template_userdata = nullptr; + Thread *low_priority_thread = nullptr; + + void free_template_userdata(); + Task() : + task_elem(this) {} + }; + + PagedAllocator<Task> task_allocator; + PagedAllocator<Group> group_allocator; + PagedAllocator<Thread> native_thread_allocator; + + SelfList<Task>::List low_priority_task_queue; + SelfList<Task>::List task_queue; + + Mutex task_mutex; + Semaphore task_available_semaphore; + + struct ThreadData { + uint32_t index; + Thread thread; + }; + + TightLocalVector<ThreadData> threads; + SafeFlag exit_threads; + + HashMap<Thread::ID, int> thread_ids; + HashMap<TaskID, Task *> tasks; + HashMap<GroupID, Group *> groups; + + bool use_native_low_priority_threads = false; + uint32_t max_low_priority_threads = 0; + SafeNumeric<uint32_t> low_priority_threads_used; + + uint64_t last_task = 1; + + static void _thread_function(void *p_user); + static void _native_low_priority_thread_function(void *p_user); + + void _process_task_queue(); + void _process_task(Task *task); + + void _post_task(Task *p_task, bool p_high_priority); + + static WorkerThreadPool *singleton; + + TaskID _add_task(const Callable &p_callable, void (*p_func)(void *), void *p_userdata, BaseTemplateUserdata *p_template_userdata, bool p_high_priority, const String &p_description); + GroupID _add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description); + + template <class C, class M, class U> + struct TaskUserData : public BaseTemplateUserdata { + C *instance; + M method; + U userdata; + virtual void callback() override { + (instance->*method)(userdata); + } + }; + + template <class C, class M, class U> + struct GroupUserData : public BaseTemplateUserdata { + C *instance; + M method; + U userdata; + virtual void callback_indexed(uint32_t p_index) override { + (instance->*method)(p_index, userdata); + } + }; + +protected: + static void _bind_methods(); + +public: + template <class C, class M, class U> + TaskID add_template_task(C *p_instance, M p_method, U p_userdata, bool p_high_priority = false, const String &p_description = String()) { + typedef TaskUserData<C, M, U> TUD; + TUD *ud = memnew(TUD); + ud->instance = p_instance; + ud->method = p_method; + ud->userdata = p_userdata; + return _add_task(Callable(), nullptr, nullptr, ud, p_high_priority, p_description); + } + TaskID add_native_task(void (*p_func)(void *), void *p_userdata, bool p_high_priority = false, const String &p_description = String()); + TaskID add_task(const Callable &p_action, bool p_high_priority = false, const String &p_description = String()); + + bool is_task_completed(TaskID p_task_id) const; + void wait_for_task_completion(TaskID p_task_id); + + template <class C, class M, class U> + GroupID add_template_group_task(C *p_instance, M p_method, U p_userdata, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String()) { + typedef GroupUserData<C, M, U> GUD; + GUD *ud = memnew(GUD); + ud->instance = p_instance; + ud->method = p_method; + ud->userdata = p_userdata; + return _add_group_task(Callable(), nullptr, nullptr, ud, p_elements, p_tasks, p_high_priority, p_description); + } + GroupID add_native_group_task(void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String()); + GroupID add_group_task(const Callable &p_action, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String()); + uint32_t get_group_processed_element_count(GroupID p_group) const; + bool is_group_task_completed(GroupID p_group) const; + void wait_for_group_task_completion(GroupID p_group); + + _FORCE_INLINE_ int get_thread_count() const { return threads.size(); } + + static WorkerThreadPool *get_singleton() { return singleton; } + void init(int p_thread_count = -1, bool p_use_native_threads_low_priority = true, float p_low_priority_task_ratio = 0.3); + void finish(); + WorkerThreadPool(); + ~WorkerThreadPool(); +}; + +#endif // WORKER_THREAD_POOL_H |