diff options
Diffstat (limited to 'core')
86 files changed, 3166 insertions, 1664 deletions
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index efd7e3dbf5..d7614c5a82 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -108,13 +108,6 @@ PoolStringArray _ResourceLoader::get_dependencies(const String &p_path) { return ret; }; -#ifndef DISABLE_DEPRECATED -bool _ResourceLoader::has(const String &p_path) { - WARN_PRINTS("ResourceLoader.has() is deprecated, please replace it with the equivalent has_cached() or the new exists()."); - return has_cached(p_path); -} -#endif // DISABLE_DEPRECATED - bool _ResourceLoader::has_cached(const String &p_path) { String local_path = ProjectSettings::get_singleton()->localize_path(p_path); @@ -134,9 +127,6 @@ void _ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_dependencies", "path"), &_ResourceLoader::get_dependencies); ClassDB::bind_method(D_METHOD("has_cached", "path"), &_ResourceLoader::has_cached); ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &_ResourceLoader::exists, DEFVAL("")); -#ifndef DISABLE_DEPRECATED - ClassDB::bind_method(D_METHOD("has", "path"), &_ResourceLoader::has); -#endif // DISABLE_DEPRECATED } _ResourceLoader::_ResourceLoader() { @@ -597,18 +587,6 @@ bool _OS::is_vsync_via_compositor_enabled() const { return OS::get_singleton()->is_vsync_via_compositor_enabled(); } -_OS::PowerState _OS::get_power_state() { - return _OS::PowerState(OS::get_singleton()->get_power_state()); -} - -int _OS::get_power_seconds_left() { - return OS::get_singleton()->get_power_seconds_left(); -} - -int _OS::get_power_percent_left() { - return OS::get_singleton()->get_power_percent_left(); -} - bool _OS::has_feature(const String &p_feature) const { return OS::get_singleton()->has_feature(p_feature); @@ -1358,10 +1336,6 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("has_feature", "tag_name"), &_OS::has_feature); - ClassDB::bind_method(D_METHOD("get_power_state"), &_OS::get_power_state); - ClassDB::bind_method(D_METHOD("get_power_seconds_left"), &_OS::get_power_seconds_left); - ClassDB::bind_method(D_METHOD("get_power_percent_left"), &_OS::get_power_percent_left); - ClassDB::bind_method(D_METHOD("request_permission", "name"), &_OS::request_permission); ClassDB::bind_method(D_METHOD("request_permissions"), &_OS::request_permissions); ClassDB::bind_method(D_METHOD("get_granted_permissions"), &_OS::get_granted_permissions); @@ -1410,7 +1384,7 @@ void _OS::_bind_methods() { ADD_PROPERTY_DEFAULT("window_size", Vector2()); BIND_ENUM_CONSTANT(VIDEO_DRIVER_GLES2); - BIND_ENUM_CONSTANT(VIDEO_DRIVER_GLES3); + BIND_ENUM_CONSTANT(VIDEO_DRIVER_VULKAN); BIND_ENUM_CONSTANT(DAY_SUNDAY); BIND_ENUM_CONSTANT(DAY_MONDAY); @@ -1449,12 +1423,6 @@ void _OS::_bind_methods() { BIND_ENUM_CONSTANT(SYSTEM_DIR_MUSIC); BIND_ENUM_CONSTANT(SYSTEM_DIR_PICTURES); BIND_ENUM_CONSTANT(SYSTEM_DIR_RINGTONES); - - BIND_ENUM_CONSTANT(POWERSTATE_UNKNOWN); - BIND_ENUM_CONSTANT(POWERSTATE_ON_BATTERY); - BIND_ENUM_CONSTANT(POWERSTATE_NO_BATTERY); - BIND_ENUM_CONSTANT(POWERSTATE_CHARGING); - BIND_ENUM_CONSTANT(POWERSTATE_CHARGED); } _OS::_OS() { @@ -2612,7 +2580,7 @@ void _Semaphore::_bind_methods() { _Semaphore::_Semaphore() { - semaphore = Semaphore::create(); + semaphore = SemaphoreOld::create(); } _Semaphore::~_Semaphore() { @@ -3217,7 +3185,7 @@ Ref<JSONParseResult> _JSON::parse(const String &p_json) { result->error = JSON::parse(p_json, result->result, result->error_string, result->error_line); if (result->error != OK) { - ERR_PRINTS(vformat("Error parsing JSON at line %s: %s", result->error_line, result->error_string)); + ERR_PRINT(vformat("Error parsing JSON at line %s: %s", result->error_line, result->error_string)); } return result; } diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 87da51f97e..39bed791d0 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -55,9 +55,6 @@ public: PoolVector<String> get_recognized_extensions_for_type(const String &p_type); void set_abort_on_missing_resources(bool p_abort); PoolStringArray get_dependencies(const String &p_path); -#ifndef DISABLE_DEPRECATED - bool has(const String &p_path); -#endif // DISABLE_DEPRECATED bool has_cached(const String &p_path); bool exists(const String &p_path, const String &p_type_hint = ""); @@ -104,16 +101,8 @@ protected: public: enum VideoDriver { - VIDEO_DRIVER_GLES3, VIDEO_DRIVER_GLES2, - }; - - enum PowerState { - POWERSTATE_UNKNOWN, // Cannot determine power status. - POWERSTATE_ON_BATTERY, // Not plugged in, running on the battery. - POWERSTATE_NO_BATTERY, // Plugged in, no battery available. - POWERSTATE_CHARGING, // Plugged in, charging battery. - POWERSTATE_CHARGED // Plugged in, battery charged. + VIDEO_DRIVER_VULKAN, }; enum Weekday { @@ -349,10 +338,6 @@ public: void set_vsync_via_compositor(bool p_enable); bool is_vsync_via_compositor_enabled() const; - PowerState get_power_state(); - int get_power_seconds_left(); - int get_power_percent_left(); - bool has_feature(const String &p_feature) const; bool request_permission(const String &p_name); @@ -365,7 +350,6 @@ public: }; VARIANT_ENUM_CAST(_OS::VideoDriver); -VARIANT_ENUM_CAST(_OS::PowerState); VARIANT_ENUM_CAST(_OS::Weekday); VARIANT_ENUM_CAST(_OS::Month); VARIANT_ENUM_CAST(_OS::SystemDir); @@ -642,7 +626,7 @@ public: class _Semaphore : public Reference { GDCLASS(_Semaphore, Reference); - Semaphore *semaphore; + SemaphoreOld *semaphore; static void _bind_methods(); diff --git a/core/class_db.cpp b/core/class_db.cpp index 65f0c6008c..a2941d70f6 100644 --- a/core/class_db.cpp +++ b/core/class_db.cpp @@ -335,6 +335,19 @@ StringName ClassDB::get_parent_class_nocheck(const StringName &p_class) { return ti->inherits; } +StringName ClassDB::get_compatibility_remapped_class(const StringName &p_class) { + + if (classes.has(p_class)) { + return p_class; + } + + if (compat_classes.has(p_class)) { + return compat_classes[p_class]; + } + + return p_class; +} + StringName ClassDB::get_parent_class(const StringName &p_class) { OBJTYPE_RLOCK; @@ -541,7 +554,7 @@ Object *ClassDB::instance(const StringName &p_class) { } #ifdef TOOLS_ENABLED if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { - ERR_PRINTS("Class '" + String(p_class) + "' can only be instantiated by editor."); + ERR_PRINT("Class '" + String(p_class) + "' can only be instantiated by editor."); return NULL; } #endif diff --git a/core/class_db.h b/core/class_db.h index 34301d6cba..404b04f2d0 100644 --- a/core/class_db.h +++ b/core/class_db.h @@ -218,6 +218,7 @@ public: static void get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes); static StringName get_parent_class_nocheck(const StringName &p_class); static StringName get_parent_class(const StringName &p_class); + static StringName get_compatibility_remapped_class(const StringName &p_class); static bool class_exists(const StringName &p_class); static bool is_parent_class(const StringName &p_class, const StringName &p_inherits); static bool can_instance(const StringName &p_class); diff --git a/core/color.cpp b/core/color.cpp index 1baa8af45d..03aeb2085b 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -508,13 +508,6 @@ Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const { return Color(m + r, m + g, m + b, p_a); } -// FIXME: Remove once Godot 3.1 has been released -float Color::gray() const { - - WARN_DEPRECATED_MSG("'Color.gray()' is deprecated and will be removed in a future version. Use 'Color.v' for a better grayscale approximation."); - return (r + g + b) / 3.0; -} - Color::operator String() const { return rtos(r) + ", " + rtos(g) + ", " + rtos(b) + ", " + rtos(a); @@ -529,14 +522,6 @@ Color Color::operator+(const Color &p_color) const { a + p_color.a); } -void Color::operator+=(const Color &p_color) { - - r = r + p_color.r; - g = g + p_color.g; - b = b + p_color.b; - a = a + p_color.a; -} - Color Color::operator-(const Color &p_color) const { return Color( diff --git a/core/color.h b/core/color.h index cfdd211b69..a7ab94ab08 100644 --- a/core/color.h +++ b/core/color.h @@ -56,7 +56,6 @@ struct Color { uint64_t to_rgba64() const; uint64_t to_argb64() const; uint64_t to_abgr64() const; - float gray() const; float get_h() const; float get_s() const; float get_v() const; @@ -70,7 +69,12 @@ struct Color { } Color operator+(const Color &p_color) const; - void operator+=(const Color &p_color); + _FORCE_INLINE_ void operator+=(const Color &p_color) { + r = r + p_color.r; + g = g + p_color.g; + b = b + p_color.b; + a = a + p_color.a; + } Color operator-() const; Color operator-(const Color &p_color) const; diff --git a/core/command_queue_mt.cpp b/core/command_queue_mt.cpp index c20735939d..861ca8d1d3 100644 --- a/core/command_queue_mt.cpp +++ b/core/command_queue_mt.cpp @@ -111,11 +111,11 @@ CommandQueueMT::CommandQueueMT(bool p_sync) { for (int i = 0; i < SYNC_SEMAPHORES; i++) { - sync_sems[i].sem = Semaphore::create(); + sync_sems[i].sem = SemaphoreOld::create(); sync_sems[i].in_use = false; } if (p_sync) - sync = Semaphore::create(); + sync = SemaphoreOld::create(); else sync = NULL; } diff --git a/core/command_queue_mt.h b/core/command_queue_mt.h index e5f93bcc36..2b6e0201f0 100644 --- a/core/command_queue_mt.h +++ b/core/command_queue_mt.h @@ -52,9 +52,17 @@ #define _COMMA_11 , #define _COMMA_12 , #define _COMMA_13 , +#define _COMMA_14 , +#define _COMMA_15 , // 1-based comma separated list of ITEMs #define COMMA_SEP_LIST(ITEM, LENGTH) _COMMA_SEP_LIST_##LENGTH(ITEM) +#define _COMMA_SEP_LIST_15(ITEM) \ + _COMMA_SEP_LIST_14(ITEM) \ + , ITEM(15) +#define _COMMA_SEP_LIST_14(ITEM) \ + _COMMA_SEP_LIST_13(ITEM) \ + , ITEM(14) #define _COMMA_SEP_LIST_13(ITEM) \ _COMMA_SEP_LIST_12(ITEM) \ , ITEM(13) @@ -98,6 +106,12 @@ // 1-based semicolon separated list of ITEMs #define SEMIC_SEP_LIST(ITEM, LENGTH) _SEMIC_SEP_LIST_##LENGTH(ITEM) +#define _SEMIC_SEP_LIST_15(ITEM) \ + _SEMIC_SEP_LIST_14(ITEM); \ + ITEM(15) +#define _SEMIC_SEP_LIST_14(ITEM) \ + _SEMIC_SEP_LIST_13(ITEM); \ + ITEM(14) #define _SEMIC_SEP_LIST_13(ITEM) \ _SEMIC_SEP_LIST_12(ITEM); \ ITEM(13) @@ -141,6 +155,12 @@ // 1-based space separated list of ITEMs #define SPACE_SEP_LIST(ITEM, LENGTH) _SPACE_SEP_LIST_##LENGTH(ITEM) +#define _SPACE_SEP_LIST_15(ITEM) \ + _SPACE_SEP_LIST_14(ITEM) \ + ITEM(15) +#define _SPACE_SEP_LIST_14(ITEM) \ + _SPACE_SEP_LIST_13(ITEM) \ + ITEM(14) #define _SPACE_SEP_LIST_13(ITEM) \ _SPACE_SEP_LIST_12(ITEM) \ ITEM(13) @@ -271,13 +291,13 @@ ss->in_use = false; \ } -#define MAX_CMD_PARAMS 13 +#define MAX_CMD_PARAMS 15 class CommandQueueMT { struct SyncSemaphore { - Semaphore *sem; + SemaphoreOld *sem; bool in_use; }; @@ -298,15 +318,15 @@ class CommandQueueMT { }; DECL_CMD(0) - SPACE_SEP_LIST(DECL_CMD, 13) + SPACE_SEP_LIST(DECL_CMD, 15) /* comands that return */ DECL_CMD_RET(0) - SPACE_SEP_LIST(DECL_CMD_RET, 13) + SPACE_SEP_LIST(DECL_CMD_RET, 15) /* commands that don't return but sync */ DECL_CMD_SYNC(0) - SPACE_SEP_LIST(DECL_CMD_SYNC, 13) + SPACE_SEP_LIST(DECL_CMD_SYNC, 15) /***** BASE *******/ @@ -322,7 +342,7 @@ class CommandQueueMT { uint32_t dealloc_ptr; SyncSemaphore sync_sems[SYNC_SEMAPHORES]; Mutex *mutex; - Semaphore *sync; + SemaphoreOld *sync; template <class T> T *allocate() { @@ -443,15 +463,15 @@ class CommandQueueMT { public: /* NORMAL PUSH COMMANDS */ DECL_PUSH(0) - SPACE_SEP_LIST(DECL_PUSH, 13) + SPACE_SEP_LIST(DECL_PUSH, 15) /* PUSH AND RET COMMANDS */ DECL_PUSH_AND_RET(0) - SPACE_SEP_LIST(DECL_PUSH_AND_RET, 13) + SPACE_SEP_LIST(DECL_PUSH_AND_RET, 15) /* PUSH AND RET SYNC COMMANDS*/ DECL_PUSH_AND_SYNC(0) - SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 13) + SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 15) void wait_and_flush_one() { ERR_FAIL_COND(!sync); diff --git a/core/core_builders.py b/core/core_builders.py index f3a9e3b221..7720183595 100644 --- a/core/core_builders.py +++ b/core/core_builders.py @@ -1,8 +1,8 @@ """Functions used to generate source files during build time All such functions are invoked in a subprocess on Windows to prevent build flakiness. - """ + from platform_methods import subprocess_main from compat import iteritems, itervalues, open_utf8, escape_string, byte_to_str diff --git a/core/engine.cpp b/core/engine.cpp index 1772cc7c48..85ad175f38 100644 --- a/core/engine.cpp +++ b/core/engine.cpp @@ -214,6 +214,9 @@ Engine *Engine::get_singleton() { return singleton; } +bool Engine::is_abort_on_gpu_errors_enabled() const { + return abort_on_gpu_errors; +} Engine::Engine() { singleton = this; @@ -232,4 +235,5 @@ Engine::Engine() { _frame_ticks = 0; _frame_step = 0; editor_hint = false; + abort_on_gpu_errors = false; } diff --git a/core/engine.h b/core/engine.h index 1aab907ac8..cfe3a918fc 100644 --- a/core/engine.h +++ b/core/engine.h @@ -64,6 +64,7 @@ private: bool _pixel_snap; uint64_t _physics_frames; float _physics_interpolation_fraction; + bool abort_on_gpu_errors; uint64_t _idle_frames; bool _in_physics; @@ -126,6 +127,8 @@ public: Dictionary get_license_info() const; String get_license_text() const; + bool is_abort_on_gpu_errors_enabled() const; + Engine(); virtual ~Engine() {} }; diff --git a/core/error_macros.h b/core/error_macros.h index 8ba6618942..4a3ea28957 100644 --- a/core/error_macros.h +++ b/core/error_macros.h @@ -32,21 +32,8 @@ #define ERROR_MACROS_H #include "core/typedefs.h" -/** - * Error macros. Unlike exceptions and asserts, these macros try to maintain consistency and stability - * inside the code. It is recommended to always return processable data, so in case of an error, - * the engine can keep working well. - * In most cases, bugs and/or invalid data are not fatal and should never allow a perfectly running application - * to fail or crash. - */ - -/** - * Pointer to the error macro printing function. Reassign to any function to have errors printed - */ - -/** Function used by the error macros */ -// function, file, line, error, explanation +class String; enum ErrorHandlerType { ERR_HANDLER_ERROR, @@ -55,7 +42,8 @@ enum ErrorHandlerType { ERR_HANDLER_SHADER, }; -class String; +// Pointer to the error handler printing function. Reassign to any function to have errors printed. +// Parameters: userdata, function, file, line, error, explanation, type. typedef void (*ErrorHandlerFunc)(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type); struct ErrorHandlerList { @@ -75,6 +63,7 @@ struct ErrorHandlerList { void add_error_handler(ErrorHandlerList *p_handler); void remove_error_handler(ErrorHandlerList *p_handler); +// Functions used by the error macros. void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR); void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR); void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); @@ -84,15 +73,6 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool fatal = false); void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool fatal = false); -#ifndef _STR -#define _STR(m_x) #m_x -#define _MKSTR(m_x) _STR(m_x) -#endif - -#define _FNL __FILE__ ":" - -/** An index has failed if m_index<0 or m_index >=m_size, the function exits */ - #ifdef __GNUC__ //#define FUNCTION_STR __PRETTY_FUNCTION__ - too annoying #define FUNCTION_STR __FUNCTION__ @@ -100,15 +80,16 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li #define FUNCTION_STR __FUNCTION__ #endif -// Don't use this directly; instead, use any of the CRASH_* macros #ifdef _MSC_VER -#define GENERATE_TRAP \ - __debugbreak(); \ - /* Avoid warning about control paths */ \ - for (;;) { \ - } +/** + * Don't use GENERATE_TRAP() directly, should only be used be the macros below. + */ +#define GENERATE_TRAP() __debugbreak() #else -#define GENERATE_TRAP __builtin_trap(); +/** + * Don't use GENERATE_TRAP() directly, should only be used be the macros below. + */ +#define GENERATE_TRAP() __builtin_trap() #endif // Used to strip debug messages in release mode @@ -118,411 +99,528 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li #define DEBUG_STR(m_msg) "" #endif -// (*): See https://stackoverflow.com/questions/257418/do-while-0-what-is-it-good-for - -/** - * If `m_index` is less than 0 or greater than or equal to `m_size`, prints a generic - * error message and returns from the function. This macro should be preferred to - * `ERR_FAIL_COND` for bounds checking. - */ -#define ERR_FAIL_INDEX(m_index, m_size) \ - do { \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ - _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ - return; \ - } \ - } while (0); // (*) - -/** - * If `m_index` is less than 0 or greater than or equal to `m_size`, prints a custom - * error message and returns from the function. This macro should be preferred to - * `ERR_FAIL_COND_MSG` for bounds checking. - */ -#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \ - do { \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ - _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ - return; \ - } \ - } while (0); // (*) - -/** - * If `m_index` is less than 0 or greater than or equal to `m_size`, - * prints a generic error message and returns the value specified in `m_retval`. - * This macro should be preferred to `ERR_FAIL_COND_V` for bounds checking. - */ -#define ERR_FAIL_INDEX_V(m_index, m_size, m_retval) \ - do { \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ - _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ - return m_retval; \ - } \ - } while (0); // (*) - -/** - * If `m_index` is less than 0 or greater than or equal to `m_size`, - * prints a custom error message and returns the value specified in `m_retval`. - * This macro should be preferred to `ERR_FAIL_COND_V_MSG` for bounds checking. - */ -#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ - do { \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ - _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ - return m_retval; \ - } \ - } while (0); // (*) - -/** - * If `m_index` is greater than or equal to `m_size`, - * prints a generic error message and returns the value specified in `m_retval`. - * This macro should be preferred to `ERR_FAIL_COND_V` for unsigned bounds checking. - */ -#define ERR_FAIL_UNSIGNED_INDEX_V(m_index, m_size, m_retval) \ - do { \ - if (unlikely((m_index) >= (m_size))) { \ - _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ - return m_retval; \ - } \ - } while (0); // (*) - -/** - * If `m_index` is greater than or equal to `m_size`, - * prints a custom error message and returns the value specified in `m_retval`. - * This macro should be preferred to `ERR_FAIL_COND_V_MSG` for unsigned bounds checking. - */ -#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ - do { \ - if (unlikely((m_index) >= (m_size))) { \ - _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ - return m_retval; \ - } \ - } while (0); // (*) - -/** - * If `m_index` is less than 0 or greater than or equal to `m_size`, - * crashes the engine immediately with a generic error message. - * Only use this if there's no sensible fallback (i.e. the error is unrecoverable). - * This macro should be preferred to `CRASH_COND` for bounds checking. - */ -#define CRASH_BAD_INDEX(m_index, m_size) \ - do { \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ - _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", true); \ - GENERATE_TRAP \ - } \ - } while (0); // (*) - -/** - * If `m_index` is less than 0 or greater than or equal to `m_size`, - * crashes the engine immediately with a custom error message. - * Only use this if there's no sensible fallback (i.e. the error is unrecoverable). - * This macro should be preferred to `CRASH_COND` for bounds checking. - */ -#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \ - do { \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ - _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ - GENERATE_TRAP \ - } \ - } while (0); // (*) - -/** - * If `m_param` is `null`, prints a generic error message and returns from the function. - */ -#define ERR_FAIL_NULL(m_param) \ - { \ - if (unlikely(!m_param)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \ - return; \ - } \ - } - /** - * If `m_param` is `null`, prints a custom error message and returns from the function. + * Error macros. + * WARNING: These macros work in the opposite way to assert(). + * + * Unlike exceptions and asserts, these macros try to maintain consistency and stability. + * In most cases, bugs and/or invalid data are not fatal. They should never allow a perfectly + * running application to fail or crash. + * Always try to return processable data, so the engine can keep running well. + * Use the _MSG versions to print a meaningful message to help with debugging. */ -#define ERR_FAIL_NULL_MSG(m_param, m_msg) \ - { \ - if (unlikely(!m_param)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \ - return; \ - } \ - } -/** - * If `m_param` is `null`, prints a generic error message and returns the value specified in `m_retval`. - */ -#define ERR_FAIL_NULL_V(m_param, m_retval) \ - { \ - if (unlikely(!m_param)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \ - return m_retval; \ - } \ - } +// Index out of bounds error macros. +// These macros should be used instead of `ERR_FAIL_COND` for bounds checking. -/** - * If `m_param` is `null`, prints a custom error message and returns the value specified in `m_retval`. - */ -#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \ - { \ - if (unlikely(!m_param)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \ - return m_retval; \ - } \ - } +// Integer index out of bounds error macros. /** - * If `m_cond` evaluates to `true`, prints a generic error message and returns from the function. + * Try using `ERR_FAIL_INDEX_MSG`. + * Only use this macro if there is no sensible error message. + * + * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. + * If not, the current function returns. */ -#define ERR_FAIL_COND(m_cond) \ - { \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true."); \ - return; \ - } \ - } +#define ERR_FAIL_INDEX(m_index, m_size) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ + return; \ + } else \ + ((void)0) /** - * If `m_cond` evaluates to `true`, prints a custom error message and returns from the function. + * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. + * If not, prints `m_msg` and the current function returns. */ -#define ERR_FAIL_COND_MSG(m_cond, m_msg) \ - { \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", DEBUG_STR(m_msg)); \ - return; \ - } \ - } +#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ + return; \ + } else \ + ((void)0) /** - * If `m_cond` evaluates to `true`, crashes the engine immediately with a generic error message. - * Only use this if there's no sensible fallback (i.e. the error is unrecoverable). + * Try using `ERR_FAIL_INDEX_V_MSG`. + * Only use this macro if there is no sensible error message. + * + * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. + * If not, the current function returns `m_retval`. */ -#define CRASH_COND(m_cond) \ - { \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true."); \ - GENERATE_TRAP \ - } \ - } +#define ERR_FAIL_INDEX_V(m_index, m_size, m_retval) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ + return m_retval; \ + } else \ + ((void)0) /** - * If `m_cond` evaluates to `true`, crashes the engine immediately with a custom error message. - * Only use this if there's no sensible fallback (i.e. the error is unrecoverable). - */ -#define CRASH_COND_MSG(m_cond, m_msg) \ - { \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", DEBUG_STR(m_msg)); \ - GENERATE_TRAP \ - } \ - } + * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. + * If not, prints `m_msg` and the current function returns `m_retval`. + */ +#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ + return m_retval; \ + } else \ + ((void)0) -/** - * If `m_cond` evaluates to `true`, prints a generic error message and returns the value specified in `m_retval`. - */ -#define ERR_FAIL_COND_V(m_cond, m_retval) \ - { \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returned: " _STR(m_retval)); \ - return m_retval; \ - } \ - } +/** + * Try using `ERR_FAIL_INDEX_MSG` or `ERR_FAIL_INDEX_V_MSG`. + * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and + * there is no sensible error message. + * + * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. + * If not, the application crashes. + */ +#define CRASH_BAD_INDEX(m_index, m_size) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", true); \ + GENERATE_TRAP(); \ + } else \ + ((void)0) /** - * If `m_cond` evaluates to `true`, prints a custom error message and returns the value specified in `m_retval`. - */ -#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \ - { \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returned: " _STR(m_retval), DEBUG_STR(m_msg)); \ - return m_retval; \ - } \ - } + * Try using `ERR_FAIL_INDEX_MSG` or `ERR_FAIL_INDEX_V_MSG`. + * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable. + * + * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. + * If not, prints `m_msg` and the application crashes. + */ +#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg), true); \ + GENERATE_TRAP(); \ + } else \ + ((void)0) + +// Unsigned integer index out of bounds error macros. /** - * If `m_cond` evaluates to `true`, prints a custom error message and continues the loop the macro is located in. - */ -#define ERR_CONTINUE(m_cond) \ - { \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing."); \ - continue; \ - } \ - } + * Try using `ERR_FAIL_UNSIGNED_INDEX_MSG`. + * Only use this macro if there is no sensible error message. + * + * Ensures an unsigned integer index `m_index` is less than `m_size`. + * If not, the current function returns. + */ +#define ERR_FAIL_UNSIGNED_INDEX(m_index, m_size) \ + if (unlikely((m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ + return; \ + } else \ + ((void)0) + +/** + * Ensures an unsigned integer index `m_index` is less than `m_size`. + * If not, prints `m_msg` and the current function returns. + */ +#define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ + return; \ + } else \ + ((void)0) + +/** + * Try using `ERR_FAIL_UNSIGNED_INDEX_V_MSG`. + * Only use this macro if there is no sensible error message. + * + * Ensures an unsigned integer index `m_index` is less than `m_size`. + * If not, the current function returns `m_retval`. + */ +#define ERR_FAIL_UNSIGNED_INDEX_V(m_index, m_size, m_retval) \ + if (unlikely((m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ + return m_retval; \ + } else \ + ((void)0) + +/** + * Ensures an unsigned integer index `m_index` is less than `m_size`. + * If not, prints `m_msg` and the current function returns `m_retval`. + */ +#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ + if (unlikely((m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ + return m_retval; \ + } else \ + ((void)0) + +/** + * Try using `ERR_FAIL_UNSIGNED_INDEX_MSG` or `ERR_FAIL_UNSIGNED_INDEX_V_MSG`. + * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and + * there is no sensible error message. + * + * Ensures an unsigned integer index `m_index` is less than `m_size`. + * If not, the application crashes. + */ +#define CRASH_BAD_UNSIGNED_INDEX(m_index, m_size) \ + if (unlikely((m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", true); \ + GENERATE_TRAP(); \ + } else \ + ((void)0) + +/** + * Try using `ERR_FAIL_UNSIGNED_INDEX_MSG` or `ERR_FAIL_UNSIGNED_INDEX_V_MSG`. + * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable. + * + * Ensures an unsigned integer index `m_index` is less than `m_size`. + * If not, prints `m_msg` and the application crashes. + */ +#define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg), true); \ + GENERATE_TRAP(); \ + } else \ + ((void)0) + +// Null reference error macros. + +/** + * Try using `ERR_FAIL_NULL_MSG`. + * Only use this macro if there is no sensible error message. + * + * Ensures a pointer `m_param` is not null. + * If it is null, the current function returns. + */ +#define ERR_FAIL_NULL(m_param) \ + if (unlikely(!m_param)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \ + return; \ + } else \ + ((void)0) + +/** + * Ensures a pointer `m_param` is not null. + * If it is null, prints `m_msg` and the current function returns. + */ +#define ERR_FAIL_NULL_MSG(m_param, m_msg) \ + if (unlikely(!m_param)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \ + return; \ + } else \ + ((void)0) + +/** + * Try using `ERR_FAIL_NULL_V_MSG`. + * Only use this macro if there is no sensible error message. + * + * Ensures a pointer `m_param` is not null. + * If it is null, the current function returns `m_retval`. + */ +#define ERR_FAIL_NULL_V(m_param, m_retval) \ + if (unlikely(!m_param)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \ + return m_retval; \ + } else \ + ((void)0) + +/** + * Ensures a pointer `m_param` is not null. + * If it is null, prints `m_msg` and the current function returns `m_retval`. + */ +#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \ + if (unlikely(!m_param)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \ + return m_retval; \ + } else \ + ((void)0) + +/** + * Try using `ERR_FAIL_COND_MSG`. + * Only use this macro if there is no sensible error message. + * If checking for null use ERR_FAIL_NULL_MSG instead. + * If checking index bounds use ERR_FAIL_INDEX_MSG instead. + * + * Ensures `m_cond` is false. + * If `m_cond` is true, the current function returns. + */ +#define ERR_FAIL_COND(m_cond) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true."); \ + return; \ + } else \ + ((void)0) + +/** + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg` and the current function returns. + * + * If checking for null use ERR_FAIL_NULL_MSG instead. + * If checking index bounds use ERR_FAIL_INDEX_MSG instead. + */ +#define ERR_FAIL_COND_MSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", DEBUG_STR(m_msg)); \ + return; \ + } else \ + ((void)0) + +/** + * Try using `ERR_FAIL_COND_V_MSG`. + * Only use this macro if there is no sensible error message. + * If checking for null use ERR_FAIL_NULL_V_MSG instead. + * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead. + * + * Ensures `m_cond` is false. + * If `m_cond` is true, the current function returns `m_retval`. + */ +#define ERR_FAIL_COND_V(m_cond, m_retval) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. returned: " _STR(m_retval)); \ + return m_retval; \ + } else \ + ((void)0) + +/** + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg` and the current function returns `m_retval`. + * + * If checking for null use ERR_FAIL_NULL_V_MSG instead. + * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead. + */ +#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. returned: " _STR(m_retval), DEBUG_STR(m_msg)); \ + return m_retval; \ + } else \ + ((void)0) + +/** + * Try using `ERR_CONTINUE_MSG`. + * Only use this macro if there is no sensible error message. + * + * Ensures `m_cond` is false. + * If `m_cond` is true, the current loop continues. + */ +#define ERR_CONTINUE(m_cond) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing."); \ + continue; \ + } else \ + ((void)0) + +/** + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg` and the current loop continues. + */ +#define ERR_CONTINUE_MSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", DEBUG_STR(m_msg)); \ + continue; \ + } else \ + ((void)0) + +/** + * Try using `ERR_BREAK_MSG`. + * Only use this macro if there is no sensible error message. + * + * Ensures `m_cond` is false. + * If `m_cond` is true, the current loop breaks. + */ +#define ERR_BREAK(m_cond) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking."); \ + break; \ + } else \ + ((void)0) + +/** + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg` and the current loop breaks. + */ +#define ERR_BREAK_MSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", DEBUG_STR(m_msg)); \ + break; \ + } else \ + ((void)0) + +/** + * Try using `ERR_FAIL_COND_MSG` or `ERR_FAIL_COND_V_MSG`. + * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and + * there is no sensible error message. + * + * Ensures `m_cond` is false. + * If `m_cond` is true, the application crashes. + */ +#define CRASH_COND(m_cond) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true."); \ + GENERATE_TRAP(); \ + } else \ + ((void)0) + +/** + * Try using `ERR_FAIL_COND_MSG` or `ERR_FAIL_COND_V_MSG`. + * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable. + * + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg` and the application crashes. + */ +#define CRASH_COND_MSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", DEBUG_STR(m_msg)); \ + GENERATE_TRAP(); \ + } else \ + ((void)0) + +// Generic error macros. + +/** + * Try using `ERR_FAIL_COND_MSG` or `ERR_FAIL_MSG`. + * Only use this macro if more complex error detection or recovery is required, and + * there is no sensible error message. + * + * The current function returns. + */ +#define ERR_FAIL() \ + if (1) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed."); \ + return; \ + } else \ + ((void)0) /** - * If `m_cond` evaluates to `true`, prints a custom error message and continues the loop the macro is located in. + * Try using `ERR_FAIL_COND_MSG`. + * Only use this macro if more complex error detection or recovery is required. + * + * Prints `m_msg`, and the current function returns. */ -#define ERR_CONTINUE_MSG(m_cond, m_msg) \ - { \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", DEBUG_STR(m_msg)); \ - continue; \ - } \ - } +#define ERR_FAIL_MSG(m_msg) \ + if (1) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed.", DEBUG_STR(m_msg)); \ + return; \ + } else \ + ((void)0) /** - * If `m_cond` evaluates to `true`, prints a generic error message and breaks from the loop the macro is located in. + * Try using `ERR_FAIL_COND_V_MSG` or `ERR_FAIL_V_MSG`. + * Only use this macro if more complex error detection or recovery is required, and + * there is no sensible error message. + * + * The current function returns `m_retval`. */ -#define ERR_BREAK(m_cond) \ - { \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking."); \ - break; \ - } \ - } +#define ERR_FAIL_V(m_retval) \ + if (1) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed, returning: " __STR(m_value)); \ + return m_retval; \ + } else \ + ((void)0) /** - * If `m_cond` evaluates to `true`, prints a custom error message and breaks from the loop the macro is located in. + * Try using `ERR_FAIL_COND_V_MSG`. + * Only use this macro if more complex error detection or recovery is required. + * + * Prints `m_msg`, and the current function returns `m_retval`. */ -#define ERR_BREAK_MSG(m_cond, m_msg) \ - { \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", DEBUG_STR(m_msg)); \ - break; \ - } \ - } - -/** - * Prints a generic error message and returns from the function. - */ -#define ERR_FAIL() \ - { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method failed."); \ - return; \ - } - -/** - * Prints a custom error message and returns from the function. - */ -#define ERR_FAIL_MSG(m_msg) \ - { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method failed.", DEBUG_STR(m_msg)); \ - return; \ - } - -/** - * Prints a generic error message and returns the value specified in `m_retval`. - */ -#define ERR_FAIL_V(m_retval) \ - { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method failed. Returning: " __STR(m_retval)); \ - return m_retval; \ - } - -/** - * Prints a custom error message and returns the value specified in `m_retval`. - */ -#define ERR_FAIL_V_MSG(m_retval, m_msg) \ - { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method failed. Returning: " __STR(m_retval), DEBUG_STR(m_msg)); \ - return m_retval; \ - } - -/** - * Crashes the engine immediately with a generic error message. - * Only use this if there's no sensible fallback (i.e. the error is unrecoverable). - */ -#define CRASH_NOW() \ - { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method failed."); \ - GENERATE_TRAP \ - } - -/** - * Crashes the engine immediately with a custom error message. - * Only use this if there's no sensible fallback (i.e. the error is unrecoverable). - */ -#define CRASH_NOW_MSG(m_msg) \ - { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method failed.", DEBUG_STR(m_msg)); \ - GENERATE_TRAP \ - } +#define ERR_FAIL_V_MSG(m_retval, m_msg) \ + if (1) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed, returning: " __STR(m_value), DEBUG_STR(m_msg)); \ + return m_retval; \ + } else \ + ((void)0) /** - * Prints an error message without returning. + * Try using `ERR_FAIL_COND_MSG`, `ERR_FAIL_COND_V_MSG`, `ERR_CONTINUE_MSG` or ERR_BREAK_MSG. + * Only use this macro at the start of a function that has not been implemented yet, or + * if more complex error detection or recovery is required. + * + * Prints `m_msg`. */ -#define ERR_PRINT(m_string) \ - { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string); \ - } +#define ERR_PRINT(m_msg) \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg)) /** - * Prints an error message without returning. - * FIXME: Remove this macro and replace all uses with `ERR_PRINT` as it's identical. + * Prints `m_msg` once during the application lifetime. */ -#define ERR_PRINTS(m_string) \ - { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string); \ - } +#define ERR_PRINT_ONCE(m_msg) \ + if (1) { \ + static bool first_print = true; \ + if (first_print) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg)); \ + first_print = false; \ + } \ + } else \ + ((void)0) -/** - * Prints an error message without returning, but only do so once in the application lifecycle. - * This can be used to avoid spamming the console with error messages. - */ -#define ERR_PRINT_ONCE(m_string) \ - { \ - static bool first_print = true; \ - if (first_print) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string); \ - first_print = false; \ - } \ - } +// Print warning message macros. /** - * Prints a warning message without returning. To warn about deprecated usage, - * use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. + * Prints `m_msg`. + * + * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. */ -#define WARN_PRINT(m_string) \ - { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string, ERR_HANDLER_WARNING); \ - } +#define WARN_PRINT(m_msg) \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg), ERR_HANDLER_WARNING) /** - * Prints a warning message without returning. - * FIXME: Remove this macro and replace all uses with `WARN_PRINT` as it's identical. + * Prints `m_msg` once during the application lifetime. + * + * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. */ -#define WARN_PRINTS(m_string) \ - { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string, ERR_HANDLER_WARNING); \ - } +#define WARN_PRINT_ONCE(m_msg) \ + if (1) { \ + static bool first_print = true; \ + if (first_print) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, DEBUG_STR(m_msg), ERR_HANDLER_WARNING); \ + first_print = false; \ + } \ + } else \ + ((void)0) -/** - * Prints a warning message without returning, but only do so once in the application lifecycle. - * This can be used to avoid spamming the console with warning messages. - */ -#define WARN_PRINT_ONCE(m_string) \ - { \ - static bool first_print = true; \ - if (first_print) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string, ERR_HANDLER_WARNING); \ - first_print = false; \ - } \ - } +// Print deprecated warning message macros. /** - * Prints a generic deprecation warning message without returning. - * This should be preferred to `WARN_PRINT` for deprecation warnings. + * Warns that the current function is deprecated. */ #define WARN_DEPRECATED \ - { \ + if (1) { \ static volatile bool warning_shown = false; \ if (!warning_shown) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", ERR_HANDLER_WARNING); \ warning_shown = true; \ } \ - } - -/** - * Prints a custom deprecation warning message without returning. - * This should be preferred to `WARN_PRINT` for deprecation warnings. - */ -#define WARN_DEPRECATED_MSG(m_msg) \ - { \ - static volatile bool warning_shown = false; \ - if (!warning_shown) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", m_msg, ERR_HANDLER_WARNING); \ - warning_shown = true; \ - } \ - } + } else \ + ((void)0) + +/** + * Warns that the current function is deprecated and prints `m_msg`. + */ +#define WARN_DEPRECATED_MSG(m_msg) \ + if (1) { \ + static volatile bool warning_shown = false; \ + if (!warning_shown) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", DEBUG_STR(m_msg), ERR_HANDLER_WARNING); \ + warning_shown = true; \ + } \ + } else \ + ((void)0) + +/** + * Do not use. + * If the application should never reach this point use CRASH_NOW_MSG(m_msg) to explain why. + * + * The application crashes. + */ +#define CRASH_NOW() \ + if (1) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/Function Failed."); \ + GENERATE_TRAP(); \ + } else \ + ((void)0) + +/** + * Only use if the application should never reach this point. + * + * Prints `m_msg`, and then the application crashes. + */ +#define CRASH_NOW_MSG(m_msg) \ + if (1) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/Function Failed.", DEBUG_STR(m_msg)); \ + GENERATE_TRAP(); \ + } else \ + ((void)0) #endif diff --git a/core/func_ref.cpp b/core/func_ref.cpp index 2dffb30bab..e20188c813 100644 --- a/core/func_ref.cpp +++ b/core/func_ref.cpp @@ -32,7 +32,7 @@ Variant FuncRef::call_func(const Variant **p_args, int p_argcount, Variant::CallError &r_error) { - if (id == 0) { + if (id.is_null()) { r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL; return Variant(); } @@ -48,7 +48,7 @@ Variant FuncRef::call_func(const Variant **p_args, int p_argcount, Variant::Call Variant FuncRef::call_funcv(const Array &p_args) { - ERR_FAIL_COND_V(id == 0, Variant()); + ERR_FAIL_COND_V(id.is_null(), Variant()); Object *obj = ObjectDB::get_instance(id); @@ -69,7 +69,7 @@ void FuncRef::set_function(const StringName &p_func) { } bool FuncRef::is_valid() const { - if (id == 0) + if (id.is_null()) return false; Object *obj = ObjectDB::get_instance(id); @@ -95,6 +95,5 @@ void FuncRef::_bind_methods() { ClassDB::bind_method(D_METHOD("is_valid"), &FuncRef::is_valid); } -FuncRef::FuncRef() : - id(0) { +FuncRef::FuncRef() { } diff --git a/core/hashfuncs.h b/core/hashfuncs.h index 647e8a40b2..d6cf04e560 100644 --- a/core/hashfuncs.h +++ b/core/hashfuncs.h @@ -34,10 +34,10 @@ #include "core/math/math_defs.h" #include "core/math/math_funcs.h" #include "core/node_path.h" +#include "core/object_id.h" #include "core/string_name.h" #include "core/typedefs.h" #include "core/ustring.h" - /** * Hashing functions */ @@ -137,6 +137,7 @@ struct HashMapHasherDefault { static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); } static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); } static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); } + static _FORCE_INLINE_ uint32_t hash(const ObjectID &p_id) { return hash_one_uint64(p_id); } static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash(uint64_t(p_int)); } static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_djb2_one_float(p_float); } diff --git a/core/image.cpp b/core/image.cpp index f43c26ab19..d9f84621a3 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -79,12 +79,16 @@ const char *Image::format_names[Image::FORMAT_MAX] = { "ETC2_RGB8", "ETC2_RGBA8", "ETC2_RGB8A1", + "ETC2_RA_AS_RG", + "FORMAT_DXT5_RA_AS_RG", }; SavePNGFunc Image::save_png_func = NULL; SaveEXRFunc Image::save_exr_func = NULL; +SavePNGBufferFunc Image::save_png_buffer_func = NULL; + void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, const uint8_t *p_pixel) { uint32_t ofs = (p_y * width + p_x) * p_pixelsize; @@ -115,7 +119,7 @@ int Image::get_format_pixel_size(Format p_format) { case FORMAT_RGB8: return 3; case FORMAT_RGBA8: return 4; case FORMAT_RGBA4444: return 2; - case FORMAT_RGBA5551: return 2; + case FORMAT_RGB565: return 2; case FORMAT_RF: return 4; //float case FORMAT_RGF: return 8; @@ -159,6 +163,8 @@ int Image::get_format_pixel_size(Format p_format) { case FORMAT_ETC2_RGB8: return 1; case FORMAT_ETC2_RGBA8: return 1; case FORMAT_ETC2_RGB8A1: return 1; + case FORMAT_ETC2_RA_AS_RG: return 1; + case FORMAT_DXT5_RA_AS_RG: return 1; case FORMAT_MAX: { } } @@ -207,7 +213,9 @@ void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) { case FORMAT_ETC2_RG11S: case FORMAT_ETC2_RGB8: case FORMAT_ETC2_RGBA8: - case FORMAT_ETC2_RGB8A1: { + case FORMAT_ETC2_RGB8A1: + case FORMAT_ETC2_RA_AS_RG: + case FORMAT_DXT5_RA_AS_RG: { r_w = 4; r_h = 4; @@ -268,7 +276,11 @@ int Image::get_format_block_size(Format p_format) { case FORMAT_ETC2_RG11S: case FORMAT_ETC2_RGB8: case FORMAT_ETC2_RGBA8: - case FORMAT_ETC2_RGB8A1: { + case FORMAT_ETC2_RGB8A1: + case FORMAT_ETC2_RA_AS_RG: //used to make basis universal happy + case FORMAT_DXT5_RA_AS_RG: //used to make basis universal happy + + { return 4; } @@ -318,6 +330,17 @@ int Image::get_mipmap_offset(int p_mipmap) const { return ofs; } +int Image::get_mipmap_byte_size(int p_mipmap) const { + + ERR_FAIL_INDEX_V(p_mipmap, get_mipmap_count() + 1, -1); + + int ofs, w, h; + _get_mipmap_offset_and_size(p_mipmap, ofs, w, h); + int ofs2; + _get_mipmap_offset_and_size(p_mipmap + 1, ofs2, w, h); + return ofs2 - ofs; +} + void Image::get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) const { int ofs, w, h; @@ -891,6 +914,7 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { ERR_FAIL_COND_MSG(p_height <= 0, "Image height must be greater than 0."); ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + "."); ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + "."); + ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS)); if (p_width == width && p_height == height) return; @@ -1215,7 +1239,7 @@ void Image::flip_x() { } } -int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps) { +int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps, int *r_mm_width, int *r_mm_height) { int size = 0; int w = p_width; @@ -1242,6 +1266,13 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int & size += s; + if (r_mm_width) { + *r_mm_width = bw; + } + if (r_mm_height) { + *r_mm_height = bh; + } + if (p_mipmaps >= 0 && mm == p_mipmaps) break; @@ -1547,6 +1578,206 @@ Error Image::generate_mipmaps(bool p_renormalize) { return OK; } +Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map) { + + Vector<double> normal_sat_vec; //summed area table + double *normal_sat = nullptr; //summed area table for normalmap + int normal_w = 0, normal_h = 0; + + ERR_FAIL_COND_V_MSG(p_normal_map.is_null() || p_normal_map->empty(), ERR_INVALID_PARAMETER, "Must provide a valid normalmap for roughness mipmaps"); + + Ref<Image> nm = p_normal_map->duplicate(); + if (nm->is_compressed()) { + nm->decompress(); + } + + normal_w = nm->get_width(); + normal_h = nm->get_height(); + + normal_sat_vec.resize(normal_w * normal_h * 3); + + normal_sat = normal_sat_vec.ptrw(); + + //create summed area table + nm->lock(); + + for (int y = 0; y < normal_h; y++) { + double line_sum[3] = { 0, 0, 0 }; + for (int x = 0; x < normal_w; x++) { + double normal[3]; + Color color = nm->get_pixel(x, y); + normal[0] = color.r * 2.0 - 1.0; + normal[1] = color.g * 2.0 - 1.0; + normal[2] = Math::sqrt(MAX(0.0, 1.0 - (normal[0] * normal[0] + normal[1] * normal[1]))); //reconstruct if missing + + line_sum[0] += normal[0]; + line_sum[1] += normal[1]; + line_sum[2] += normal[2]; + + uint32_t ofs = (y * normal_w + x) * 3; + + normal_sat[ofs + 0] = line_sum[0]; + normal_sat[ofs + 1] = line_sum[1]; + normal_sat[ofs + 2] = line_sum[2]; + + if (y > 0) { + uint32_t prev_ofs = ((y - 1) * normal_w + x) * 3; + normal_sat[ofs + 0] += normal_sat[prev_ofs + 0]; + normal_sat[ofs + 1] += normal_sat[prev_ofs + 1]; + normal_sat[ofs + 2] += normal_sat[prev_ofs + 2]; + } + } + } + +#if 0 + { + Vector3 beg(normal_sat_vec[0], normal_sat_vec[1], normal_sat_vec[2]); + Vector3 end(normal_sat_vec[normal_sat_vec.size() - 3], normal_sat_vec[normal_sat_vec.size() - 2], normal_sat_vec[normal_sat_vec.size() - 1]); + Vector3 avg = (end - beg) / (normal_w * normal_h); + print_line("average: " + avg); + } +#endif + + int mmcount; + + _get_dst_image_size(width, height, format, mmcount); + + lock(); + + uint8_t *base_ptr = write_lock.ptr(); + + for (int i = 1; i <= mmcount; i++) { + + int ofs, w, h; + _get_mipmap_offset_and_size(i, ofs, w, h); + uint8_t *ptr = &base_ptr[ofs]; + + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + int from_x = x * normal_w / w; + int from_y = y * normal_h / h; + int to_x = (x + 1) * normal_w / w; + int to_y = (y + 1) * normal_h / h; + to_x = MIN(to_x - 1, normal_w); + to_y = MIN(to_y - 1, normal_h); + + int size_x = (to_x - from_x) + 1; + int size_y = (to_y - from_y) + 1; + + //summed area table version (much faster) + + double avg[3] = { 0, 0, 0 }; + + if (from_x > 0 && from_y > 0) { + uint32_t tofs = ((from_y - 1) * normal_w + (from_x - 1)) * 3; + avg[0] += normal_sat[tofs + 0]; + avg[1] += normal_sat[tofs + 1]; + avg[2] += normal_sat[tofs + 2]; + } + + if (from_y > 0) { + uint32_t tofs = ((from_y - 1) * normal_w + to_x) * 3; + avg[0] -= normal_sat[tofs + 0]; + avg[1] -= normal_sat[tofs + 1]; + avg[2] -= normal_sat[tofs + 2]; + } + + if (from_x > 0) { + uint32_t tofs = (to_y * normal_w + (from_x - 1)) * 3; + avg[0] -= normal_sat[tofs + 0]; + avg[1] -= normal_sat[tofs + 1]; + avg[2] -= normal_sat[tofs + 2]; + } + + uint32_t tofs = (to_y * normal_w + to_x) * 3; + avg[0] += normal_sat[tofs + 0]; + avg[1] += normal_sat[tofs + 1]; + avg[2] += normal_sat[tofs + 2]; + + double div = double(size_x * size_y); + Vector3 vec(avg[0] / div, avg[1] / div, avg[2] / div); + + float r = vec.length(); + + int pixel_ofs = y * w + x; + Color c = _get_color_at_ofs(ptr, pixel_ofs); + + float roughness; + + switch (p_roughness_channel) { + case ROUGHNESS_CHANNEL_R: { + roughness = c.r; + } break; + case ROUGHNESS_CHANNEL_G: { + roughness = c.g; + } break; + case ROUGHNESS_CHANNEL_B: { + roughness = c.b; + } break; + case ROUGHNESS_CHANNEL_L: { + roughness = c.get_v(); + } break; + case ROUGHNESS_CHANNEL_A: { + roughness = c.a; + } break; + } + + float variance = 0; + if (r < 1.0f) { + float r2 = r * r; + float kappa = (3.0f * r - r * r2) / (1.0f - r2); + variance = 0.25f / kappa; + } + + float threshold = 0.4; + roughness = Math::sqrt(roughness * roughness + MIN(3.0f * variance, threshold * threshold)); + + switch (p_roughness_channel) { + case ROUGHNESS_CHANNEL_R: { + c.r = roughness; + } break; + case ROUGHNESS_CHANNEL_G: { + c.g = roughness; + } break; + case ROUGHNESS_CHANNEL_B: { + c.b = roughness; + } break; + case ROUGHNESS_CHANNEL_L: { + c.r = roughness; + c.g = roughness; + c.b = roughness; + } break; + case ROUGHNESS_CHANNEL_A: { + c.a = roughness; + } break; + } + + _set_color_at_ofs(ptr, pixel_ofs, c); + } + } +#if 0 + { + int size = get_mipmap_byte_size(i); + print_line("size for mimpap " + itos(i) + ": " + itos(size)); + PoolVector<uint8_t> imgdata; + imgdata.resize(size); + PoolVector<uint8_t>::Write wr = imgdata.write(); + copymem(wr.ptr(), ptr, size); + wr = PoolVector<uint8_t>::Write(); + Ref<Image> im; + im.instance(); + im->create(w, h, false, format, imgdata); + im->save_png("res://mipmap_" + itos(i) + ".png"); + } +#endif + } + + unlock(); + nm->unlock(); + + return OK; +} + void Image::clear_mipmaps() { if (!mipmaps) @@ -1576,6 +1807,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH); ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT); + ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS)); int mm = 0; int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); @@ -1595,6 +1827,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH); ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT); + ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS)); int mm; int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); @@ -1879,7 +2112,7 @@ Image::AlphaMode Image::detect_alpha() const { Error Image::load(const String &p_path) { #ifdef DEBUG_ENABLED if (p_path.begins_with("res://") && ResourceLoader::exists(p_path)) { - WARN_PRINTS("Loaded resource as image file, this will not work on export: '" + p_path + "'. Instead, import the image file as an Image resource and load it normally as a resource."); + WARN_PRINT("Loaded resource as image file, this will not work on export: '" + p_path + "'. Instead, import the image file as an Image resource and load it normally as a resource."); } #endif return ImageLoader::load_image(p_path, this); @@ -1893,6 +2126,14 @@ Error Image::save_png(const String &p_path) const { return save_png_func(p_path, Ref<Image>((Image *)this)); } +PoolVector<uint8_t> Image::save_png_to_buffer() const { + if (save_png_buffer_func == NULL) { + return PoolVector<uint8_t>(); + } + + return save_png_buffer_func(Ref<Image>((Image *)this)); +} + Error Image::save_exr(const String &p_path, bool p_grayscale) const { if (save_exr_func == NULL) @@ -1914,6 +2155,13 @@ int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format return mm; } +Size2i Image::get_image_mipmap_size(int p_width, int p_height, Format p_format, int p_mipmap) { + int mm; + Size2i ret; + _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmap, &ret.x, &ret.y); + return ret; +} + int Image::get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap) { if (p_mipmap <= 0) { @@ -1923,13 +2171,24 @@ int Image::get_image_mipmap_offset(int p_width, int p_height, Format p_format, i return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmap - 1); } +int Image::get_image_mipmap_offset_and_dimensions(int p_width, int p_height, Format p_format, int p_mipmap, int &r_w, int &r_h) { + + if (p_mipmap <= 0) { + r_w = p_width; + r_h = p_height; + return 0; + } + int mm; + return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmap - 1, &r_w, &r_h); +} + bool Image::is_compressed() const { return format > FORMAT_RGBE9995; } Error Image::decompress() { - if (format >= FORMAT_DXT1 && format <= FORMAT_RGTC_RG && _image_decompress_bc) + if (((format >= FORMAT_DXT1 && format <= FORMAT_RGTC_RG) || (format == FORMAT_DXT5_RA_AS_RG)) && _image_decompress_bc) _image_decompress_bc(this); else if (format >= FORMAT_BPTC_RGBA && format <= FORMAT_BPTC_RGBFU && _image_decompress_bptc) _image_decompress_bptc(this); @@ -1937,7 +2196,7 @@ Error Image::decompress() { _image_decompress_pvrtc(this); else if (format == FORMAT_ETC && _image_decompress_etc1) _image_decompress_etc1(this); - else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RGB8A1 && _image_decompress_etc2) + else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RA_AS_RG && _image_decompress_etc2) _image_decompress_etc2(this); else return ERR_UNAVAILABLE; @@ -1946,12 +2205,16 @@ Error Image::decompress() { Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality) { + return compress_from_channels(p_mode, detect_used_channels(p_source), p_lossy_quality); +} +Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality) { + switch (p_mode) { case COMPRESS_S3TC: { ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE); - _image_compress_bc_func(this, p_lossy_quality, p_source); + _image_compress_bc_func(this, p_lossy_quality, p_channels); } break; case COMPRESS_PVRTC2: { @@ -1971,12 +2234,12 @@ Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_loss case COMPRESS_ETC2: { ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE); - _image_compress_etc2_func(this, p_lossy_quality, p_source); + _image_compress_etc2_func(this, p_lossy_quality, p_channels); } break; case COMPRESS_BPTC: { ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE); - _image_compress_bptc_func(this, p_lossy_quality, p_source); + _image_compress_bptc_func(this, p_lossy_quality, p_channels); } break; } @@ -2015,7 +2278,7 @@ Image::Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const P Rect2 Image::get_used_rect() const { - if (format != FORMAT_LA8 && format != FORMAT_RGBA8 && format != FORMAT_RGBAF && format != FORMAT_RGBAH && format != FORMAT_RGBA4444 && format != FORMAT_RGBA5551) + if (format != FORMAT_LA8 && format != FORMAT_RGBA8 && format != FORMAT_RGBAF && format != FORMAT_RGBAH && format != FORMAT_RGBA4444 && format != FORMAT_RGB565) return Rect2(Point2(), Size2(width, height)); int len = data.size(); @@ -2317,12 +2580,12 @@ ImageMemLoadFunc Image::_png_mem_loader_func = NULL; ImageMemLoadFunc Image::_jpg_mem_loader_func = NULL; ImageMemLoadFunc Image::_webp_mem_loader_func = NULL; -void (*Image::_image_compress_bc_func)(Image *, float, Image::CompressSource) = NULL; -void (*Image::_image_compress_bptc_func)(Image *, float, Image::CompressSource) = NULL; +void (*Image::_image_compress_bc_func)(Image *, float, Image::UsedChannels) = NULL; +void (*Image::_image_compress_bptc_func)(Image *, float, Image::UsedChannels) = NULL; void (*Image::_image_compress_pvrtc2_func)(Image *) = NULL; void (*Image::_image_compress_pvrtc4_func)(Image *) = NULL; void (*Image::_image_compress_etc1_func)(Image *, float) = NULL; -void (*Image::_image_compress_etc2_func)(Image *, float, Image::CompressSource) = NULL; +void (*Image::_image_compress_etc2_func)(Image *, float, Image::UsedChannels) = NULL; void (*Image::_image_decompress_pvrtc)(Image *) = NULL; void (*Image::_image_decompress_bc)(Image *) = NULL; void (*Image::_image_decompress_bptc)(Image *) = NULL; @@ -2333,6 +2596,8 @@ PoolVector<uint8_t> (*Image::lossy_packer)(const Ref<Image> &, float) = NULL; Ref<Image> (*Image::lossy_unpacker)(const PoolVector<uint8_t> &) = NULL; PoolVector<uint8_t> (*Image::lossless_packer)(const Ref<Image> &) = NULL; Ref<Image> (*Image::lossless_unpacker)(const PoolVector<uint8_t> &) = NULL; +PoolVector<uint8_t> (*Image::basis_universal_packer)(const Ref<Image> &, Image::UsedChannels) = NULL; +Ref<Image> (*Image::basis_universal_unpacker)(const PoolVector<uint8_t> &) = NULL; void Image::_set_data(const Dictionary &p_data) { @@ -2386,18 +2651,7 @@ Color Image::get_pixelv(const Point2 &p_src) const { return get_pixel(p_src.x, p_src.y); } -Color Image::get_pixel(int p_x, int p_y) const { - - uint8_t *ptr = write_lock.ptr(); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_V_MSG(!ptr, Color(), "Image must be locked with 'lock()' before using get_pixel()."); - - ERR_FAIL_INDEX_V(p_x, width, Color()); - ERR_FAIL_INDEX_V(p_y, height, Color()); - -#endif - - uint32_t ofs = p_y * width + p_x; +Color Image::_get_color_at_ofs(uint8_t *ptr, uint32_t ofs) const { switch (format) { case FORMAT_L8: { @@ -2441,14 +2695,13 @@ Color Image::get_pixel(int p_x, int p_y) const { float a = (u & 0xF) / 15.0; return Color(r, g, b, a); } - case FORMAT_RGBA5551: { + case FORMAT_RGB565: { uint16_t u = ((uint16_t *)ptr)[ofs]; - float r = ((u >> 11) & 0x1F) / 15.0; - float g = ((u >> 6) & 0x1F) / 15.0; - float b = ((u >> 1) & 0x1F) / 15.0; - float a = (u & 0x1) / 1.0; - return Color(r, g, b, a); + float r = (u & 0x1F) / 31.0; + float g = ((u >> 5) & 0x3F) / 63.0; + float b = ((u >> 11) & 0x1F) / 31.0; + return Color(r, g, b, 1.0); } case FORMAT_RF: { @@ -2511,23 +2764,7 @@ Color Image::get_pixel(int p_x, int p_y) const { } } -void Image::set_pixelv(const Point2 &p_dst, const Color &p_color) { - set_pixel(p_dst.x, p_dst.y, p_color); -} - -void Image::set_pixel(int p_x, int p_y, const Color &p_color) { - - uint8_t *ptr = write_lock.ptr(); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!ptr, "Image must be locked with 'lock()' before using set_pixel()."); - - ERR_FAIL_INDEX(p_x, width); - ERR_FAIL_INDEX(p_y, height); - -#endif - - uint32_t ofs = p_y * width + p_x; - +void Image::_set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color) { switch (format) { case FORMAT_L8: { ptr[ofs] = uint8_t(CLAMP(p_color.get_v() * 255.0, 0, 255)); @@ -2569,14 +2806,13 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) { ((uint16_t *)ptr)[ofs] = rgba; } break; - case FORMAT_RGBA5551: { + case FORMAT_RGB565: { uint16_t rgba = 0; - rgba = uint16_t(CLAMP(p_color.r * 31.0, 0, 31)) << 11; - rgba |= uint16_t(CLAMP(p_color.g * 31.0, 0, 31)) << 6; - rgba |= uint16_t(CLAMP(p_color.b * 31.0, 0, 31)) << 1; - rgba |= uint16_t(p_color.a > 0.5 ? 1 : 0); + rgba = uint16_t(CLAMP(p_color.r * 31.0, 0, 31)); + rgba |= uint16_t(CLAMP(p_color.g * 63.0, 0, 33)) << 5; + rgba |= uint16_t(CLAMP(p_color.b * 31.0, 0, 31)) << 11; ((uint16_t *)ptr)[ofs] = rgba; @@ -2636,10 +2872,44 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) { } } -Image::DetectChannels Image::get_detected_channels() { +Color Image::get_pixel(int p_x, int p_y) const { + + uint8_t *ptr = write_lock.ptr(); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_V_MSG(!ptr, Color(), "Image must be locked with 'lock()' before using get_pixel()."); + + ERR_FAIL_INDEX_V(p_x, width, Color()); + ERR_FAIL_INDEX_V(p_y, height, Color()); + +#endif + + uint32_t ofs = p_y * width + p_x; + return _get_color_at_ofs(ptr, ofs); +} + +void Image::set_pixelv(const Point2 &p_dst, const Color &p_color) { + set_pixel(p_dst.x, p_dst.y, p_color); +} + +void Image::set_pixel(int p_x, int p_y, const Color &p_color) { + + uint8_t *ptr = write_lock.ptr(); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!ptr, "Image must be locked with 'lock()' before using set_pixel()."); + + ERR_FAIL_INDEX(p_x, width); + ERR_FAIL_INDEX(p_y, height); + +#endif + + uint32_t ofs = p_y * width + p_x; + _set_color_at_ofs(ptr, ofs, p_color); +} - ERR_FAIL_COND_V(data.size() == 0, DETECTED_RGBA); - ERR_FAIL_COND_V(is_compressed(), DETECTED_RGBA); +Image::UsedChannels Image::detect_used_channels(CompressSource p_source) { + + ERR_FAIL_COND_V(data.size() == 0, USED_CHANNELS_RGBA); + ERR_FAIL_COND_V(is_compressed(), USED_CHANNELS_RGBA); bool r = false, g = false, b = false, a = false, c = false; lock(); for (int i = 0; i < width; i++) { @@ -2664,31 +2934,42 @@ Image::DetectChannels Image::get_detected_channels() { unlock(); - if (!c && !a) - return DETECTED_L; - if (!c && a) - return DETECTED_LA; + UsedChannels used_channels; - if (r && !g && !b && !a) - return DETECTED_R; + if (!c && !a) + used_channels = USED_CHANNELS_L; + else if (!c && a) + used_channels = USED_CHANNELS_LA; + else if (r && !g && !b && !a) + used_channels = USED_CHANNELS_R; + else if (r && g && !b && !a) + used_channels = USED_CHANNELS_RG; + else if (r && g && b && !a) + used_channels = USED_CHANNELS_RGB; + else + used_channels = USED_CHANNELS_RGBA; - if (r && g && !b && !a) - return DETECTED_RG; + if (p_source == COMPRESS_SOURCE_SRGB && (used_channels == USED_CHANNELS_R || used_channels == USED_CHANNELS_RG)) { + //R and RG do not support SRGB + used_channels = USED_CHANNELS_RGB; + } - if (r && g && b && !a) - return DETECTED_RGB; + if (p_source == COMPRESS_SOURCE_NORMAL) { + //use RG channels only for normal + used_channels = USED_CHANNELS_RG; + } - return DETECTED_RGBA; + return used_channels; } void Image::optimize_channels() { - switch (get_detected_channels()) { - case DETECTED_L: convert(FORMAT_L8); break; - case DETECTED_LA: convert(FORMAT_LA8); break; - case DETECTED_R: convert(FORMAT_R8); break; - case DETECTED_RG: convert(FORMAT_RG8); break; - case DETECTED_RGB: convert(FORMAT_RGB8); break; - case DETECTED_RGBA: convert(FORMAT_RGBA8); break; + switch (detect_used_channels()) { + case USED_CHANNELS_L: convert(FORMAT_L8); break; + case USED_CHANNELS_LA: convert(FORMAT_LA8); break; + case USED_CHANNELS_R: convert(FORMAT_R8); break; + case USED_CHANNELS_RG: convert(FORMAT_RG8); break; + case USED_CHANNELS_RGB: convert(FORMAT_RGB8); break; + case USED_CHANNELS_RGBA: convert(FORMAT_RGBA8); break; } } @@ -2728,7 +3009,9 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("detect_alpha"), &Image::detect_alpha); ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible); - ClassDB::bind_method(D_METHOD("compress", "mode", "source", "lossy_quality"), &Image::compress); + ClassDB::bind_method(D_METHOD("detect_used_channels", "source"), &Image::detect_used_channels, DEFVAL(COMPRESS_SOURCE_GENERIC)); + ClassDB::bind_method(D_METHOD("compress", "mode", "source", "lossy_quality"), &Image::compress, DEFVAL(COMPRESS_SOURCE_GENERIC), DEFVAL(0.7)); + ClassDB::bind_method(D_METHOD("compress_from_channels", "mode", "channels", "lossy_quality"), &Image::compress, DEFVAL(0.7)); ClassDB::bind_method(D_METHOD("decompress"), &Image::decompress); ClassDB::bind_method(D_METHOD("is_compressed"), &Image::is_compressed); @@ -2776,7 +3059,7 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(FORMAT_RGB8); BIND_ENUM_CONSTANT(FORMAT_RGBA8); BIND_ENUM_CONSTANT(FORMAT_RGBA4444); - BIND_ENUM_CONSTANT(FORMAT_RGBA5551); + BIND_ENUM_CONSTANT(FORMAT_RGB565); BIND_ENUM_CONSTANT(FORMAT_RF); //float BIND_ENUM_CONSTANT(FORMAT_RGF); BIND_ENUM_CONSTANT(FORMAT_RGBF); @@ -2806,6 +3089,8 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(FORMAT_ETC2_RGB8); BIND_ENUM_CONSTANT(FORMAT_ETC2_RGBA8); BIND_ENUM_CONSTANT(FORMAT_ETC2_RGB8A1); + BIND_ENUM_CONSTANT(FORMAT_ETC2_RA_AS_RG); + BIND_ENUM_CONSTANT(FORMAT_DXT5_RA_AS_RG); BIND_ENUM_CONSTANT(FORMAT_MAX); BIND_ENUM_CONSTANT(INTERPOLATE_NEAREST); @@ -2824,17 +3109,24 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(COMPRESS_ETC); BIND_ENUM_CONSTANT(COMPRESS_ETC2); + BIND_ENUM_CONSTANT(USED_CHANNELS_L); + BIND_ENUM_CONSTANT(USED_CHANNELS_LA); + BIND_ENUM_CONSTANT(USED_CHANNELS_R); + BIND_ENUM_CONSTANT(USED_CHANNELS_RG); + BIND_ENUM_CONSTANT(USED_CHANNELS_RGB); + BIND_ENUM_CONSTANT(USED_CHANNELS_RGBA); + BIND_ENUM_CONSTANT(COMPRESS_SOURCE_GENERIC); BIND_ENUM_CONSTANT(COMPRESS_SOURCE_SRGB); BIND_ENUM_CONSTANT(COMPRESS_SOURCE_NORMAL); } -void Image::set_compress_bc_func(void (*p_compress_func)(Image *, float, CompressSource)) { +void Image::set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels)) { _image_compress_bc_func = p_compress_func; } -void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, float, CompressSource)) { +void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, float, UsedChannels)) { _image_compress_bptc_func = p_compress_func; } @@ -2890,6 +3182,29 @@ Ref<Image> Image::rgbe_to_srgb() { return new_image; } +Ref<Image> Image::get_image_from_mipmap(int p_mipamp) const { + + int ofs, size, w, h; + get_mipmap_offset_size_and_dimensions(p_mipamp, ofs, size, w, h); + + PoolVector<uint8_t> new_data; + new_data.resize(size); + { + PoolVector<uint8_t>::Write wr = new_data.write(); + PoolVector<uint8_t>::Read rd = data.read(); + copymem(wr.ptr(), rd.ptr() + ofs, size); + } + + Ref<Image> image; + image.instance(); + image->width = w; + image->height = h; + image->format = format; + image->data = new_data; + image->mipmaps = false; + return image; +} + void Image::bumpmap_to_normalmap(float bump_scale) { ERR_FAIL_COND(!_can_modify(format)); convert(Image::FORMAT_RF); @@ -3078,6 +3393,31 @@ Error Image::load_webp_from_buffer(const PoolVector<uint8_t> &p_array) { return _load_from_buffer(p_array, _webp_mem_loader_func); } +void Image::convert_rg_to_ra_rgba8() { + ERR_FAIL_COND(format != FORMAT_RGBA8); + ERR_FAIL_COND(!data.size()); + + int s = data.size(); + PoolVector<uint8_t>::Write w = data.write(); + for (int i = 0; i < s; i += 4) { + w[i + 3] = w[i + 1]; + w[i + 1] = 0; + w[i + 2] = 0; + } +} +void Image::convert_ra_rgba8_to_rg() { + ERR_FAIL_COND(format != FORMAT_RGBA8); + ERR_FAIL_COND(!data.size()); + + int s = data.size(); + PoolVector<uint8_t>::Write w = data.write(); + for (int i = 0; i < s; i += 4) { + w[i + 1] = w[i + 3]; + w[i + 2] = 0; + w[i + 3] = 255; + } +} + Error Image::_load_from_buffer(const PoolVector<uint8_t> &p_array, ImageMemLoadFunc p_loader) { int buffer_size = p_array.size(); diff --git a/core/image.h b/core/image.h index c15cfc9f6f..1cc22420d5 100644 --- a/core/image.h +++ b/core/image.h @@ -47,6 +47,7 @@ class Image; typedef Error (*SavePNGFunc)(const String &p_path, const Ref<Image> &p_img); +typedef PoolVector<uint8_t> (*SavePNGBufferFunc)(const Ref<Image> &p_img); typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size); typedef Error (*SaveEXRFunc)(const String &p_path, const Ref<Image> &p_img, bool p_grayscale); @@ -57,10 +58,12 @@ class Image : public Resource { public: static SavePNGFunc save_png_func; static SaveEXRFunc save_exr_func; + static SavePNGBufferFunc save_png_buffer_func; enum { - MAX_WIDTH = 16384, // force a limit somehow - MAX_HEIGHT = 16384 // force a limit somehow + MAX_WIDTH = (1 << 24), // force a limit somehow + MAX_HEIGHT = (1 << 24), // force a limit somehow + MAX_PIXELS = 268435456 }; enum Format { @@ -72,7 +75,7 @@ public: FORMAT_RGB8, FORMAT_RGBA8, FORMAT_RGBA4444, - FORMAT_RGBA5551, + FORMAT_RGB565, FORMAT_RF, //float FORMAT_RGF, FORMAT_RGBF, @@ -102,6 +105,8 @@ public: FORMAT_ETC2_RGB8, FORMAT_ETC2_RGBA8, FORMAT_ETC2_RGB8A1, + FORMAT_ETC2_RA_AS_RG, //used to make basis universal happy + FORMAT_DXT5_RA_AS_RG, //used to make basis universal happy FORMAT_MAX }; @@ -117,25 +122,27 @@ public: /* INTERPOLATE GAUSS */ }; - enum CompressSource { - COMPRESS_SOURCE_GENERIC, - COMPRESS_SOURCE_SRGB, - COMPRESS_SOURCE_NORMAL, - COMPRESS_SOURCE_LAYERED, + //this is used for compression + enum UsedChannels { + USED_CHANNELS_L, + USED_CHANNELS_LA, + USED_CHANNELS_R, + USED_CHANNELS_RG, + USED_CHANNELS_RGB, + USED_CHANNELS_RGBA, }; - //some functions provided by something else static ImageMemLoadFunc _png_mem_loader_func; static ImageMemLoadFunc _jpg_mem_loader_func; static ImageMemLoadFunc _webp_mem_loader_func; - static void (*_image_compress_bc_func)(Image *, float, CompressSource p_source); - static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, CompressSource p_source); + static void (*_image_compress_bc_func)(Image *, float, UsedChannels p_channels); + static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, UsedChannels p_channels); static void (*_image_compress_pvrtc2_func)(Image *); static void (*_image_compress_pvrtc4_func)(Image *); static void (*_image_compress_etc1_func)(Image *, float); - static void (*_image_compress_etc2_func)(Image *, float, CompressSource p_source); + static void (*_image_compress_etc2_func)(Image *, float, UsedChannels p_channels); static void (*_image_decompress_pvrtc)(Image *); static void (*_image_decompress_bc)(Image *); @@ -147,9 +154,14 @@ public: static Ref<Image> (*lossy_unpacker)(const PoolVector<uint8_t> &p_buffer); static PoolVector<uint8_t> (*lossless_packer)(const Ref<Image> &p_image); static Ref<Image> (*lossless_unpacker)(const PoolVector<uint8_t> &p_buffer); + static PoolVector<uint8_t> (*basis_universal_packer)(const Ref<Image> &p_image, UsedChannels p_channels); + static Ref<Image> (*basis_universal_unpacker)(const PoolVector<uint8_t> &p_buffer); PoolVector<uint8_t>::Write write_lock; + _FORCE_INLINE_ Color _get_color_at_ofs(uint8_t *ptr, uint32_t ofs) const; + _FORCE_INLINE_ void _set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color); + protected: static void _bind_methods(); @@ -177,7 +189,7 @@ private: _FORCE_INLINE_ void _get_mipmap_offset_and_size(int p_mipmap, int &r_offset, int &r_width, int &r_height) const; //get where the mipmap begins in data - static int _get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps = -1); + static int _get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps = -1, int *r_mm_width = NULL, int *r_mm_height = NULL); bool _can_modify(Format p_format) const; _FORCE_INLINE_ void _put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, const uint8_t *p_pixel); @@ -214,6 +226,7 @@ public: */ Format get_format() const; + int get_mipmap_byte_size(int p_mipmap) const; //get where the mipmap begins in data int get_mipmap_offset(int p_mipmap) const; //get where the mipmap begins in data void get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) const; //get where the mipmap begins in data void get_mipmap_offset_size_and_dimensions(int p_mipmap, int &r_ofs, int &r_size, int &w, int &h) const; //get where the mipmap begins in data @@ -240,6 +253,16 @@ public: */ Error generate_mipmaps(bool p_renormalize = false); + enum RoughnessChannel { + ROUGHNESS_CHANNEL_R, + ROUGHNESS_CHANNEL_G, + ROUGHNESS_CHANNEL_B, + ROUGHNESS_CHANNEL_A, + ROUGHNESS_CHANNEL_L, + }; + + Error generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map); + void clear_mipmaps(); void normalize(); //for normal maps @@ -259,6 +282,7 @@ public: Error load(const String &p_path); Error save_png(const String &p_path) const; + PoolVector<uint8_t> save_png_to_buffer() const; Error save_exr(const String &p_path, bool p_grayscale) const; /** @@ -290,7 +314,9 @@ public: static int get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps = false); static int get_image_required_mipmaps(int p_width, int p_height, Format p_format); + static Size2i get_image_mipmap_size(int p_width, int p_height, Format p_format, int p_mipmap); static int get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap); + static int get_image_mipmap_offset_and_dimensions(int p_width, int p_height, Format p_format, int p_mipmap, int &r_w, int &r_h); enum CompressMode { COMPRESS_S3TC, @@ -300,8 +326,14 @@ public: COMPRESS_ETC2, COMPRESS_BPTC }; + enum CompressSource { + COMPRESS_SOURCE_GENERIC, + COMPRESS_SOURCE_SRGB, + COMPRESS_SOURCE_NORMAL + }; - Error compress(CompressMode p_mode = COMPRESS_S3TC, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7); + Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7); + Error compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality = 0.7); Error decompress(); bool is_compressed() const; @@ -310,6 +342,7 @@ public: void srgb_to_linear(); void normalmap_to_xy(); Ref<Image> rgbe_to_srgb(); + Ref<Image> get_image_from_mipmap(int p_mipamp) const; void bumpmap_to_normalmap(float bump_scale = 1.0); void blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest); @@ -321,14 +354,17 @@ public: Rect2 get_used_rect() const; Ref<Image> get_rect(const Rect2 &p_area) const; - static void set_compress_bc_func(void (*p_compress_func)(Image *, float, CompressSource)); - static void set_compress_bptc_func(void (*p_compress_func)(Image *, float, CompressSource)); + static void set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels)); + static void set_compress_bptc_func(void (*p_compress_func)(Image *, float, UsedChannels)); static String get_format_name(Format p_format); Error load_png_from_buffer(const PoolVector<uint8_t> &p_array); Error load_jpg_from_buffer(const PoolVector<uint8_t> &p_array); Error load_webp_from_buffer(const PoolVector<uint8_t> &p_array); + void convert_rg_to_ra_rgba8(); + void convert_ra_rgba8_to_rg(); + Image(const uint8_t *p_mem_png_jpg, int p_len = -1); Image(const char **p_xpm); @@ -337,17 +373,7 @@ public: void lock(); void unlock(); - //this is used for compression - enum DetectChannels { - DETECTED_L, - DETECTED_LA, - DETECTED_R, - DETECTED_RG, - DETECTED_RGB, - DETECTED_RGBA, - }; - - DetectChannels get_detected_channels(); + UsedChannels detect_used_channels(CompressSource p_source = COMPRESS_SOURCE_GENERIC); void optimize_channels(); Color get_pixelv(const Point2 &p_src) const; @@ -371,6 +397,8 @@ VARIANT_ENUM_CAST(Image::Format) VARIANT_ENUM_CAST(Image::Interpolation) VARIANT_ENUM_CAST(Image::CompressMode) VARIANT_ENUM_CAST(Image::CompressSource) +VARIANT_ENUM_CAST(Image::UsedChannels) VARIANT_ENUM_CAST(Image::AlphaMode) +VARIANT_ENUM_CAST(Image::RoughnessChannel) #endif diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index 5c25cad770..5968972143 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -86,9 +86,10 @@ void ConfigFile::set_value(const String &p_section, const String &p_key, const V Variant ConfigFile::get_value(const String &p_section, const String &p_key, Variant p_default) const { if (!values.has(p_section) || !values[p_section].has(p_key)) { - ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, p_default, "Couldn't find the given section/key and no default was given."); + ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, Variant(), "Couldn't find the given section '" + p_section + "', key '" + p_key + "' and no default was given."); return p_default; } + return values[p_section][p_key]; } @@ -255,6 +256,22 @@ Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) { VariantParser::StreamFile stream; stream.f = f; + Error err = _parse(p_path, &stream); + + memdelete(f); + + return err; +} + +Error ConfigFile::parse(const String &p_data) { + + VariantParser::StreamString stream; + stream.s = p_data; + return _parse("<string>", &stream); +} + +Error ConfigFile::_parse(const String &p_path, VariantParser::Stream *p_stream) { + String assign; Variant value; VariantParser::Tag next_tag; @@ -270,13 +287,11 @@ Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) { next_tag.fields.clear(); next_tag.name = String(); - Error err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, NULL, true); + Error err = VariantParser::parse_tag_assign_eof(p_stream, lines, error_text, next_tag, assign, value, NULL, true); if (err == ERR_FILE_EOF) { - memdelete(f); return OK; } else if (err != OK) { - ERR_PRINTS("ConfgFile::load - " + p_path + ":" + itos(lines) + " error: " + error_text + "."); - memdelete(f); + ERR_PRINT("ConfgFile - " + p_path + ":" + itos(lines) + " error: " + error_text + "."); return err; } @@ -286,6 +301,8 @@ Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) { section = next_tag.name; } } + + return OK; } void ConfigFile::_bind_methods() { @@ -303,6 +320,7 @@ void ConfigFile::_bind_methods() { ClassDB::bind_method(D_METHOD("erase_section_key", "section", "key"), &ConfigFile::erase_section_key); ClassDB::bind_method(D_METHOD("load", "path"), &ConfigFile::load); + ClassDB::bind_method(D_METHOD("parse", "data"), &ConfigFile::parse); ClassDB::bind_method(D_METHOD("save", "path"), &ConfigFile::save); ClassDB::bind_method(D_METHOD("load_encrypted", "path", "key"), &ConfigFile::load_encrypted); diff --git a/core/io/config_file.h b/core/io/config_file.h index 95a581d156..2d61ef6afe 100644 --- a/core/io/config_file.h +++ b/core/io/config_file.h @@ -34,6 +34,7 @@ #include "core/ordered_hash_map.h" #include "core/os/file_access.h" #include "core/reference.h" +#include "core/variant_parser.h" class ConfigFile : public Reference { @@ -46,6 +47,8 @@ class ConfigFile : public Reference { Error _internal_load(const String &p_path, FileAccess *f); Error _internal_save(FileAccess *file); + Error _parse(const String &p_path, VariantParser::Stream *p_stream); + protected: static void _bind_methods(); @@ -64,6 +67,7 @@ public: Error save(const String &p_path); Error load(const String &p_path); + Error parse(const String &p_data); Error load_encrypted(const String &p_path, const Vector<uint8_t> &p_key); Error load_encrypted_pass(const String &p_path, const String &p_pass); diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index 87ead37b91..17cc6ce58f 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -63,6 +63,10 @@ Error FileAccessCompressed::open_after_magic(FileAccess *p_base) { f = p_base; cmode = (Compression::Mode)f->get_32(); block_size = f->get_32(); + if (block_size == 0) { + f = NULL; // Let the caller to handle the FileAccess object if failed to open as compressed file. + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Can't open compressed file '" + p_base->get_path() + "' with block size 0, it is corrupted."); + } read_total = f->get_32(); int bc = (read_total / block_size) + 1; int acc_ofs = f->get_position() + bc * 4; @@ -125,13 +129,11 @@ Error FileAccessCompressed::_open(const String &p_path, int p_mode_flags) { char rmagic[5]; f->get_buffer((uint8_t *)rmagic, 4); rmagic[4] = 0; - if (magic != rmagic) { + if (magic != rmagic || open_after_magic(f) != OK) { memdelete(f); f = NULL; return ERR_FILE_UNRECOGNIZED; } - - open_after_magic(f); } return OK; diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index 1b09ac7208..202eb89dbd 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -231,7 +231,7 @@ FileAccessNetworkClient::FileAccessNetworkClient() { singleton = this; last_id = 0; client.instance(); - sem = Semaphore::create(); + sem = SemaphoreOld::create(); lockcount = 0; } @@ -522,8 +522,8 @@ FileAccessNetwork::FileAccessNetwork() { eof_flag = false; opened = false; pos = 0; - sem = Semaphore::create(); - page_sem = Semaphore::create(); + sem = SemaphoreOld::create(); + page_sem = SemaphoreOld::create(); buffer_mutex = Mutex::create(); FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; nc->lock_mutex(); diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h index e2da1d0893..f329abf7c5 100644 --- a/core/io/file_access_network.h +++ b/core/io/file_access_network.h @@ -49,7 +49,7 @@ class FileAccessNetworkClient { List<BlockRequest> block_requests; - Semaphore *sem; + SemaphoreOld *sem; Thread *thread; bool quit; Mutex *mutex; @@ -85,8 +85,8 @@ public: class FileAccessNetwork : public FileAccess { - Semaphore *sem; - Semaphore *page_sem; + SemaphoreOld *sem; + SemaphoreOld *page_sem; Mutex *buffer_mutex; bool opened; size_t total_size; diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index 05929ee8ed..720f25f91b 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -53,7 +53,7 @@ Error ImageLoader::load_image(String p_file, Ref<Image> p_image, FileAccess *p_c Error err; f = FileAccess::open(p_file, FileAccess::READ, &err); if (!f) { - ERR_PRINTS("Error opening file '" + p_file + "'."); + ERR_PRINT("Error opening file '" + p_file + "'."); return err; } } @@ -66,7 +66,7 @@ Error ImageLoader::load_image(String p_file, Ref<Image> p_image, FileAccess *p_c continue; Error err = loader[i]->load_image(p_image, f, p_force_linear, p_scale); if (err != OK) { - ERR_PRINTS("Error loading image: " + p_file); + ERR_PRINT("Error loading image: " + p_file); } if (err != ERR_FILE_UNRECOGNIZED) { diff --git a/core/io/ip.cpp b/core/io/ip.cpp index ea791fb327..7d18117711 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -71,7 +71,7 @@ struct _IP_ResolverPrivate { } Mutex *mutex; - Semaphore *sem; + SemaphoreOld *sem; Thread *thread; //Semaphore* semaphore; @@ -184,7 +184,7 @@ IP_Address IP::get_resolve_item_address(ResolverID p_id) const { resolver->mutex->lock(); if (resolver->queue[p_id].status != IP::RESOLVER_STATUS_DONE) { - ERR_PRINTS("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet."); + ERR_PRINT("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet."); resolver->mutex->unlock(); return IP_Address(); } @@ -319,7 +319,7 @@ IP::IP() { #ifndef NO_THREADS - resolver->sem = Semaphore::create(); + resolver->sem = SemaphoreOld::create(); if (resolver->sem) { resolver->thread_abort = false; diff --git a/core/io/json.cpp b/core/io/json.cpp index dbf1676e62..144e4bdc3b 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -203,8 +203,7 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to case 'f': res = 12; break; case 'r': res = 13; break; case 'u': { - //hexnumbarh - oct is deprecated - + // hex number for (int j = 0; j < 4; j++) { CharType c = p_str[index + j + 1]; if (c == 0) { @@ -226,7 +225,7 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to v = c - 'A'; v += 10; } else { - ERR_PRINT("BUG"); + ERR_PRINT("Bug parsing hex constant."); v = 0; } @@ -236,13 +235,8 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to index += 4; //will add at the end anyway } break; - //case '\"': res='\"'; break; - //case '\\': res='\\'; break; - //case '/': res='/'; break; default: { res = next; - //r_err_str="Invalid escape sequence"; - //return ERR_PARSE_ERROR; } break; } diff --git a/core/io/logger.cpp b/core/io/logger.cpp index c3ea0d024e..4d732332d5 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -58,12 +58,12 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c return; } - const char *err_type = "**ERROR**"; + const char *err_type = "ERROR"; switch (p_type) { - case ERR_ERROR: err_type = "**ERROR**"; break; - case ERR_WARNING: err_type = "**WARNING**"; break; - case ERR_SCRIPT: err_type = "**SCRIPT ERROR**"; break; - case ERR_SHADER: err_type = "**SHADER ERROR**"; break; + case ERR_ERROR: err_type = "ERROR"; break; + case ERR_WARNING: err_type = "WARNING"; break; + case ERR_SCRIPT: err_type = "SCRIPT ERROR"; break; + case ERR_SHADER: err_type = "SHADER ERROR"; break; default: ERR_PRINT("Unknown error type"); break; } @@ -74,7 +74,7 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c err_details = p_code; logf_error("%s: %s\n", err_type, err_details); - logf_error(" At: %s:%i:%s() - %s\n", p_file, p_line, p_function, p_code); + logf_error(" at: %s (%s:%i) - %s\n", p_function, p_file, p_line, p_code); } void Logger::logf(const char *p_format, ...) { diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index e847a9cf0c..17edc4982c 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -53,8 +53,7 @@ ObjectID EncodedObjectAsID::get_object_id() const { return id; } -EncodedObjectAsID::EncodedObjectAsID() : - id(0) { +EncodedObjectAsID::EncodedObjectAsID() { } #define _S(a) ((int32_t)a) @@ -386,11 +385,11 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (type & ENCODE_FLAG_OBJECT_AS_ID) { //this _is_ allowed ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); - ObjectID val = decode_uint64(buf); + ObjectID val = ObjectID(decode_uint64(buf)); if (r_len) (*r_len) += 8; - if (val == 0) { + if (val.is_null()) { r_variant = (Object *)NULL; } else { Ref<EncodedObjectAsID> obj_as_id; @@ -803,10 +802,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } } break; case Variant::OBJECT: { -#ifdef DEBUG_ENABLED + // Test for potential wrong values sent by the debugger when it breaks. - Object *obj = p_variant; - if (!obj || !ObjectDB::instance_validate(obj)) { + Object *obj = p_variant.get_validated_object(); + if (!obj) { // Object is invalid, send a NULL instead. if (buf) { encode_uint32(Variant::NIL, buf); @@ -814,7 +813,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; return OK; } -#endif // DEBUG_ENABLED + if (!p_full_objects) { flags |= ENCODE_FLAG_OBJECT_AS_ID; } @@ -1128,9 +1127,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } else { if (buf) { - Object *obj = p_variant; - ObjectID id = 0; - if (obj && ObjectDB::instance_validate(obj)) { + Object *obj = p_variant.get_validated_object(); + ObjectID id; + if (obj) { id = obj->get_instance_id(); } diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 381ac4c0bb..3f66e00021 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -32,6 +32,7 @@ #include "core/io/marshalls.h" #include "scene/main/node.h" +#include <stdint.h> #ifdef DEBUG_ENABLED #include "core/os/os.h" @@ -180,7 +181,8 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ } #endif - uint8_t packet_type = p_packet[0]; + // Extract the `packet_type` from the LSB three bits: + uint8_t packet_type = p_packet[0] & 7; switch (packet_type) { @@ -197,31 +199,80 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ case NETWORK_COMMAND_REMOTE_CALL: case NETWORK_COMMAND_REMOTE_SET: { - ERR_FAIL_COND_MSG(p_packet_len < 6, "Invalid packet received. Size too small."); - - Node *node = _process_get_node(p_from, p_packet, p_packet_len); + // Extract packet meta + int packet_min_size = 1; + int name_id_offset = 1; + ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); + // Compute the meta size, which depends on the compression level. + int node_id_compression = (p_packet[0] & 24) >> 3; + int name_id_compression = (p_packet[0] & 32) >> 5; + + switch (node_id_compression) { + case NETWORK_NODE_ID_COMPRESSION_8: + packet_min_size += 1; + name_id_offset += 1; + break; + case NETWORK_NODE_ID_COMPRESSION_16: + packet_min_size += 2; + name_id_offset += 2; + break; + case NETWORK_NODE_ID_COMPRESSION_32: + packet_min_size += 4; + name_id_offset += 4; + break; + default: + ERR_FAIL_MSG("Was not possible to extract the node id compression mode."); + } + switch (name_id_compression) { + case NETWORK_NAME_ID_COMPRESSION_8: + packet_min_size += 1; + break; + case NETWORK_NAME_ID_COMPRESSION_16: + packet_min_size += 2; + break; + default: + ERR_FAIL_MSG("Was not possible to extract the name id compression mode."); + } + ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); + uint32_t node_target = 0; + switch (node_id_compression) { + case NETWORK_NODE_ID_COMPRESSION_8: + node_target = p_packet[1]; + break; + case NETWORK_NODE_ID_COMPRESSION_16: + node_target = decode_uint16(p_packet + 1); + break; + case NETWORK_NODE_ID_COMPRESSION_32: + node_target = decode_uint32(p_packet + 1); + break; + default: + // Unreachable, checked before. + CRASH_NOW(); + } + Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len); ERR_FAIL_COND_MSG(node == NULL, "Invalid packet received. Requested node was not found."); - // Detect cstring end. - int len_end = 5; - for (; len_end < p_packet_len; len_end++) { - if (p_packet[len_end] == 0) { + uint16_t name_id = 0; + switch (name_id_compression) { + case NETWORK_NAME_ID_COMPRESSION_8: + name_id = p_packet[name_id_offset]; break; - } + case NETWORK_NAME_ID_COMPRESSION_16: + name_id = decode_uint16(p_packet + name_id_offset); + break; + default: + // Unreachable, checked before. + CRASH_NOW(); } - ERR_FAIL_COND_MSG(len_end >= p_packet_len, "Invalid packet received. Size too small."); - - StringName name = String::utf8((const char *)&p_packet[5]); - if (packet_type == NETWORK_COMMAND_REMOTE_CALL) { - _process_rpc(node, name, p_from, p_packet, p_packet_len, len_end + 1); + _process_rpc(node, name_id, p_from, p_packet, p_packet_len, packet_min_size); } else { - _process_rset(node, name, p_from, p_packet, p_packet_len, len_end + 1); + _process_rset(node, name_id, p_from, p_packet, p_packet_len, packet_min_size); } } break; @@ -233,15 +284,14 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ } } -Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len) { +Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) { - uint32_t target = decode_uint32(&p_packet[1]); Node *node = NULL; - if (target & 0x80000000) { + if (p_node_target & 0x80000000) { // Use full path (not cached yet). - int ofs = target & 0x7FFFFFFF; + int ofs = p_node_target & 0x7FFFFFFF; ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, NULL, "Invalid packet received. Size smaller than declared."); @@ -253,10 +303,10 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int node = root_node->get_node(np); if (!node) - ERR_PRINTS("Failed to get path from RPC: " + String(np) + "."); + ERR_PRINT("Failed to get path from RPC: " + String(np) + "."); } else { // Use cached path. - int id = target; + int id = p_node_target; Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from); ERR_FAIL_COND_V_MSG(!E, NULL, "Invalid packet received. Requests invalid peer cache."); @@ -269,26 +319,26 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int node = root_node->get_node(ni->path); if (!node) - ERR_PRINTS("Failed to get cached path from RPC: " + String(ni->path) + "."); + ERR_PRINT("Failed to get cached path from RPC: " + String(ni->path) + "."); } return node; } -void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { +void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); // Check that remote can call the RPC on this node. - RPCMode rpc_mode = RPC_MODE_DISABLED; - const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_name); - if (E) { - rpc_mode = E->get(); - } else if (p_node->get_script_instance()) { - rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_name); + StringName name = p_node->get_node_rpc_method(p_rpc_method_id); + RPCMode rpc_mode = p_node->get_node_rpc_mode_by_id(p_rpc_method_id); + if (name == StringName() && p_node->get_script_instance()) { + name = p_node->get_script_instance()->get_rpc_method(p_rpc_method_id); + rpc_mode = p_node->get_script_instance()->get_rpc_mode_by_id(p_rpc_method_id); } + ERR_FAIL_COND(name == StringName()); bool can_call = _can_call_mode(p_node, rpc_mode, p_from); - ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + "."); + ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + "."); int argc = p_packet[p_offset]; Vector<Variant> args; @@ -311,7 +361,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); int vlen; - Error err = decode_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen, allow_object_decoding || network_peer->is_object_decoding_allowed()); + Error err = _decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument."); argp.write[i] = &args[i]; @@ -320,29 +370,29 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ Variant::CallError ce; - p_node->call(p_name, (const Variant **)argp.ptr(), argc, ce); + p_node->call(name, (const Variant **)argp.ptr(), argc, ce); if (ce.error != Variant::CallError::CALL_OK) { - String error = Variant::get_call_error_text(p_node, p_name, (const Variant **)argp.ptr(), argc, ce); + String error = Variant::get_call_error_text(p_node, name, (const Variant **)argp.ptr(), argc, ce); error = "RPC - " + error; - ERR_PRINTS(error); + ERR_PRINT(error); } } -void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { +void MultiplayerAPI::_process_rset(Node *p_node, const uint16_t p_rpc_property_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); // Check that remote can call the RSET on this node. - RPCMode rset_mode = RPC_MODE_DISABLED; - const Map<StringName, RPCMode>::Element *E = p_node->get_node_rset_mode(p_name); - if (E) { - rset_mode = E->get(); - } else if (p_node->get_script_instance()) { - rset_mode = p_node->get_script_instance()->get_rset_mode(p_name); + StringName name = p_node->get_node_rset_property(p_rpc_property_id); + RPCMode rset_mode = p_node->get_node_rset_mode_by_id(p_rpc_property_id); + if (name == StringName() && p_node->get_script_instance()) { + name = p_node->get_script_instance()->get_rset_property(p_rpc_property_id); + rset_mode = p_node->get_script_instance()->get_rset_mode_by_id(p_rpc_property_id); } + ERR_FAIL_COND(name == StringName()); bool can_call = _can_call_mode(p_node, rset_mode, p_from); - ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + "."); + ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + "."); #ifdef DEBUG_ENABLED if (profiling) { @@ -353,26 +403,33 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p #endif Variant value; - Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL, allow_object_decoding || network_peer->is_object_decoding_allowed()); + Error err = _decode_and_decompress_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL); ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RSET value."); bool valid; - p_node->set(p_name, value, &valid); + p_node->set(name, value, &valid); if (!valid) { - String error = "Error setting remote property '" + String(p_name) + "', not found in object of type " + p_node->get_class() + "."; - ERR_PRINTS(error); + String error = "Error setting remote property '" + String(name) + "', not found in object of type " + p_node->get_class() + "."; + ERR_PRINT(error); } } void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < 5, "Invalid packet received. Size too small."); - int id = decode_uint32(&p_packet[1]); + ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small."); + int ofs = 1; + + String methods_md5; + methods_md5.parse_utf8((const char *)(p_packet + ofs), 32); + ofs += 33; + + int id = decode_uint32(&p_packet[ofs]); + ofs += 4; String paths; - paths.parse_utf8((const char *)&p_packet[5], p_packet_len - 5); + paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs); NodePath path = paths; @@ -380,9 +437,15 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, path_get_cache[p_from] = PathGetCache(); } + Node *node = root_node->get_node(path); + ERR_FAIL_COND(node == NULL); + const bool valid_rpc_checksum = node->get_rpc_md5() == methods_md5; + if (valid_rpc_checksum == false) { + ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); + } + PathGetCache::NodeInfo ni; ni.path = path; - ni.instance = 0; path_get_cache[p_from].nodes[id] = ni; @@ -392,9 +455,10 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, Vector<uint8_t> packet; - packet.resize(1 + len); + packet.resize(1 + 1 + len); packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH; - encode_cstring(pname.get_data(), &packet.write[1]); + packet.write[1] = valid_rpc_checksum; + encode_cstring(pname.get_data(), &packet.write[2]); network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->set_target_peer(p_from); @@ -403,13 +467,19 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small."); + ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small."); + + const bool valid_rpc_checksum = p_packet[1]; String paths; - paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1); + paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2); NodePath path = paths; + if (valid_rpc_checksum == false) { + ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); + } + PathSentCache *psc = path_send_cache.getptr(path); ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache."); @@ -418,7 +488,7 @@ void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, E->get() = true; } -bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target) { +bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) { bool has_all_peers = true; List<int> peers_to_add; // If one is missing, take note to add it. @@ -443,31 +513,192 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int } } - // Those that need to be added, send a message for this. + if (peers_to_add.size() > 0) { - for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) { + // Those that need to be added, send a message for this. // Encode function name. - CharString pname = String(p_path).utf8(); - int len = encode_cstring(pname.get_data(), NULL); + const CharString path = String(p_path).utf8(); + const int path_len = encode_cstring(path.get_data(), NULL); + + // Extract MD5 from rpc methods list. + const String methods_md5 = p_node->get_rpc_md5(); + const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder. Vector<uint8_t> packet; + packet.resize(1 + 4 + path_len + methods_md5_len); + int ofs = 0; + + packet.write[ofs] = NETWORK_COMMAND_SIMPLIFY_PATH; + ofs += 1; + + ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]); + + ofs += encode_uint32(psc->id, &packet.write[ofs]); + + ofs += encode_cstring(path.get_data(), &packet.write[ofs]); - packet.resize(1 + 4 + len); - packet.write[0] = NETWORK_COMMAND_SIMPLIFY_PATH; - encode_uint32(psc->id, &packet.write[1]); - encode_cstring(pname.get_data(), &packet.write[5]); + for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) { - network_peer->set_target_peer(E->get()); // To all of you. - network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); - network_peer->put_packet(packet.ptr(), packet.size()); + network_peer->set_target_peer(E->get()); // To all of you. + network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); + network_peer->put_packet(packet.ptr(), packet.size()); - psc->confirmed_peers.insert(E->get(), false); // Insert into confirmed, but as false since it was not confirmed. + psc->confirmed_peers.insert(E->get(), false); // Insert into confirmed, but as false since it was not confirmed. + } } return has_all_peers; } +// The variant is compressed and encoded; The first byte contains all the meta +// information and the format is: +// - The first LSB 5 bits are used for the variant type. +// - The next two bits are used to store the encoding mode. +// - The most significant is used to store the boolean value. +#define VARIANT_META_TYPE_MASK 0x1F +#define VARIANT_META_EMODE_MASK 0x60 +#define VARIANT_META_BOOL_MASK 0x80 +#define ENCODE_8 0 << 5 +#define ENCODE_16 1 << 5 +#define ENCODE_32 2 << 5 +#define ENCODE_64 3 << 5 +Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) { + + // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31 + CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK); + + uint8_t *buf = r_buffer; + r_len = 0; + uint8_t encode_mode = 0; + + switch (p_variant.get_type()) { + case Variant::BOOL: { + if (buf) { + // We still have 1 free bit in the meta, so let's use it. + buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0; + buf[0] |= encode_mode | p_variant.get_type(); + } + r_len += 1; + } break; + case Variant::INT: { + if (buf) { + // Reserve the first byte for the meta. + buf += 1; + } + r_len += 1; + int64_t val = p_variant; + if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) { + // Use 8 bit + encode_mode = ENCODE_8; + if (buf) { + buf[0] = val; + } + r_len += 1; + } else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) { + // Use 16 bit + encode_mode = ENCODE_16; + if (buf) { + encode_uint16(val, buf); + } + r_len += 2; + } else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) { + // Use 32 bit + encode_mode = ENCODE_32; + if (buf) { + encode_uint32(val, buf); + } + r_len += 4; + } else { + // Use 64 bit + encode_mode = ENCODE_64; + if (buf) { + encode_uint64(val, buf); + } + r_len += 8; + } + // Store the meta + if (buf) { + buf -= 1; + buf[0] = encode_mode | p_variant.get_type(); + } + } break; + default: + // Any other case is not yet compressed. + Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding); + if (err != OK) + return err; + if (r_buffer) { + // The first byte is not used by the marshaling, so store the type + // so we know how to decompress and decode this variant. + r_buffer[0] = p_variant.get_type(); + } + } + + return OK; +} +Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) { + + const uint8_t *buf = p_buffer; + int len = p_len; + + ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); + uint8_t type = buf[0] & VARIANT_META_TYPE_MASK; + uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK; + + ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA); + + switch (type) { + case Variant::BOOL: { + bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0; + r_variant = val; + if (r_len) + *r_len = 1; + } break; + case Variant::INT: { + buf += 1; + len -= 1; + if (r_len) + *r_len = 1; + if (encode_mode == ENCODE_8) { + // 8 bits. + ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); + int8_t val = buf[0]; + r_variant = val; + if (r_len) + (*r_len) += 1; + } else if (encode_mode == ENCODE_16) { + // 16 bits. + ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA); + int16_t val = decode_uint16(buf); + r_variant = val; + if (r_len) + (*r_len) += 2; + } else if (encode_mode == ENCODE_32) { + // 32 bits. + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); + int32_t val = decode_uint32(buf); + r_variant = val; + if (r_len) + (*r_len) += 4; + } else { + // 64 bits. + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); + int64_t val = decode_uint64(buf); + r_variant = val; + if (r_len) + (*r_len) += 8; + } + } break; + default: + Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding); + if (err != OK) + return err; + } + + return OK; +} + void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount) { ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree."); @@ -496,6 +727,9 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p psc->id = last_send_cache_id++; } + // See if all peers have cached path (if so, call can be fast). + const bool has_all_peers = _send_confirm_path(p_from, from_path, psc, p_to); + // Create base packet, lots of hardcode because it must be tight. int ofs = 0; @@ -503,45 +737,125 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p #define MAKE_ROOM(m_amount) \ if (packet_cache.size() < m_amount) packet_cache.resize(m_amount); - // Encode type. + // Encode meta. + // The meta is composed by a single byte that contains (starting from the least segnificant bit): + // - `NetworkCommands` in the first three bits. + // - `NetworkNodeIdCompression` in the next 2 bits. + // - `NetworkNameIdCompression` in the next 1 bit. + // - So we still have the last two bits free! + uint8_t command_type = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; + uint8_t node_id_compression = UINT8_MAX; + uint8_t name_id_compression = UINT8_MAX; + MAKE_ROOM(1); - packet_cache.write[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; + // The meta is composed along the way, so just set 0 for now. + packet_cache.write[0] = 0; ofs += 1; - // Encode ID. - MAKE_ROOM(ofs + 4); - encode_uint32(psc->id, &(packet_cache.write[ofs])); - ofs += 4; - - // Encode function name. - CharString name = String(p_name).utf8(); - int len = encode_cstring(name.get_data(), NULL); - MAKE_ROOM(ofs + len); - encode_cstring(name.get_data(), &(packet_cache.write[ofs])); - ofs += len; + // Encode Node ID. + if (has_all_peers) { + // Compress the node ID only if all the target peers already know it. + if (psc->id >= 0 && psc->id <= 255) { + // We can encode the id in 1 byte + node_id_compression = NETWORK_NODE_ID_COMPRESSION_8; + MAKE_ROOM(ofs + 1); + packet_cache.write[ofs] = static_cast<uint8_t>(psc->id); + ofs += 1; + } else if (psc->id >= 0 && psc->id <= 65535) { + // We can encode the id in 2 bytes + node_id_compression = NETWORK_NODE_ID_COMPRESSION_16; + MAKE_ROOM(ofs + 2); + encode_uint16(static_cast<uint16_t>(psc->id), &(packet_cache.write[ofs])); + ofs += 2; + } else { + // Too big, let's use 4 bytes. + node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; + MAKE_ROOM(ofs + 4); + encode_uint32(psc->id, &(packet_cache.write[ofs])); + ofs += 4; + } + } else { + // The targets doesn't know the node yet, so we need to use 32 bits int. + node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; + MAKE_ROOM(ofs + 4); + encode_uint32(psc->id, &(packet_cache.write[ofs])); + ofs += 4; + } if (p_set) { + + // Take the rpc property ID + uint16_t property_id = p_from->get_node_rset_property_id(p_name); + if (property_id == UINT16_MAX && p_from->get_script_instance()) { + property_id = p_from->get_script_instance()->get_rset_property_id(p_name); + } + ERR_FAIL_COND_MSG(property_id == UINT16_MAX, "Unable to take the `property_id` for the property:" + p_name + ". this can happen only if this property is not marked as `remote`."); + + if (property_id <= UINT8_MAX) { + // The ID fits in 1 byte + name_id_compression = NETWORK_NAME_ID_COMPRESSION_8; + MAKE_ROOM(ofs + 1); + packet_cache.write[ofs] = static_cast<uint8_t>(property_id); + ofs += 1; + } else { + // The ID is larger, let's use 2 bytes + name_id_compression = NETWORK_NAME_ID_COMPRESSION_16; + MAKE_ROOM(ofs + 2); + encode_uint16(property_id, &(packet_cache.write[ofs])); + ofs += 2; + } + // Set argument. - Error err = encode_variant(*p_arg[0], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed()); + int len(0); + Error err = _encode_and_compress_variant(*p_arg[0], NULL, len); ERR_FAIL_COND_MSG(err != OK, "Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!"); MAKE_ROOM(ofs + len); - encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed()); + _encode_and_compress_variant(*p_arg[0], &(packet_cache.write[ofs]), len); ofs += len; } else { + // Take the rpc method ID + uint16_t method_id = p_from->get_node_rpc_method_id(p_name); + if (method_id == UINT16_MAX && p_from->get_script_instance()) { + method_id = p_from->get_script_instance()->get_rpc_method_id(p_name); + } + ERR_FAIL_COND_MSG(method_id == UINT16_MAX, "Unable to take the `method_id` for the function:" + p_name + ". this can happen only if this method is not marked as `remote`."); + + if (method_id <= UINT8_MAX) { + // The ID fits in 1 byte + name_id_compression = NETWORK_NAME_ID_COMPRESSION_8; + MAKE_ROOM(ofs + 1); + packet_cache.write[ofs] = static_cast<uint8_t>(method_id); + ofs += 1; + } else { + // The ID is larger, let's use 2 bytes + name_id_compression = NETWORK_NAME_ID_COMPRESSION_16; + MAKE_ROOM(ofs + 2); + encode_uint16(method_id, &(packet_cache.write[ofs])); + ofs += 2; + } + // Call arguments. MAKE_ROOM(ofs + 1); packet_cache.write[ofs] = p_argcount; ofs += 1; for (int i = 0; i < p_argcount; i++) { - Error err = encode_variant(*p_arg[i], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed()); + int len(0); + Error err = _encode_and_compress_variant(*p_arg[i], NULL, len); ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); MAKE_ROOM(ofs + len); - encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed()); + _encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len); ofs += len; } } + ERR_FAIL_COND(command_type > 7); + ERR_FAIL_COND(node_id_compression > 3); + ERR_FAIL_COND(name_id_compression > 1); + + // We can now set the meta + packet_cache.write[0] = command_type + (node_id_compression << 3) + (name_id_compression << 5); + #ifdef DEBUG_ENABLED if (profiling) { bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].timestamp = OS::get_singleton()->get_ticks_msec(); @@ -550,9 +864,6 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p } #endif - // See if all peers have cached path (is so, call can be fast). - bool has_all_peers = _send_confirm_path(from_path, psc, p_to); - // Take chance and set transfer mode, since all send methods will use it. network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); @@ -562,6 +873,9 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p network_peer->set_target_peer(p_to); // To all of you. network_peer->put_packet(packet_cache.ptr(), ofs); // A message with love. } else { + // Unreachable because the node ID is never compressed if the peers doesn't know it. + CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32); + // Not all verified path, so send one by one. // Append path at the end, since we will need it for some packets. @@ -647,16 +961,14 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { // Check that send mode can use local call. - const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_method); - if (E) { - call_local_native = _should_call_local(E->get(), is_master, skip_rpc); - } + RPCMode rpc_mode = p_node->get_node_rpc_mode(p_method); + call_local_native = _should_call_local(rpc_mode, is_master, skip_rpc); if (call_local_native) { // Done below. } else if (p_node->get_script_instance()) { // Attempt with script. - RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method); + rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method); call_local_script = _should_call_local(rpc_mode, is_master, skip_rpc); } } @@ -683,7 +995,7 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const if (ce.error != Variant::CallError::CALL_OK) { String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce); error = "rpc() aborted in local call: - " + error + "."; - ERR_PRINTS(error); + ERR_PRINT(error); return; } } @@ -698,7 +1010,7 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const if (ce.error != Variant::CallError::CALL_OK) { String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce); error = "rpc() aborted in script local call: - " + error + "."; - ERR_PRINTS(error); + ERR_PRINT(error); return; } } @@ -719,11 +1031,8 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { // Check that send mode can use local call. - const Map<StringName, RPCMode>::Element *E = p_node->get_node_rset_mode(p_property); - if (E) { - - set_local = _should_call_local(E->get(), is_master, skip_rset); - } + RPCMode rpc_mode = p_node->get_node_rset_mode(p_property); + set_local = _should_call_local(rpc_mode, is_master, skip_rset); if (set_local) { bool valid; @@ -735,12 +1044,12 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const if (!valid) { String error = "rset() aborted in local set, property not found: - " + String(p_property) + "."; - ERR_PRINTS(error); + ERR_PRINT(error); return; } } else if (p_node->get_script_instance()) { // Attempt with script. - RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property); + rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property); set_local = _should_call_local(rpc_mode, is_master, skip_rset); @@ -753,7 +1062,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const if (!valid) { String error = "rset() aborted in local script set, property not found: - " + String(p_property) + "."; - ERR_PRINTS(error); + ERR_PRINT(error); return; } } @@ -984,9 +1293,7 @@ void MultiplayerAPI::_bind_methods() { BIND_ENUM_CONSTANT(RPC_MODE_REMOTE); BIND_ENUM_CONSTANT(RPC_MODE_MASTER); BIND_ENUM_CONSTANT(RPC_MODE_PUPPET); - BIND_ENUM_CONSTANT(RPC_MODE_SLAVE); // Deprecated. BIND_ENUM_CONSTANT(RPC_MODE_REMOTESYNC); - BIND_ENUM_CONSTANT(RPC_MODE_SYNC); // Deprecated. BIND_ENUM_CONSTANT(RPC_MODE_MASTERSYNC); BIND_ENUM_CONSTANT(RPC_MODE_PUPPETSYNC); } diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h index c9f127b6b2..8748dba03c 100644 --- a/core/io/multiplayer_api.h +++ b/core/io/multiplayer_api.h @@ -98,32 +98,44 @@ protected: void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len); void _process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len); void _process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len); - Node *_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len); - void _process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); - void _process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); + Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len); + void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); + void _process_rset(Node *p_node, const uint16_t p_rpc_property_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len); void _send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount); - bool _send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target); + bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target); + + Error _encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len); + Error _decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len); public: enum NetworkCommands { - NETWORK_COMMAND_REMOTE_CALL, + NETWORK_COMMAND_REMOTE_CALL = 0, NETWORK_COMMAND_REMOTE_SET, NETWORK_COMMAND_SIMPLIFY_PATH, NETWORK_COMMAND_CONFIRM_PATH, NETWORK_COMMAND_RAW, }; + enum NetworkNodeIdCompression { + NETWORK_NODE_ID_COMPRESSION_8 = 0, + NETWORK_NODE_ID_COMPRESSION_16, + NETWORK_NODE_ID_COMPRESSION_32, + }; + + enum NetworkNameIdCompression { + NETWORK_NAME_ID_COMPRESSION_8 = 0, + NETWORK_NAME_ID_COMPRESSION_16, + }; + enum RPCMode { RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default) RPC_MODE_REMOTE, // Using rpc() on it will call method / set property in all remote peers RPC_MODE_MASTER, // Using rpc() on it will call method on wherever the master is, be it local or remote RPC_MODE_PUPPET, // Using rpc() on it will call method for all puppets - RPC_MODE_SLAVE = RPC_MODE_PUPPET, // Deprecated, same as puppet RPC_MODE_REMOTESYNC, // Using rpc() on it will call method / set property in all remote peers and locally - RPC_MODE_SYNC = RPC_MODE_REMOTESYNC, // Deprecated. Same as RPC_MODE_REMOTESYNC RPC_MODE_MASTERSYNC, // Using rpc() on it will call method / set property in the master peer and locally RPC_MODE_PUPPETSYNC, // Using rpc() on it will call method / set property in all puppets peers and locally }; diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 9e53d773ba..81d7b86161 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -37,20 +37,9 @@ PacketPeer::PacketPeer() : last_get_error(OK), - allow_object_decoding(false), encode_buffer_max_size(8 * 1024 * 1024) { } -void PacketPeer::set_allow_object_decoding(bool p_enable) { - - allow_object_decoding = p_enable; -} - -bool PacketPeer::is_object_decoding_allowed() const { - - return allow_object_decoding; -} - void PacketPeer::set_encode_buffer_max_size(int p_max_size) { ERR_FAIL_COND_MSG(p_max_size < 1024, "Max encode buffer must be at least 1024 bytes"); @@ -101,13 +90,13 @@ Error PacketPeer::get_var(Variant &r_variant, bool p_allow_objects) { if (err) return err; - return decode_variant(r_variant, buffer, buffer_size, NULL, p_allow_objects || allow_object_decoding); + return decode_variant(r_variant, buffer, buffer_size, NULL, p_allow_objects); } Error PacketPeer::put_var(const Variant &p_packet, bool p_full_objects) { int len; - Error err = encode_variant(p_packet, NULL, len, p_full_objects || allow_object_decoding); // compute len first + Error err = encode_variant(p_packet, NULL, len, p_full_objects); // compute len first if (err) return err; @@ -122,7 +111,7 @@ Error PacketPeer::put_var(const Variant &p_packet, bool p_full_objects) { } PoolVector<uint8_t>::Write w = encode_buffer.write(); - err = encode_variant(p_packet, w.ptr(), len, p_full_objects || allow_object_decoding); + err = encode_variant(p_packet, w.ptr(), len, p_full_objects); ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to encode Variant."); return put_packet(w.ptr(), len); @@ -160,13 +149,10 @@ void PacketPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_packet_error"), &PacketPeer::_get_packet_error); ClassDB::bind_method(D_METHOD("get_available_packet_count"), &PacketPeer::get_available_packet_count); - ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &PacketPeer::set_allow_object_decoding); - ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &PacketPeer::is_object_decoding_allowed); ClassDB::bind_method(D_METHOD("get_encode_buffer_max_size"), &PacketPeer::get_encode_buffer_max_size); ClassDB::bind_method(D_METHOD("set_encode_buffer_max_size", "max_size"), &PacketPeer::set_encode_buffer_max_size); ADD_PROPERTY(PropertyInfo(Variant::INT, "encode_buffer_max_size"), "set_encode_buffer_max_size", "get_encode_buffer_max_size"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed"); }; /***************/ diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index 2b13f2e952..bee69f5ca9 100644 --- a/core/io/packet_peer.h +++ b/core/io/packet_peer.h @@ -49,8 +49,6 @@ class PacketPeer : public Reference { mutable Error last_get_error; - bool allow_object_decoding; - int encode_buffer_max_size; PoolVector<uint8_t> encode_buffer; @@ -69,9 +67,6 @@ public: virtual Error get_var(Variant &r_variant, bool p_allow_objects = false); virtual Error put_var(const Variant &p_packet, bool p_full_objects = false); - void set_allow_object_decoding(bool p_enable); - bool is_object_decoding_allowed() const; - void set_encode_buffer_max_size(int p_max_size); int get_encode_buffer_max_size() const; diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index 5c98c4fcab..a8cfd741bb 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -222,7 +222,7 @@ Error PacketPeerUDP::_poll() { if (rb.space_left() < read + 24) { #ifdef TOOLS_ENABLED - WARN_PRINTS("Buffer full, dropping packets!"); + WARN_PRINT("Buffer full, dropping packets!"); #endif continue; } diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 272d5c1116..02ae5788fc 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -73,13 +73,6 @@ enum { VARIANT_VECTOR2_ARRAY = 37, VARIANT_INT64 = 40, VARIANT_DOUBLE = 41, -#ifndef DISABLE_DEPRECATED - VARIANT_IMAGE = 21, // - no longer variant type - IMAGE_ENCODING_EMPTY = 0, - IMAGE_ENCODING_RAW = 1, - IMAGE_ENCODING_LOSSLESS = 2, - IMAGE_ENCODING_LOSSY = 3, -#endif OBJECT_EMPTY = 0, OBJECT_EXTERNAL_RESOURCE = 1, OBJECT_INTERNAL_RESOURCE = 2, @@ -549,69 +542,6 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { w.release(); r_v = array; } break; -#ifndef DISABLE_DEPRECATED - case VARIANT_IMAGE: { - uint32_t encoding = f->get_32(); - if (encoding == IMAGE_ENCODING_EMPTY) { - r_v = Ref<Image>(); - break; - } else if (encoding == IMAGE_ENCODING_RAW) { - uint32_t width = f->get_32(); - uint32_t height = f->get_32(); - uint32_t mipmaps = f->get_32(); - uint32_t format = f->get_32(); - const uint32_t format_version_shift = 24; - const uint32_t format_version_mask = format_version_shift - 1; - - uint32_t format_version = format >> format_version_shift; - - const uint32_t current_version = 0; - if (format_version > current_version) { - - ERR_PRINT("Format version for encoded binary image is too new."); - return ERR_PARSE_ERROR; - } - - Image::Format fmt = Image::Format(format & format_version_mask); //if format changes, we can add a compatibility bit on top - - uint32_t datalen = f->get_32(); - - PoolVector<uint8_t> imgdata; - imgdata.resize(datalen); - PoolVector<uint8_t>::Write w = imgdata.write(); - f->get_buffer(w.ptr(), datalen); - _advance_padding(datalen); - w.release(); - - Ref<Image> image; - image.instance(); - image->create(width, height, mipmaps, fmt, imgdata); - r_v = image; - - } else { - //compressed - PoolVector<uint8_t> data; - data.resize(f->get_32()); - PoolVector<uint8_t>::Write w = data.write(); - f->get_buffer(w.ptr(), data.size()); - w.release(); - - Ref<Image> image; - - if (encoding == IMAGE_ENCODING_LOSSY && Image::lossy_unpacker) { - - image = Image::lossy_unpacker(data); - } else if (encoding == IMAGE_ENCODING_LOSSLESS && Image::lossless_unpacker) { - - image = Image::lossless_unpacker(data); - } - _advance_padding(data.size()); - - r_v = image; - } - - } break; -#endif default: { ERR_FAIL_V(ERR_FILE_CORRUPT); } break; @@ -836,15 +766,20 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { uint8_t header[4]; f->get_buffer(header, 4); if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') { - //compressed + // Compressed. FileAccessCompressed *fac = memnew(FileAccessCompressed); - fac->open_after_magic(f); + error = fac->open_after_magic(f); + if (error != OK) { + memdelete(fac); + f->close(); + ERR_FAIL_MSG("Failed to open binary resource file: " + local_path + "."); + } f = fac; } else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') { - //not normal - + // Not normal. error = ERR_FILE_UNRECOGNIZED; + f->close(); ERR_FAIL_MSG("Unrecognized binary resource file: " + local_path + "."); } @@ -919,6 +854,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { if (f->eof_reached()) { error = ERR_FILE_CORRUPT; + f->close(); ERR_FAIL_MSG("Premature end of file (EOF): " + local_path + "."); } } @@ -931,14 +867,20 @@ String ResourceInteractiveLoaderBinary::recognize(FileAccess *p_f) { uint8_t header[4]; f->get_buffer(header, 4); if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') { - //compressed + // Compressed. FileAccessCompressed *fac = memnew(FileAccessCompressed); - fac->open_after_magic(f); + error = fac->open_after_magic(f); + if (error != OK) { + memdelete(fac); + f->close(); + return ""; + } f = fac; } else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') { - //not normal + // Not normal. error = ERR_FILE_UNRECOGNIZED; + f->close(); return ""; } @@ -1055,14 +997,19 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons uint8_t header[4]; f->get_buffer(header, 4); if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') { - //compressed + // Compressed. FileAccessCompressed *fac = memnew(FileAccessCompressed); - fac->open_after_magic(f); + Error err = fac->open_after_magic(f); + if (err != OK) { + memdelete(fac); + memdelete(f); + ERR_FAIL_V_MSG(err, "Cannot open file '" + p_path + "'."); + } f = fac; FileAccessCompressed *facw = memnew(FileAccessCompressed); facw->configure("RSCC"); - Error err = facw->_open(p_path + ".depren", FileAccess::WRITE); + err = facw->_open(p_path + ".depren", FileAccess::WRITE); if (err) { memdelete(fac); memdelete(facw); @@ -1072,9 +1019,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons fw = facw; } else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') { - //not normal - - //error=ERR_FILE_UNRECOGNIZED; + // Not normal. memdelete(f); ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unrecognized binary resource file '" + local_path + "'."); } else { @@ -1113,7 +1058,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons memdelete(da); //use the old approach - WARN_PRINTS("This file is old, so it can't refactor dependencies, opening and resaving '" + p_path + "'."); + WARN_PRINT("This file is old, so it can't refactor dependencies, opening and resaving '" + p_path + "'."); Error err; f = FileAccess::open(p_path, FileAccess::READ, &err); @@ -1255,7 +1200,7 @@ String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const ria->res_path = ria->local_path; //ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); String r = ria->recognize(f); - return r; + return ClassDB::get_compatibility_remapped_class(r); } /////////////////////////////////////////////////////////// @@ -1628,14 +1573,14 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant switch (p_variant.get_type()) { case Variant::OBJECT: { - RES res = p_variant.operator RefPtr(); + RES res = p_variant; if (res.is_null() || external_resources.has(res)) return; if (!p_main && (!bundle_resources) && res->get_path().length() && res->get_path().find("::") == -1) { if (res->get_path() == path) { - ERR_PRINTS("Circular reference to resource being saved found: '" + local_path + "' will be null next time it's loaded."); + ERR_PRINT("Circular reference to resource being saved found: '" + local_path + "' will be null next time it's loaded."); return; } int idx = external_resources.size(); diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index 7737006d10..f02dbaa0c2 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -51,7 +51,6 @@ class ResourceInteractiveLoaderBinary : public ResourceInteractiveLoader { Vector<char> str_buf; List<RES> resource_cache; - //Map<int,StringName> string_map; Vector<StringName> string_map; StringName _get_string(); diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index c2d90ed204..f147170ff7 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -74,7 +74,7 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy memdelete(f); return OK; } else if (err != OK) { - ERR_PRINTS("ResourceFormatImporter::load - " + p_path + ".import:" + itos(lines) + " error: " + error_text); + ERR_PRINT("ResourceFormatImporter::load - " + p_path + ".import:" + itos(lines) + " error: " + error_text); memdelete(f); return err; } @@ -279,7 +279,7 @@ void ResourceFormatImporter::get_internal_resource_path_list(const String &p_pat memdelete(f); return; } else if (err != OK) { - ERR_PRINTS("ResourceFormatImporter::get_internal_resource_path_list - " + p_path + ".import:" + itos(lines) + " error: " + error_text); + ERR_PRINT("ResourceFormatImporter::get_internal_resource_path_list - " + p_path + ".import:" + itos(lines) + " error: " + error_text); memdelete(f); return; } diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 7471ab4241..6877f816e1 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -404,6 +404,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p if (!p_no_cache) { _remove_from_loading_map(local_path); } + print_verbose("Failed loading resource: " + path); return RES(); } if (!p_no_cache) @@ -728,8 +729,9 @@ String ResourceLoader::get_resource_type(const String &p_path) { for (int i = 0; i < loader_count; i++) { String result = loader[i]->get_resource_type(local_path); - if (result != "") + if (result != "") { return result; + } } return ""; @@ -815,7 +817,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem if (err == ERR_FILE_EOF) { break; } else if (err != OK) { - ERR_PRINTS("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text + "."); + ERR_PRINT("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text + "."); break; } @@ -953,7 +955,7 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) { ERR_FAIL_COND_V_MSG(obj == NULL, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + "."); ResourceFormatLoader *crl = Object::cast_to<ResourceFormatLoader>(obj); - crl->set_script(s.get_ref_ptr()); + crl->set_script(s); ResourceLoader::add_resource_format_loader(crl); return true; @@ -1013,7 +1015,7 @@ void ResourceLoader::finalize() { #ifndef NO_THREADS const LoadingMapKey *K = NULL; while ((K = loading_map.next(K))) { - ERR_PRINTS("Exited while resource is being loaded: " + K->path); + ERR_PRINT("Exited while resource is being loaded: " + K->path); } loading_map.clear(); memdelete(loading_map_mutex); diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index b468685e4d..685d21107f 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -221,7 +221,7 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) { ERR_FAIL_COND_V_MSG(obj == NULL, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + "."); ResourceFormatSaver *crl = Object::cast_to<ResourceFormatSaver>(obj); - crl->set_script(s.get_ref_ptr()); + crl->set_script(s); ResourceSaver::add_resource_format_saver(crl); return true; diff --git a/core/list.h b/core/list.h index 0796410a84..6250cec598 100644 --- a/core/list.h +++ b/core/list.h @@ -456,17 +456,12 @@ public: Element *I = front(); int c = 0; - while (I) { - - if (c == p_index) { - - return I->get(); - } + while (c < p_index) { I = I->next(); c++; } - CRASH_NOW(); // bug!! + return I->get(); } const T &operator[](int p_index) const { @@ -475,17 +470,12 @@ public: const Element *I = front(); int c = 0; - while (I) { - - if (c == p_index) { - - return I->get(); - } + while (c < p_index) { I = I->next(); c++; } - CRASH_NOW(); // bug!! + return I->get(); } void move_to_back(Element *p_I) { diff --git a/core/make_binders.py b/core/make_binders.py index c38db5cef4..11cfbf6e79 100644 --- a/core/make_binders.py +++ b/core/make_binders.py @@ -342,7 +342,7 @@ def make_version(template, nargs, argmax, const, ret): def run(target, source, env): - versions = 13 + versions = 15 versions_ext = 6 text = "" text_ext = "" diff --git a/core/math/basis.cpp b/core/math/basis.cpp index ddf5f13d55..14079f811d 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -244,6 +244,18 @@ void Basis::scale_local(const Vector3 &p_scale) { *this = scaled_local(p_scale); } +float Basis::get_uniform_scale() const { + return (elements[0].length() + elements[1].length() + elements[2].length()) / 3.0; +} + +void Basis::make_scale_uniform() { + float l = (elements[0].length() + elements[1].length() + elements[2].length()) / 3.0; + for (int i = 0; i < 3; i++) { + elements[i].normalize(); + elements[i] *= l; + } +} + Basis Basis::scaled_local(const Vector3 &p_scale) const { Basis b; b.set_diagonal(p_scale); diff --git a/core/math/basis.h b/core/math/basis.h index 6c3a939d70..0261cf67c6 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -108,6 +108,9 @@ public: void scale_local(const Vector3 &p_scale); Basis scaled_local(const Vector3 &p_scale) const; + void make_scale_uniform(); + float get_uniform_scale() const; + Vector3 get_scale() const; Vector3 get_scale_abs() const; Vector3 get_scale_local() const; diff --git a/core/math/bsp_tree.cpp b/core/math/bsp_tree.cpp index f155d626d7..7ad907db97 100644 --- a/core/math/bsp_tree.cpp +++ b/core/math/bsp_tree.cpp @@ -341,7 +341,7 @@ static int _bsp_create_node(const Face3 *p_faces, const Vector<int> &p_indices, ERR_FAIL_COND_V(p_nodes.size() == BSP_Tree::MAX_NODES, -1); // should not reach here - ERR_FAIL_COND_V(p_indices.size() == 0, -1) + ERR_FAIL_COND_V(p_indices.size() == 0, -1); int ic = p_indices.size(); const int *indices = p_indices.ptr(); diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp index 380bae871a..c4981b954b 100644 --- a/core/math/camera_matrix.cpp +++ b/core/math/camera_matrix.cpp @@ -276,6 +276,36 @@ Vector2 CameraMatrix::get_viewport_half_extents() const { return Vector2(res.x, res.y); } +void CameraMatrix::get_far_plane_size(real_t &r_width, real_t &r_height) const { + + const real_t *matrix = (const real_t *)this->matrix; + ///////--- Far Plane ---/////// + Plane far_plane = Plane(matrix[3] - matrix[2], + matrix[7] - matrix[6], + matrix[11] - matrix[10], + -matrix[15] + matrix[14]); + far_plane.normalize(); + + ///////--- Right Plane ---/////// + Plane right_plane = Plane(matrix[3] - matrix[0], + matrix[7] - matrix[4], + matrix[11] - matrix[8], + -matrix[15] + matrix[12]); + right_plane.normalize(); + + Plane top_plane = Plane(matrix[3] - matrix[1], + matrix[7] - matrix[5], + matrix[11] - matrix[9], + -matrix[15] + matrix[13]); + top_plane.normalize(); + + Vector3 res; + far_plane.intersect_3(right_plane, top_plane, &res); + + r_width = res.x; + r_height = res.y; +} + bool CameraMatrix::get_endpoints(const Transform &p_transform, Vector3 *p_8points) const { Vector<Plane> planes = get_projection_planes(Transform()); @@ -485,6 +515,12 @@ void CameraMatrix::invert() { } } +void CameraMatrix::flip_y() { + for (int i = 0; i < 4; i++) { + matrix[1][i] = -matrix[1][i]; + } +} + CameraMatrix::CameraMatrix() { set_identity(); @@ -506,6 +542,28 @@ CameraMatrix CameraMatrix::operator*(const CameraMatrix &p_matrix) const { return new_matrix; } +void CameraMatrix::set_depth_correction(bool p_flip_y) { + + real_t *m = &matrix[0][0]; + + m[0] = 1; + m[1] = 0.0; + m[2] = 0.0; + m[3] = 0.0; + m[4] = 0.0; + m[5] = p_flip_y ? -1 : 1; + m[6] = 0.0; + m[7] = 0.0; + m[8] = 0.0; + m[9] = 0.0; + m[10] = 0.5; + m[11] = 0.0; + m[12] = 0.0; + m[13] = 0.0; + m[14] = 0.5; + m[15] = 1.0; +} + void CameraMatrix::set_light_bias() { real_t *m = &matrix[0][0]; diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h index 2eed6d25d6..60f7d15974 100644 --- a/core/math/camera_matrix.h +++ b/core/math/camera_matrix.h @@ -50,6 +50,7 @@ struct CameraMatrix { void set_identity(); void set_zero(); void set_light_bias(); + void set_depth_correction(bool p_flip_y = true); void set_light_atlas_rect(const Rect2 &p_rect); void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false); void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist); @@ -74,6 +75,7 @@ struct CameraMatrix { bool get_endpoints(const Transform &p_transform, Vector3 *p_8points) const; Vector2 get_viewport_half_extents() const; + void get_far_plane_size(real_t &r_width, real_t &r_height) const; void invert(); CameraMatrix inverse() const; @@ -90,6 +92,23 @@ struct CameraMatrix { int get_pixels_per_meter(int p_for_pixel_width) const; operator Transform() const; + void flip_y(); + + bool operator==(const CameraMatrix &p_cam) const { + for (uint32_t i = 0; i < 4; i++) { + for (uint32_t j = 0; j < 4; j++) { + if (matrix[i][j] != p_cam.matrix[i][j]) { + return false; + } + } + } + return true; + } + + bool operator!=(const CameraMatrix &p_cam) const { + return !(*this == p_cam); + } + CameraMatrix(); CameraMatrix(const Transform &p_transform); ~CameraMatrix(); diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 655098376c..2fda7a27d5 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -64,7 +64,6 @@ const char *Expression::func_name[Expression::FUNC_MAX] = { "is_nan", "is_inf", "ease", - "decimals", "step_decimals", "stepify", "lerp", @@ -153,7 +152,6 @@ int Expression::get_func_argument_count(BuiltinFunc p_func) { case MATH_EXP: case MATH_ISNAN: case MATH_ISINF: - case MATH_DECIMALS: case MATH_STEP_DECIMALS: case MATH_SEED: case MATH_RANDSEED: @@ -376,11 +374,6 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant VALIDATE_ARG_NUM(1); *r_return = Math::ease((double)*p_inputs[0], (double)*p_inputs[1]); } break; - case MATH_DECIMALS: { - - VALIDATE_ARG_NUM(0); - *r_return = Math::step_decimals((double)*p_inputs[0]); - } break; case MATH_STEP_DECIMALS: { VALIDATE_ARG_NUM(0); @@ -1036,8 +1029,7 @@ Error Expression::_get_token(Token &r_token) { case 'f': res = 12; break; case 'r': res = 13; break; case 'u': { - //hexnumbarh - oct is deprecated - + // hex number for (int j = 0; j < 4; j++) { CharType c = GET_CHAR(); @@ -1062,7 +1054,7 @@ Error Expression::_get_token(Token &r_token) { v = c - 'A'; v += 10; } else { - ERR_PRINT("BUG"); + ERR_PRINT("Bug parsing hex constant."); v = 0; } @@ -1071,13 +1063,8 @@ Error Expression::_get_token(Token &r_token) { } } break; - //case '\"': res='\"'; break; - //case '\\': res='\\'; break; - //case '/': res='/'; break; default: { res = next; - //r_err_str="Invalid escape sequence"; - //return ERR_PARSE_ERROR; } break; } diff --git a/core/math/expression.h b/core/math/expression.h index c5b9d79a16..1cd1415dcf 100644 --- a/core/math/expression.h +++ b/core/math/expression.h @@ -63,7 +63,6 @@ public: MATH_ISNAN, MATH_ISINF, MATH_EASE, - MATH_DECIMALS, MATH_STEP_DECIMALS, MATH_STEPIFY, MATH_LERP, diff --git a/core/math/rect2.h b/core/math/rect2.h index 9017377770..0d2e7eb6e5 100644 --- a/core/math/rect2.h +++ b/core/math/rect2.h @@ -60,6 +60,19 @@ struct Rect2 { return true; } + inline bool intersects_touch(const Rect2 &p_rect) const { + if (position.x > (p_rect.position.x + p_rect.size.width)) + return false; + if ((position.x + size.width) < p_rect.position.x) + return false; + if (position.y > (p_rect.position.y + p_rect.size.height)) + return false; + if ((position.y + size.height) < p_rect.position.y) + return false; + + return true; + } + inline real_t distance_to(const Vector2 &p_point) const { real_t dist = 0.0; diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index 71ff79c0fc..353b2acd16 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -103,7 +103,7 @@ Vector3 Vector3::cubic_interpolaten(const Vector3 &p_b, const Vector3 &p_pre_a, Vector3 out; out = 0.5 * ((p1 * 2.0) + (-p0 + p2) * t + - (2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 + + (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 + (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3); return out; } @@ -122,7 +122,7 @@ Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, c Vector3 out; out = 0.5 * ((p1 * 2.0) + (-p0 + p2) * t + - (2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 + + (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 + (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3); return out; } diff --git a/core/math/vector3.h b/core/math/vector3.h index 4ad3017109..3bf8644af9 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -32,6 +32,7 @@ #define VECTOR3_H #include "core/math/math_funcs.h" +#include "core/math/vector3i.h" #include "core/ustring.h" class Basis; @@ -147,6 +148,15 @@ struct Vector3 { _FORCE_INLINE_ bool operator>=(const Vector3 &p_v) const; operator String() const; + _FORCE_INLINE_ operator Vector3i() const { + return Vector3i(x, y, z); + } + + _FORCE_INLINE_ Vector3(const Vector3i &p_ivec) { + x = p_ivec.x; + y = p_ivec.y; + z = p_ivec.z; + } _FORCE_INLINE_ Vector3(real_t p_x, real_t p_y, real_t p_z) { x = p_x; diff --git a/core/math/vector3i.cpp b/core/math/vector3i.cpp new file mode 100644 index 0000000000..8a4ddf03b9 --- /dev/null +++ b/core/math/vector3i.cpp @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* vector3i.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "vector3i.h" + +void Vector3i::set_axis(int p_axis, int32_t p_value) { + ERR_FAIL_INDEX(p_axis, 3); + coord[p_axis] = p_value; +} +int32_t Vector3i::get_axis(int p_axis) const { + + ERR_FAIL_INDEX_V(p_axis, 3, 0); + return operator[](p_axis); +} + +int Vector3i::min_axis() const { + + return x < y ? (x < z ? 0 : 2) : (y < z ? 1 : 2); +} +int Vector3i::max_axis() const { + + return x < y ? (y < z ? 2 : 1) : (x < z ? 2 : 0); +} + +Vector3i::operator String() const { + + return (itos(x) + ", " + itos(y) + ", " + itos(z)); +} diff --git a/core/math/vector3i.h b/core/math/vector3i.h new file mode 100644 index 0000000000..6f9754d3b9 --- /dev/null +++ b/core/math/vector3i.h @@ -0,0 +1,272 @@ +/*************************************************************************/ +/* vector3i.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef VECTOR3I_H +#define VECTOR3I_H + +#include "core/typedefs.h" +#include "core/ustring.h" + +struct Vector3i { + + enum Axis { + AXIS_X, + AXIS_Y, + AXIS_Z, + }; + + union { + struct { + int32_t x; + int32_t y; + int32_t z; + }; + + int32_t coord[3]; + }; + + _FORCE_INLINE_ const int32_t &operator[](int p_axis) const { + + return coord[p_axis]; + } + + _FORCE_INLINE_ int32_t &operator[](int p_axis) { + + return coord[p_axis]; + } + + void set_axis(int p_axis, int32_t p_value); + int32_t get_axis(int p_axis) const; + + int min_axis() const; + int max_axis() const; + + _FORCE_INLINE_ void zero(); + + _FORCE_INLINE_ Vector3i abs() const; + _FORCE_INLINE_ Vector3i sign() const; + + /* Operators */ + + _FORCE_INLINE_ Vector3i &operator+=(const Vector3i &p_v); + _FORCE_INLINE_ Vector3i operator+(const Vector3i &p_v) const; + _FORCE_INLINE_ Vector3i &operator-=(const Vector3i &p_v); + _FORCE_INLINE_ Vector3i operator-(const Vector3i &p_v) const; + _FORCE_INLINE_ Vector3i &operator*=(const Vector3i &p_v); + _FORCE_INLINE_ Vector3i operator*(const Vector3i &p_v) const; + _FORCE_INLINE_ Vector3i &operator/=(const Vector3i &p_v); + _FORCE_INLINE_ Vector3i operator/(const Vector3i &p_v) const; + + _FORCE_INLINE_ Vector3i &operator*=(int32_t p_scalar); + _FORCE_INLINE_ Vector3i operator*(int32_t p_scalar) const; + _FORCE_INLINE_ Vector3i &operator/=(int32_t p_scalar); + _FORCE_INLINE_ Vector3i operator/(int32_t p_scalar) const; + + _FORCE_INLINE_ Vector3i operator-() const; + + _FORCE_INLINE_ bool operator==(const Vector3i &p_v) const; + _FORCE_INLINE_ bool operator!=(const Vector3i &p_v) const; + _FORCE_INLINE_ bool operator<(const Vector3i &p_v) const; + _FORCE_INLINE_ bool operator<=(const Vector3i &p_v) const; + _FORCE_INLINE_ bool operator>(const Vector3i &p_v) const; + _FORCE_INLINE_ bool operator>=(const Vector3i &p_v) const; + + operator String() const; + + _FORCE_INLINE_ Vector3i(int32_t p_x, int32_t p_y, int32_t p_z) { + x = p_x; + y = p_y; + z = p_z; + } + _FORCE_INLINE_ Vector3i() { x = y = z = 0; } +}; + +Vector3i Vector3i::abs() const { + + return Vector3i(ABS(x), ABS(y), ABS(z)); +} + +Vector3i Vector3i::sign() const { + + return Vector3i(SGN(x), SGN(y), SGN(z)); +} + +/* Operators */ + +Vector3i &Vector3i::operator+=(const Vector3i &p_v) { + + x += p_v.x; + y += p_v.y; + z += p_v.z; + return *this; +} + +Vector3i Vector3i::operator+(const Vector3i &p_v) const { + + return Vector3i(x + p_v.x, y + p_v.y, z + p_v.z); +} + +Vector3i &Vector3i::operator-=(const Vector3i &p_v) { + + x -= p_v.x; + y -= p_v.y; + z -= p_v.z; + return *this; +} +Vector3i Vector3i::operator-(const Vector3i &p_v) const { + + return Vector3i(x - p_v.x, y - p_v.y, z - p_v.z); +} + +Vector3i &Vector3i::operator*=(const Vector3i &p_v) { + + x *= p_v.x; + y *= p_v.y; + z *= p_v.z; + return *this; +} +Vector3i Vector3i::operator*(const Vector3i &p_v) const { + + return Vector3i(x * p_v.x, y * p_v.y, z * p_v.z); +} + +Vector3i &Vector3i::operator/=(const Vector3i &p_v) { + + x /= p_v.x; + y /= p_v.y; + z /= p_v.z; + return *this; +} + +Vector3i Vector3i::operator/(const Vector3i &p_v) const { + + return Vector3i(x / p_v.x, y / p_v.y, z / p_v.z); +} + +Vector3i &Vector3i::operator*=(int32_t p_scalar) { + + x *= p_scalar; + y *= p_scalar; + z *= p_scalar; + return *this; +} + +_FORCE_INLINE_ Vector3i operator*(int32_t p_scalar, const Vector3i &p_vec) { + + return p_vec * p_scalar; +} + +Vector3i Vector3i::operator*(int32_t p_scalar) const { + + return Vector3i(x * p_scalar, y * p_scalar, z * p_scalar); +} + +Vector3i &Vector3i::operator/=(int32_t p_scalar) { + + x /= p_scalar; + y /= p_scalar; + z /= p_scalar; + return *this; +} + +Vector3i Vector3i::operator/(int32_t p_scalar) const { + + return Vector3i(x / p_scalar, y / p_scalar, z / p_scalar); +} + +Vector3i Vector3i::operator-() const { + + return Vector3i(-x, -y, -z); +} + +bool Vector3i::operator==(const Vector3i &p_v) const { + + return (x == p_v.x && y == p_v.y && z == p_v.z); +} + +bool Vector3i::operator!=(const Vector3i &p_v) const { + + return (x != p_v.x || y != p_v.y || z != p_v.z); +} + +bool Vector3i::operator<(const Vector3i &p_v) const { + + if (x == p_v.x) { + if (y == p_v.y) + return z < p_v.z; + else + return y < p_v.y; + } else { + return x < p_v.x; + } +} + +bool Vector3i::operator>(const Vector3i &p_v) const { + + if (x == p_v.x) { + if (y == p_v.y) + return z > p_v.z; + else + return y > p_v.y; + } else { + return x > p_v.x; + } +} + +bool Vector3i::operator<=(const Vector3i &p_v) const { + + if (x == p_v.x) { + if (y == p_v.y) + return z <= p_v.z; + else + return y < p_v.y; + } else { + return x < p_v.x; + } +} + +bool Vector3i::operator>=(const Vector3i &p_v) const { + + if (x == p_v.x) { + if (y == p_v.y) + return z >= p_v.z; + else + return y > p_v.y; + } else { + return x > p_v.x; + } +} + +void Vector3i::zero() { + + x = y = z = 0; +} + +#endif // VECTOR3I_H diff --git a/core/message_queue.cpp b/core/message_queue.cpp index 64ceec5ee4..42390935d4 100644 --- a/core/message_queue.cpp +++ b/core/message_queue.cpp @@ -250,7 +250,7 @@ void MessageQueue::_call_function(Object *p_target, const StringName &p_func, co p_target->call(p_func, argptrs, p_argcount, ce); if (p_show_error && ce.error != Variant::CallError::CALL_OK) { - ERR_PRINTS("Error calling deferred method: " + Variant::get_call_error_text(p_target, p_func, argptrs, p_argcount, ce) + "."); + ERR_PRINT("Error calling deferred method: " + Variant::get_call_error_text(p_target, p_func, argptrs, p_argcount, ce) + "."); } } diff --git a/core/method_ptrcall.h b/core/method_ptrcall.h index 0f2458d982..118970de80 100644 --- a/core/method_ptrcall.h +++ b/core/method_ptrcall.h @@ -32,6 +32,7 @@ #define METHOD_PTRCALL_H #include "core/math/transform_2d.h" +#include "core/object_id.h" #include "core/typedefs.h" #include "core/variant.h" @@ -167,6 +168,21 @@ struct PtrToArg<const T *> { } }; +//this is for ObjectID + +template <> +struct PtrToArg<ObjectID> { + _FORCE_INLINE_ static const ObjectID convert(const void *p_ptr) { + + return ObjectID(*reinterpret_cast<const uint64_t *>(p_ptr)); + } + + _FORCE_INLINE_ static void encode(const ObjectID &p_val, void *p_ptr) { + + *((uint64_t *)p_ptr) = p_val; + } +}; + //this is for the special cases used by Variant #define MAKE_VECARG(m_type) \ diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h index 7407c52816..182ed8b116 100644 --- a/core/oa_hash_map.h +++ b/core/oa_hash_map.h @@ -240,6 +240,22 @@ public: return false; } + /** + * returns true if the value was found, false otherwise. + * + * if r_data is not NULL then the value will be written to the object + * it points to. + */ + TValue *lookup_ptr(const TKey &p_key) const { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + + if (exists) { + return &values[pos]; + } + return NULL; + } + _FORCE_INLINE_ bool has(const TKey &p_key) const { uint32_t _pos = 0; return _lookup_pos(p_key, _pos); diff --git a/core/object.cpp b/core/object.cpp index 21a3b2cc6c..9a5cfe5c22 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -968,7 +968,7 @@ void Object::cancel_delete() { _predelete_ok = true; } -void Object::set_script_and_instance(const RefPtr &p_script, ScriptInstance *p_instance) { +void Object::set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance) { //this function is not meant to be used in any of these ways ERR_FAIL_COND(p_script.is_null()); @@ -979,7 +979,7 @@ void Object::set_script_and_instance(const RefPtr &p_script, ScriptInstance *p_i script_instance = p_instance; } -void Object::set_script(const RefPtr &p_script) { +void Object::set_script(const Variant &p_script) { if (script == p_script) return; @@ -990,7 +990,7 @@ void Object::set_script(const RefPtr &p_script) { } script = p_script; - Ref<Script> s(script); + Ref<Script> s = script; if (!s.is_null()) { if (s->can_instance()) { @@ -1017,12 +1017,12 @@ void Object::set_script_instance(ScriptInstance *p_instance) { script_instance = p_instance; if (p_instance) - script = p_instance->get_script().get_ref_ptr(); + script = p_instance->get_script(); else - script = RefPtr(); + script = Variant(); } -RefPtr Object::get_script() const { +Variant Object::get_script() const { return script; } @@ -1225,7 +1225,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int if (ce.error == Variant::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) { //most likely object is not initialized yet, do not throw error. } else { - ERR_PRINTS("Error calling method from signal '" + String(p_name) + "': " + Variant::get_call_error_text(target, c.method, args, argc, ce) + "."); + ERR_PRINT("Error calling method from signal '" + String(p_name) + "': " + Variant::get_call_error_text(target, c.method, args, argc, ce) + "."); err = ERR_METHOD_NOT_FOUND; } } @@ -1911,12 +1911,11 @@ void Object::set_script_instance_binding(int p_script_language_index, void *p_da _script_instance_bindings[p_script_language_index] = p_data; } -Object::Object() { - +void Object::_construct_object(bool p_reference) { + type_is_reference = p_reference; _class_ptr = NULL; _block_signals = false; _predelete_ok = 0; - _instance_id = 0; _instance_id = ObjectDB::add_instance(this); _can_translate = true; _is_queued_for_deletion = false; @@ -1934,6 +1933,14 @@ Object::Object() { _lock_index.init(1); #endif } +Object::Object(bool p_reference) { + _construct_object(p_reference); +} + +Object::Object() { + + _construct_object(false); +} Object::~Object() { @@ -1945,7 +1952,7 @@ Object::~Object() { if (_emitting) { //@todo this may need to actually reach the debugger prioritarily somehow because it may crash before - ERR_PRINTS("Object " + to_string() + " was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes."); + ERR_PRINT("Object " + to_string() + " was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes."); } while ((S = signal_map.next(NULL))) { @@ -1972,7 +1979,7 @@ Object::~Object() { } ObjectDB::remove_instance(this); - _instance_id = 0; + _instance_id = ObjectID(); _predelete_ok = 2; if (!ScriptServer::are_languages_finished()) { @@ -1994,96 +2001,139 @@ void postinitialize_handler(Object *p_object) { p_object->_postinitialize(); } -HashMap<ObjectID, Object *> ObjectDB::instances; -ObjectID ObjectDB::instance_counter = 1; -HashMap<Object *, ObjectID, ObjectDB::ObjectPtrHash> ObjectDB::instance_checks; -ObjectID ObjectDB::add_instance(Object *p_object) { - - ERR_FAIL_COND_V(p_object->get_instance_id() != 0, 0); - - rw_lock->write_lock(); - ObjectID instance_id = ++instance_counter; - instances[instance_id] = p_object; - instance_checks[p_object] = instance_id; - - rw_lock->write_unlock(); +void ObjectDB::debug_objects(DebugFunc p_func) { - return instance_id; + spin_lock.lock(); + for (uint32_t i = 0; i < slot_count; i++) { + uint32_t slot = object_slots[i].next_free; + p_func(object_slots[slot].object); + } + spin_lock.unlock(); } -void ObjectDB::remove_instance(Object *p_object) { +void Object::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { +} - rw_lock->write_lock(); +SpinLock ObjectDB::spin_lock; +uint32_t ObjectDB::slot_count = 0; +uint32_t ObjectDB::slot_max = 0; +ObjectDB::ObjectSlot *ObjectDB::object_slots = nullptr; +uint64_t ObjectDB::validator_counter = 0; - instances.erase(p_object->get_instance_id()); - instance_checks.erase(p_object); +int ObjectDB::get_object_count() { - rw_lock->write_unlock(); + return slot_count; } -Object *ObjectDB::get_instance(ObjectID p_instance_id) { - rw_lock->read_lock(); - Object **obj = instances.getptr(p_instance_id); - rw_lock->read_unlock(); +ObjectID ObjectDB::add_instance(Object *p_object) { - if (!obj) - return NULL; - return *obj; -} + spin_lock.lock(); + if (unlikely(slot_count == slot_max)) { -void ObjectDB::debug_objects(DebugFunc p_func) { + CRASH_COND(slot_count == (1 << OBJECTDB_SLOT_MAX_COUNT_BITS)); + + uint32_t new_slot_max = slot_max > 0 ? slot_max * 2 : 1; + object_slots = (ObjectSlot *)memrealloc(object_slots, sizeof(ObjectSlot) * new_slot_max); + for (uint32_t i = slot_max; i < new_slot_max; i++) { + object_slots[i].object = nullptr; + object_slots[i].is_reference = false; + object_slots[i].next_free = i; + object_slots[i].validator = 0; + } + slot_max = new_slot_max; + } - rw_lock->read_lock(); + uint32_t slot = object_slots[slot_count].next_free; + if (object_slots[slot].object != nullptr) { + spin_lock.unlock(); + ERR_FAIL_COND_V(object_slots[slot].object != nullptr, ObjectID()); + } + object_slots[slot].object = p_object; + object_slots[slot].is_reference = p_object->is_reference(); + validator_counter = (validator_counter + 1) & OBJECTDB_VALIDATOR_MASK; + if (unlikely(validator_counter == 0)) { + validator_counter = 1; + } + object_slots[slot].validator = validator_counter; - const ObjectID *K = NULL; - while ((K = instances.next(K))) { + uint64_t id = validator_counter; + id <<= OBJECTDB_SLOT_MAX_COUNT_BITS; + id |= uint64_t(slot); - p_func(instances[*K]); + if (p_object->is_reference()) { + id |= OBJECTDB_REFERENCE_BIT; } - rw_lock->read_unlock(); -} + slot_count++; -void Object::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + spin_lock.unlock(); + + return ObjectID(id); } -int ObjectDB::get_object_count() { +void ObjectDB::remove_instance(Object *p_object) { + uint64_t t = p_object->get_instance_id(); + uint32_t slot = t & OBJECTDB_SLOT_MAX_COUNT_MASK; //slot is always valid on valid object - rw_lock->read_lock(); - int count = instances.size(); - rw_lock->read_unlock(); + spin_lock.lock(); - return count; -} +#ifdef DEBUG_ENABLED -RWLock *ObjectDB::rw_lock = NULL; + if (object_slots[slot].object != p_object) { + spin_lock.unlock(); + ERR_FAIL_COND(object_slots[slot].object != p_object); + } + { + uint64_t validator = (t >> OBJECTDB_SLOT_MAX_COUNT_BITS) & OBJECTDB_VALIDATOR_MASK; + if (object_slots[slot].validator != validator) { + spin_lock.unlock(); + ERR_FAIL_COND(object_slots[slot].validator != validator); + } + } + +#endif + //decrease slot count + slot_count--; + //set the free slot properly + object_slots[slot_count].next_free = slot; + //invalidate, so checks against it fail + object_slots[slot].validator = 0; + object_slots[slot].is_reference = false; + object_slots[slot].object = nullptr; + + spin_lock.unlock(); +} void ObjectDB::setup() { - rw_lock = RWLock::create(); + //nothing to do now } void ObjectDB::cleanup() { - rw_lock->write_lock(); - if (instances.size()) { + if (slot_count > 0) { + spin_lock.lock(); WARN_PRINT("ObjectDB Instances still exist!"); if (OS::get_singleton()->is_stdout_verbose()) { - const ObjectID *K = NULL; - while ((K = instances.next(K))) { + for (uint32_t i = 0; i < slot_count; i++) { + uint32_t slot = object_slots[i].next_free; + Object *obj = object_slots[slot].object; String node_name; - if (instances[*K]->is_class("Node")) - node_name = " - Node name: " + String(instances[*K]->call("get_name")); - if (instances[*K]->is_class("Resource")) - node_name = " - Resource name: " + String(instances[*K]->call("get_name")) + " Path: " + String(instances[*K]->call("get_path")); - print_line("Leaked instance: " + String(instances[*K]->get_class()) + ":" + itos(*K) + node_name); + if (obj->is_class("Node")) + node_name = " - Node name: " + String(obj->call("get_name")); + if (obj->is_class("Resource")) + node_name = " - Resource name: " + String(obj->call("get_name")) + " Path: " + String(obj->call("get_path")); + + uint64_t id = uint64_t(slot) | (uint64_t(object_slots[slot].validator) << OBJECTDB_VALIDATOR_BITS) | (object_slots[slot].is_reference ? OBJECTDB_REFERENCE_BIT : 0); + print_line("Leaked instance: " + String(obj->get_class()) + ":" + itos(id) + node_name); } } + spin_lock.unlock(); + } + + if (object_slots) { + memfree(object_slots); } - instances.clear(); - instance_checks.clear(); - rw_lock->write_unlock(); - memdelete(rw_lock); } diff --git a/core/object.h b/core/object.h index 865c155764..6a229afaea 100644 --- a/core/object.h +++ b/core/object.h @@ -34,8 +34,10 @@ #include "core/hash_map.h" #include "core/list.h" #include "core/map.h" +#include "core/object_id.h" #include "core/os/rw_lock.h" #include "core/set.h" +#include "core/spin_lock.h" #include "core/variant.h" #include "core/vmap.h" @@ -89,6 +91,7 @@ enum PropertyHint { PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send PROPERTY_HINT_NODE_PATH_VALID_TYPES, PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog + PROPERTY_HINT_INT_IS_OBJECTID, PROPERTY_HINT_MAX, // When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit }; @@ -397,7 +400,6 @@ public: \ private: class ScriptInstance; -typedef uint64_t ObjectID; class Object { public: @@ -452,7 +454,7 @@ private: _id(p_id), method(p_method) { } - Target() { _id = 0; } + Target() { _id = ObjectID(); } }; struct Slot { @@ -487,7 +489,7 @@ private: Set<String> editor_section_folding; #endif ScriptInstance *script_instance; - RefPtr script; + Variant script; //reference does not yet exist, store it in a Dictionary metadata; mutable StringName _class_name; mutable const StringName *_class_ptr; @@ -505,9 +507,13 @@ private: void property_list_changed_notify(); + _FORCE_INLINE_ void _construct_object(bool p_reference); + friend class Reference; + bool type_is_reference = false; uint32_t instance_binding_count; void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS]; + Object(bool p_reference); protected: virtual void _initialize_classv() { initialize_class(); } @@ -679,8 +685,8 @@ public: /* SCRIPT */ - void set_script(const RefPtr &p_script); - RefPtr get_script() const; + void set_script(const Variant &p_script); + Variant get_script() const; /* SCRIPT */ @@ -699,7 +705,7 @@ public: void set_script_instance(ScriptInstance *p_instance); _FORCE_INLINE_ ScriptInstance *get_script_instance() const { return script_instance; } - void set_script_and_instance(const RefPtr &p_script, ScriptInstance *p_instance); //some script languages can't control instance creation, so this function eases the process + void set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance); //some script languages can't control instance creation, so this function eases the process void add_user_signal(const MethodInfo &p_signal); Error emit_signal(const StringName &p_name, VARIANT_ARG_LIST); @@ -750,6 +756,7 @@ public: void clear_internal_resource_paths(); + _ALWAYS_INLINE_ bool is_reference() const { return type_is_reference; } Object(); virtual ~Object(); }; @@ -759,49 +766,63 @@ void postinitialize_handler(Object *p_object); class ObjectDB { - struct ObjectPtrHash { - - static _FORCE_INLINE_ uint32_t hash(const Object *p_obj) { - - union { - const Object *p; - unsigned long i; - } u; - u.p = p_obj; - return HashMapHasherDefault::hash((uint64_t)u.i); - } +//this needs to add up to 63, 1 bit is for reference +#define OBJECTDB_VALIDATOR_BITS 39 +#define OBJECTDB_VALIDATOR_MASK ((uint64_t(1) << OBJECTDB_VALIDATOR_BITS) - 1) +#define OBJECTDB_SLOT_MAX_COUNT_BITS 24 +#define OBJECTDB_SLOT_MAX_COUNT_MASK ((uint64_t(1) << OBJECTDB_SLOT_MAX_COUNT_BITS) - 1) +#define OBJECTDB_REFERENCE_BIT (uint64_t(1) << (OBJECTDB_SLOT_MAX_COUNT_BITS + OBJECTDB_VALIDATOR_BITS)) + + struct ObjectSlot { //128 bits per slot + uint64_t validator : OBJECTDB_VALIDATOR_BITS; + uint64_t next_free : OBJECTDB_SLOT_MAX_COUNT_BITS; + uint64_t is_reference : 1; + Object *object; }; - static HashMap<ObjectID, Object *> instances; - static HashMap<Object *, ObjectID, ObjectPtrHash> instance_checks; + static SpinLock spin_lock; + static uint32_t slot_count; + static uint32_t slot_max; + static ObjectSlot *object_slots; + static uint64_t validator_counter; - static ObjectID instance_counter; friend class Object; friend void unregister_core_types(); - - static RWLock *rw_lock; static void cleanup(); + static ObjectID add_instance(Object *p_object); static void remove_instance(Object *p_object); + friend void register_core_types(); static void setup(); public: typedef void (*DebugFunc)(Object *p_obj); - static Object *get_instance(ObjectID p_instance_id); - static void debug_objects(DebugFunc p_func); - static int get_object_count(); + _ALWAYS_INLINE_ static Object *get_instance(ObjectID p_instance_id) { + + uint64_t id = p_instance_id; + uint32_t slot = id & OBJECTDB_SLOT_MAX_COUNT_MASK; + + ERR_FAIL_COND_V(slot >= slot_max, nullptr); //this should never happen unless RID is corrupted + + spin_lock.lock(); - _FORCE_INLINE_ static bool instance_validate(Object *p_ptr) { - rw_lock->read_lock(); + uint64_t validator = (id >> OBJECTDB_SLOT_MAX_COUNT_BITS) & OBJECTDB_VALIDATOR_MASK; - bool exists = instance_checks.has(p_ptr); + if (unlikely(object_slots[slot].validator != validator)) { + spin_lock.unlock(); + return nullptr; + } + + Object *object = object_slots[slot].object; - rw_lock->read_unlock(); + spin_lock.unlock(); - return exists; + return object; } + static void debug_objects(DebugFunc p_func); + static int get_object_count(); }; //needed by macros diff --git a/core/object_id.h b/core/object_id.h new file mode 100644 index 0000000000..6ab1a3031a --- /dev/null +++ b/core/object_id.h @@ -0,0 +1,33 @@ +#ifndef OBJECT_ID_H +#define OBJECT_ID_H + +#include "core/typedefs.h" + +// Class to store an object ID (int64) +// needs to be compatile with int64 because this is what Variant uses +// Also, need to be explicitly only castable to 64 bits integer types +// to avoid bugs due to loss of precision + +class ObjectID { + uint64_t id = 0; + +public: + _ALWAYS_INLINE_ bool is_reference() const { return (id & (uint64_t(1) << 63)) != 0; } + _ALWAYS_INLINE_ bool is_valid() const { return id != 0; } + _ALWAYS_INLINE_ bool is_null() const { return id == 0; } + _ALWAYS_INLINE_ operator uint64_t() const { return id; } + _ALWAYS_INLINE_ operator int64_t() const { return id; } + + _ALWAYS_INLINE_ bool operator==(const ObjectID &p_id) const { return id == p_id.id; } + _ALWAYS_INLINE_ bool operator!=(const ObjectID &p_id) const { return id != p_id.id; } + _ALWAYS_INLINE_ bool operator<(const ObjectID &p_id) const { return id < p_id.id; } + + _ALWAYS_INLINE_ void operator=(int64_t p_int64) { id = p_int64; } + _ALWAYS_INLINE_ void operator=(uint64_t p_uint64) { id = p_uint64; } + + _ALWAYS_INLINE_ ObjectID() {} + _ALWAYS_INLINE_ explicit ObjectID(const uint64_t p_id) { id = p_id; } + _ALWAYS_INLINE_ explicit ObjectID(const int64_t p_id) { id = p_id; } +}; + +#endif // OBJECT_ID_H diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp index 0477db82be..f65fc00077 100644 --- a/core/os/dir_access.cpp +++ b/core/os/dir_access.cpp @@ -285,7 +285,7 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) { FileAccess *fsrc = FileAccess::open(p_from, FileAccess::READ, &err); if (err) { - ERR_PRINTS("Failed to open " + p_from); + ERR_PRINT("Failed to open " + p_from); return err; } @@ -294,7 +294,7 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) { fsrc->close(); memdelete(fsrc); - ERR_PRINTS("Failed to open " + p_to); + ERR_PRINT("Failed to open " + p_to); return err; } diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp index 5ecdd74a4b..6020c4b219 100644 --- a/core/os/main_loop.cpp +++ b/core/os/main_loop.cpp @@ -95,7 +95,7 @@ void MainLoop::input_event(const Ref<InputEvent> &p_event) { void MainLoop::init() { if (init_script.is_valid()) - set_script(init_script.get_ref_ptr()); + set_script(init_script); if (get_script_instance()) get_script_instance()->call("_initialize"); @@ -131,6 +131,6 @@ void MainLoop::finish() { if (get_script_instance()) { get_script_instance()->call("_finalize"); - set_script(RefPtr()); //clear script + set_script(Variant()); //clear script } } diff --git a/core/os/os.cpp b/core/os/os.cpp index 1ed9484208..d1b6ccab54 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -188,7 +188,7 @@ int OS::get_process_id() const { void OS::vibrate_handheld(int p_duration_ms) { - WARN_PRINTS("vibrate_handheld() only works with Android and iOS"); + WARN_PRINT("vibrate_handheld() only works with Android and iOS"); } bool OS::is_stdout_verbose() const { @@ -586,16 +586,6 @@ bool OS::is_vsync_via_compositor_enabled() const { return _vsync_via_compositor; } -OS::PowerState OS::get_power_state() { - return POWERSTATE_UNKNOWN; -} -int OS::get_power_seconds_left() { - return -1; -} -int OS::get_power_percent_left() { - return -1; -} - void OS::set_has_server_feature_callback(HasServerFeatureCallback p_callback) { has_server_feature_callback = p_callback; @@ -691,9 +681,9 @@ const char *OS::get_video_driver_name(int p_driver) const { switch (p_driver) { case VIDEO_DRIVER_GLES2: return "GLES2"; - case VIDEO_DRIVER_GLES3: + case VIDEO_DRIVER_VULKAN: default: - return "GLES3"; + return "Vulkan"; } } diff --git a/core/os/os.h b/core/os/os.h index 89b3414b3e..e4661e4583 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -78,14 +78,6 @@ public: typedef void (*ImeCallback)(void *p_inp, String p_text, Point2 p_selection); typedef bool (*HasServerFeatureCallback)(const String &p_feature); - enum PowerState { - POWERSTATE_UNKNOWN, /**< cannot determine power status */ - POWERSTATE_ON_BATTERY, /**< Not plugged in, running on the battery */ - POWERSTATE_NO_BATTERY, /**< Plugged in, no battery available */ - POWERSTATE_CHARGING, /**< Plugged in, charging battery */ - POWERSTATE_CHARGED /**< Plugged in, battery charged */ - }; - enum RenderThreadMode { RENDER_THREAD_UNSAFE, @@ -181,7 +173,7 @@ public: virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const = 0; enum VideoDriver { - VIDEO_DRIVER_GLES3, + VIDEO_DRIVER_VULKAN, VIDEO_DRIVER_GLES2, VIDEO_DRIVER_MAX, }; @@ -517,10 +509,6 @@ public: void set_vsync_via_compositor(bool p_enable); bool is_vsync_via_compositor_enabled() const; - virtual OS::PowerState get_power_state(); - virtual int get_power_seconds_left(); - virtual int get_power_percent_left(); - virtual void force_process_input(){}; bool has_feature(const String &p_feature); @@ -542,6 +530,4 @@ public: virtual ~OS(); }; -VARIANT_ENUM_CAST(OS::PowerState); - #endif diff --git a/core/os/semaphore.cpp b/core/os/semaphore.cpp index b2ba9716f0..2c20f234d0 100644 --- a/core/os/semaphore.cpp +++ b/core/os/semaphore.cpp @@ -32,14 +32,14 @@ #include "core/error_macros.h" -Semaphore *(*Semaphore::create_func)() = 0; +SemaphoreOld *(*SemaphoreOld::create_func)() = 0; -Semaphore *Semaphore::create() { +SemaphoreOld *SemaphoreOld::create() { ERR_FAIL_COND_V(!create_func, 0); return create_func(); } -Semaphore::~Semaphore() { +SemaphoreOld::~SemaphoreOld() { } diff --git a/core/os/semaphore.h b/core/os/semaphore.h index 9f3c0f549c..f16a15a6db 100644 --- a/core/os/semaphore.h +++ b/core/os/semaphore.h @@ -32,19 +32,53 @@ #define SEMAPHORE_H #include "core/error_list.h" +#include "core/typedefs.h" + +#include <condition_variable> +#include <mutex> class Semaphore { +private: + std::mutex mutex_; + std::condition_variable condition_; + unsigned long count_ = 0; // Initialized as locked. + +public: + _ALWAYS_INLINE_ void post() { + std::lock_guard<decltype(mutex_)> lock(mutex_); + ++count_; + condition_.notify_one(); + } + + _ALWAYS_INLINE_ void wait() { + std::unique_lock<decltype(mutex_)> lock(mutex_); + while (!count_) // Handle spurious wake-ups. + condition_.wait(lock); + --count_; + } + + _ALWAYS_INLINE_ bool try_wait() { + std::lock_guard<decltype(mutex_)> lock(mutex_); + if (count_) { + --count_; + return true; + } + return false; + } +}; + +class SemaphoreOld { protected: - static Semaphore *(*create_func)(); + static SemaphoreOld *(*create_func)(); public: virtual Error wait() = 0; ///< wait until semaphore has positive value, then decrement and pass virtual Error post() = 0; ///< unlock the semaphore, incrementing the value virtual int get() const = 0; ///< get semaphore value - static Semaphore *create(); ///< Create a mutex + static SemaphoreOld *create(); ///< Create a mutex - virtual ~Semaphore(); + virtual ~SemaphoreOld(); }; #endif diff --git a/core/os/thread_dummy.cpp b/core/os/thread_dummy.cpp index d4f65b0312..916aeeda30 100644 --- a/core/os/thread_dummy.cpp +++ b/core/os/thread_dummy.cpp @@ -48,12 +48,12 @@ void MutexDummy::make_default() { Mutex::create_func = &MutexDummy::create; }; -Semaphore *SemaphoreDummy::create() { +SemaphoreOld *SemaphoreDummy::create() { return memnew(SemaphoreDummy); }; void SemaphoreDummy::make_default() { - Semaphore::create_func = &SemaphoreDummy::create; + SemaphoreOld::create_func = &SemaphoreDummy::create; }; RWLock *RWLockDummy::create() { diff --git a/core/os/thread_dummy.h b/core/os/thread_dummy.h index c8b52ae4dd..9329cdaa32 100644 --- a/core/os/thread_dummy.h +++ b/core/os/thread_dummy.h @@ -58,9 +58,9 @@ public: static void make_default(); }; -class SemaphoreDummy : public Semaphore { +class SemaphoreDummy : public SemaphoreOld { - static Semaphore *create(); + static SemaphoreOld *create(); public: virtual Error wait() { return OK; }; diff --git a/core/project_settings.cpp b/core/project_settings.cpp index a01a8a35c6..59d7e82850 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -206,7 +206,7 @@ bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const { name = feature_overrides[name]; } if (!props.has(name)) { - WARN_PRINTS("Property not found: " + String(name)); + WARN_PRINT("Property not found: " + String(name)); return false; } r_ret = props[name].variant; @@ -579,7 +579,7 @@ Error ProjectSettings::_load_settings_text(const String &p_path) { _convert_to_last_version(config_version); return OK; } else if (err != OK) { - ERR_PRINTS("Error parsing " + p_path + " at line " + itos(lines) + ": " + error_text + " File might be corrupted."); + ERR_PRINT("Error parsing " + p_path + " at line " + itos(lines) + ": " + error_text + " File might be corrupted."); memdelete(f); return err; } @@ -612,7 +612,7 @@ Error ProjectSettings::_load_settings_text_or_binary(const String &p_text_path, return OK; } else if (err_text != ERR_FILE_NOT_FOUND) { // If the text-based file exists but can't be loaded, we want to know it - ERR_PRINTS("Couldn't load file '" + p_text_path + "', error code " + itos(err_text) + "."); + ERR_PRINT("Couldn't load file '" + p_text_path + "', error code " + itos(err_text) + "."); return err_text; } diff --git a/core/reference.cpp b/core/reference.cpp index b24b2a3ec0..dd65ccce69 100644 --- a/core/reference.cpp +++ b/core/reference.cpp @@ -102,7 +102,8 @@ bool Reference::unreference() { return die; } -Reference::Reference() { +Reference::Reference() : + Object(true) { refcount.init(); refcount_init.init(); @@ -113,7 +114,7 @@ Reference::~Reference() { Variant WeakRef::get_ref() const { - if (ref == 0) + if (ref.is_null()) return Variant(); Object *obj = ObjectDB::get_instance(ref); @@ -129,16 +130,15 @@ Variant WeakRef::get_ref() const { } void WeakRef::set_obj(Object *p_object) { - ref = p_object ? p_object->get_instance_id() : 0; + ref = p_object ? p_object->get_instance_id() : ObjectID(); } void WeakRef::set_ref(const REF &p_ref) { - ref = p_ref.is_valid() ? p_ref->get_instance_id() : 0; + ref = p_ref.is_valid() ? p_ref->get_instance_id() : ObjectID(); } -WeakRef::WeakRef() : - ref(0) { +WeakRef::WeakRef() { } void WeakRef::_bind_methods() { diff --git a/core/reference.h b/core/reference.h index d2314005b9..b01e0035a7 100644 --- a/core/reference.h +++ b/core/reference.h @@ -33,7 +33,6 @@ #include "core/class_db.h" #include "core/object.h" -#include "core/ref_ptr.h" #include "core/safe_refcount.h" class Reference : public Object { @@ -133,17 +132,9 @@ public: return reference; } - RefPtr get_ref_ptr() const { - - RefPtr refptr; - Ref<Reference> *irr = reinterpret_cast<Ref<Reference> *>(refptr.get_data()); - *irr = *this; - return refptr; - }; - operator Variant() const { - return Variant(get_ref_ptr()); + return Variant(reference); } void operator=(const Ref &p_from) { @@ -165,33 +156,24 @@ public: r.reference = NULL; } - void operator=(const RefPtr &p_refptr) { + void operator=(const Variant &p_variant) { - Ref<Reference> *irr = reinterpret_cast<Ref<Reference> *>(p_refptr.get_data()); - Reference *refb = irr->ptr(); - if (!refb) { - unref(); + Object *object = p_variant.get_validated_object(); + + if (object == reference) { return; } - Ref r; - r.reference = Object::cast_to<T>(refb); - ref(r); - r.reference = NULL; - } - void operator=(const Variant &p_variant) { + unref(); - RefPtr refptr = p_variant; - Ref<Reference> *irr = reinterpret_cast<Ref<Reference> *>(refptr.get_data()); - Reference *refb = irr->ptr(); - if (!refb) { - unref(); + if (!object) { return; } - Ref r; - r.reference = Object::cast_to<T>(refb); - ref(r); - r.reference = NULL; + + Reference *r = Object::cast_to<Reference>(object); + if (r && r->reference()) { + reference = static_cast<T *>(r); + } } template <class T_Other> @@ -237,33 +219,19 @@ public: Ref(const Variant &p_variant) { - RefPtr refptr = p_variant; - Ref<Reference> *irr = reinterpret_cast<Ref<Reference> *>(refptr.get_data()); - reference = NULL; - Reference *refb = irr->ptr(); - if (!refb) { - unref(); + Object *object = p_variant.get_validated_object(); + + if (!object) { + reference = nullptr; return; } - Ref r; - r.reference = Object::cast_to<T>(refb); - ref(r); - r.reference = NULL; - } - - Ref(const RefPtr &p_refptr) { - Ref<Reference> *irr = reinterpret_cast<Ref<Reference> *>(p_refptr.get_data()); - reference = NULL; - Reference *refb = irr->ptr(); - if (!refb) { - unref(); - return; + Reference *r = Object::cast_to<Reference>(object); + if (r && r->reference()) { + reference = static_cast<T *>(r); + } else { + reference = nullptr; } - Ref r; - r.reference = Object::cast_to<T>(refb); - ref(r); - r.reference = NULL; } inline bool is_valid() const { return reference != NULL; } @@ -340,32 +308,6 @@ struct PtrToArg<const Ref<T> &> { } }; -//this is for RefPtr - -template <> -struct PtrToArg<RefPtr> { - - _FORCE_INLINE_ static RefPtr convert(const void *p_ptr) { - - return Ref<Reference>(const_cast<Reference *>(reinterpret_cast<const Reference *>(p_ptr))).get_ref_ptr(); - } - - _FORCE_INLINE_ static void encode(RefPtr p_val, const void *p_ptr) { - - Ref<Reference> r = p_val; - *(Ref<Reference> *)p_ptr = r; - } -}; - -template <> -struct PtrToArg<const RefPtr &> { - - _FORCE_INLINE_ static RefPtr convert(const void *p_ptr) { - - return Ref<Reference>(const_cast<Reference *>(reinterpret_cast<const Reference *>(p_ptr))).get_ref_ptr(); - } -}; - #endif // PTRCALL_ENABLED #ifdef DEBUG_METHODS_ENABLED diff --git a/core/resource.h b/core/resource.h index 00d330a094..b30788010b 100644 --- a/core/resource.h +++ b/core/resource.h @@ -33,7 +33,6 @@ #include "core/class_db.h" #include "core/object.h" -#include "core/ref_ptr.h" #include "core/reference.h" #include "core/safe_refcount.h" #include "core/self_list.h" diff --git a/core/rid.h b/core/rid.h index 7e12409181..0c4a96efed 100644 --- a/core/rid.h +++ b/core/rid.h @@ -32,172 +32,46 @@ #define RID_H #include "core/list.h" +#include "core/oa_hash_map.h" #include "core/os/memory.h" #include "core/safe_refcount.h" #include "core/set.h" #include "core/typedefs.h" -class RID_OwnerBase; - -class RID_Data { - - friend class RID_OwnerBase; - -#ifndef DEBUG_ENABLED - RID_OwnerBase *_owner; -#endif - uint32_t _id; - -public: - _FORCE_INLINE_ uint32_t get_id() const { return _id; } - - virtual ~RID_Data(); -}; +class RID_AllocBase; class RID { - friend class RID_OwnerBase; - - mutable RID_Data *_data; + friend class RID_AllocBase; + uint64_t _id; public: - _FORCE_INLINE_ RID_Data *get_data() const { return _data; } - _FORCE_INLINE_ bool operator==(const RID &p_rid) const { - return _data == p_rid._data; + return _id == p_rid._id; } _FORCE_INLINE_ bool operator<(const RID &p_rid) const { - return _data < p_rid._data; + return _id < p_rid._id; } _FORCE_INLINE_ bool operator<=(const RID &p_rid) const { - return _data <= p_rid._data; + return _id <= p_rid._id; } _FORCE_INLINE_ bool operator>(const RID &p_rid) const { - return _data > p_rid._data; + return _id > p_rid._id; } _FORCE_INLINE_ bool operator!=(const RID &p_rid) const { - return _data != p_rid._data; + return _id != p_rid._id; } - _FORCE_INLINE_ bool is_valid() const { return _data != NULL; } + _FORCE_INLINE_ bool is_valid() const { return _id != 0; } + _FORCE_INLINE_ bool is_null() const { return _id == 0; } - _FORCE_INLINE_ uint32_t get_id() const { return _data ? _data->get_id() : 0; } + _FORCE_INLINE_ uint64_t get_id() const { return _id; } _FORCE_INLINE_ RID() { - _data = NULL; - } -}; - -class RID_OwnerBase { -protected: - static SafeRefCount refcount; - _FORCE_INLINE_ void _set_data(RID &p_rid, RID_Data *p_data) { - p_rid._data = p_data; - refcount.ref(); - p_data->_id = refcount.get(); -#ifndef DEBUG_ENABLED - p_data->_owner = this; -#endif - } - -#ifndef DEBUG_ENABLED - - _FORCE_INLINE_ bool _is_owner(const RID &p_rid) const { - - return this == p_rid._data->_owner; - } - - _FORCE_INLINE_ void _remove_owner(RID &p_rid) { - - p_rid._data->_owner = NULL; - } -#endif - -public: - virtual void get_owned_list(List<RID> *p_owned) = 0; - - static void init_rid(); - virtual ~RID_OwnerBase() {} -}; - -template <class T> -class RID_Owner : public RID_OwnerBase { -public: -#ifdef DEBUG_ENABLED - mutable Set<RID_Data *> id_map; -#endif -public: - _FORCE_INLINE_ RID make_rid(T *p_data) { - - RID rid; - _set_data(rid, p_data); - -#ifdef DEBUG_ENABLED - id_map.insert(p_data); -#endif - - return rid; - } - - _FORCE_INLINE_ T *get(const RID &p_rid) { - -#ifdef DEBUG_ENABLED - - ERR_FAIL_COND_V(!p_rid.is_valid(), NULL); - ERR_FAIL_COND_V(!id_map.has(p_rid.get_data()), NULL); -#endif - return static_cast<T *>(p_rid.get_data()); - } - - _FORCE_INLINE_ T *getornull(const RID &p_rid) { - -#ifdef DEBUG_ENABLED - - if (p_rid.get_data()) { - ERR_FAIL_COND_V(!id_map.has(p_rid.get_data()), NULL); - } -#endif - return static_cast<T *>(p_rid.get_data()); - } - - _FORCE_INLINE_ T *getptr(const RID &p_rid) { - - return static_cast<T *>(p_rid.get_data()); - } - - _FORCE_INLINE_ bool owns(const RID &p_rid) const { - - if (p_rid.get_data() == NULL) - return false; -#ifdef DEBUG_ENABLED - return id_map.has(p_rid.get_data()); -#else - return _is_owner(p_rid); -#endif - } - - void free(RID p_rid) { - -#ifdef DEBUG_ENABLED - id_map.erase(p_rid.get_data()); -#else - _remove_owner(p_rid); -#endif - } - - void get_owned_list(List<RID> *p_owned) { - -#ifdef DEBUG_ENABLED - - for (typename Set<RID_Data *>::Element *E = id_map.front(); E; E = E->next()) { - RID r; - _set_data(r, static_cast<T *>(E->get())); - p_owned->push_back(r); - } -#endif + _id = 0; } }; diff --git a/core/rid.cpp b/core/rid_owner.cpp index 727658314f..a5065f29f8 100644 --- a/core/rid.cpp +++ b/core/rid_owner.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* rid.cpp */ +/* rid_owner.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,14 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "rid.h" +#include "rid_owner.h" -RID_Data::~RID_Data() { -} - -SafeRefCount RID_OwnerBase::refcount; - -void RID_OwnerBase::init_rid() { - - refcount.init(); -} +volatile uint64_t RID_AllocBase::base_id = 1; diff --git a/core/rid_owner.h b/core/rid_owner.h new file mode 100644 index 0000000000..bd01eba17d --- /dev/null +++ b/core/rid_owner.h @@ -0,0 +1,406 @@ +/*************************************************************************/ +/* rid_owner.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RID_OWNER_H +#define RID_OWNER_H + +#include "core/print_string.h" +#include "core/rid.h" +#include "core/spin_lock.h" +#include <stdio.h> +#include <typeinfo> + +class RID_AllocBase { + + static volatile uint64_t base_id; + +protected: + static RID _make_from_id(uint64_t p_id) { + RID rid; + rid._id = p_id; + return rid; + } + + static uint64_t _gen_id() { + return atomic_increment(&base_id); + } + + static RID _gen_rid() { + return _make_from_id(_gen_id()); + } + +public: + virtual ~RID_AllocBase() {} +}; + +template <class T, bool THREAD_SAFE = false> +class RID_Alloc : public RID_AllocBase { + + T **chunks; + uint32_t **free_list_chunks; + uint32_t **validator_chunks; + + uint32_t elements_in_chunk; + uint32_t max_alloc; + uint32_t alloc_count; + + const char *description; + + SpinLock spin_lock; + +public: + RID make_rid(const T &p_value) { + + if (THREAD_SAFE) { + spin_lock.lock(); + } + + if (alloc_count == max_alloc) { + //allocate a new chunk + uint32_t chunk_count = alloc_count == 0 ? 0 : (max_alloc / elements_in_chunk); + + //grow chunks + chunks = (T **)memrealloc(chunks, sizeof(T *) * (chunk_count + 1)); + chunks[chunk_count] = (T *)memalloc(sizeof(T) * elements_in_chunk); //but don't initialize + + //grow validators + validator_chunks = (uint32_t **)memrealloc(validator_chunks, sizeof(uint32_t *) * (chunk_count + 1)); + validator_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk); + //grow free lists + free_list_chunks = (uint32_t **)memrealloc(free_list_chunks, sizeof(uint32_t *) * (chunk_count + 1)); + free_list_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk); + + //initialize + for (uint32_t i = 0; i < elements_in_chunk; i++) { + //dont initialize chunk + validator_chunks[chunk_count][i] = 0xFFFFFFFF; + free_list_chunks[chunk_count][i] = alloc_count + i; + } + + max_alloc += elements_in_chunk; + } + + uint32_t free_index = free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk]; + + uint32_t free_chunk = free_index / elements_in_chunk; + uint32_t free_element = free_index % elements_in_chunk; + + T *ptr = &chunks[free_chunk][free_element]; + memnew_placement(ptr, T(p_value)); + + uint32_t validator = (uint32_t)(_gen_id() & 0xFFFFFFFF); + uint64_t id = validator; + id <<= 32; + id |= free_index; + + validator_chunks[free_chunk][free_element] = validator; + alloc_count++; + + if (THREAD_SAFE) { + spin_lock.unlock(); + } + + return _make_from_id(id); + } + + _FORCE_INLINE_ T *getornull(const RID &p_rid) { + + if (THREAD_SAFE) { + spin_lock.lock(); + } + + uint64_t id = p_rid.get_id(); + uint32_t idx = uint32_t(id & 0xFFFFFFFF); + if (unlikely(idx >= max_alloc)) { + if (THREAD_SAFE) { + spin_lock.unlock(); + } + return NULL; + } + + uint32_t idx_chunk = idx / elements_in_chunk; + uint32_t idx_element = idx % elements_in_chunk; + + uint32_t validator = uint32_t(id >> 32); + if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) { + if (THREAD_SAFE) { + spin_lock.unlock(); + } + return NULL; + } + + T *ptr = &chunks[idx_chunk][idx_element]; + + if (THREAD_SAFE) { + spin_lock.unlock(); + } + + return ptr; + } + + _FORCE_INLINE_ bool owns(const RID &p_rid) { + + if (THREAD_SAFE) { + spin_lock.lock(); + } + + uint64_t id = p_rid.get_id(); + uint32_t idx = uint32_t(id & 0xFFFFFFFF); + if (unlikely(idx >= max_alloc)) { + if (THREAD_SAFE) { + spin_lock.unlock(); + } + return false; + } + + uint32_t idx_chunk = idx / elements_in_chunk; + uint32_t idx_element = idx % elements_in_chunk; + + uint32_t validator = uint32_t(id >> 32); + + bool owned = validator_chunks[idx_chunk][idx_element] == validator; + + if (THREAD_SAFE) { + spin_lock.unlock(); + } + + return owned; + } + + _FORCE_INLINE_ void free(const RID &p_rid) { + + if (THREAD_SAFE) { + spin_lock.lock(); + } + + uint64_t id = p_rid.get_id(); + uint32_t idx = uint32_t(id & 0xFFFFFFFF); + if (unlikely(idx >= max_alloc)) { + if (THREAD_SAFE) { + spin_lock.unlock(); + } + ERR_FAIL(); + } + + uint32_t idx_chunk = idx / elements_in_chunk; + uint32_t idx_element = idx % elements_in_chunk; + + uint32_t validator = uint32_t(id >> 32); + if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) { + if (THREAD_SAFE) { + spin_lock.unlock(); + } + ERR_FAIL(); + } + + chunks[idx_chunk][idx_element].~T(); + validator_chunks[idx_chunk][idx_element] = 0xFFFFFFFF; // go invalid + + alloc_count--; + free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk] = idx; + + if (THREAD_SAFE) { + spin_lock.unlock(); + } + } + + _FORCE_INLINE_ uint32_t get_rid_count() const { + return alloc_count; + } + + _FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) { + ERR_FAIL_INDEX_V(p_index, alloc_count, NULL); + if (THREAD_SAFE) { + spin_lock.lock(); + } + uint64_t idx = free_list_chunks[p_index / elements_in_chunk][p_index % elements_in_chunk]; + T *ptr = &chunks[idx / elements_in_chunk][idx % elements_in_chunk]; + if (THREAD_SAFE) { + spin_lock.unlock(); + } + return ptr; + } + + _FORCE_INLINE_ RID get_rid_by_index(uint32_t p_index) { + ERR_FAIL_INDEX_V(p_index, alloc_count, RID()); + if (THREAD_SAFE) { + spin_lock.lock(); + } + uint64_t idx = free_list_chunks[p_index / elements_in_chunk][p_index % elements_in_chunk]; + uint64_t validator = validator_chunks[idx / elements_in_chunk][idx % elements_in_chunk]; + + RID rid = _make_from_id((validator << 32) | idx); + if (THREAD_SAFE) { + spin_lock.unlock(); + } + return rid; + } + + void get_owned_list(List<RID> *p_owned) { + if (THREAD_SAFE) { + spin_lock.lock(); + } + for (size_t i = 0; i < max_alloc; i++) { + uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk]; + if (validator != 0xFFFFFFFF) { + p_owned->push_back(_make_from_id((validator << 32) | i)); + } + } + if (THREAD_SAFE) { + spin_lock.unlock(); + } + } + + void set_description(const char *p_descrption) { + description = p_descrption; + } + + RID_Alloc(uint32_t p_target_chunk_byte_size = 4096) { + chunks = NULL; + free_list_chunks = NULL; + validator_chunks = NULL; + + elements_in_chunk = sizeof(T) > p_target_chunk_byte_size ? 1 : (p_target_chunk_byte_size / sizeof(T)); + max_alloc = 0; + alloc_count = 0; + description = NULL; + } + + ~RID_Alloc() { + if (alloc_count) { + if (description) { + print_error("ERROR: " + itos(alloc_count) + " RID allocations of type '" + description + "' were leaked at exit."); + } else { + print_error("ERROR: " + itos(alloc_count) + " RID allocations of type '" + typeid(T).name() + "' were leaked at exit."); + } + + for (size_t i = 0; i < max_alloc; i++) { + uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk]; + if (validator != 0xFFFFFFFF) { + chunks[i / elements_in_chunk][i % elements_in_chunk].~T(); + } + } + } + + uint32_t chunk_count = max_alloc / elements_in_chunk; + for (uint32_t i = 0; i < chunk_count; i++) { + memfree(chunks[i]); + memfree(validator_chunks[i]); + memfree(free_list_chunks[i]); + } + + if (chunks) { + memfree(chunks); + memfree(free_list_chunks); + memfree(validator_chunks); + } + } +}; + +template <class T, bool THREAD_SAFE = false> +class RID_PtrOwner { + RID_Alloc<T *, THREAD_SAFE> alloc; + +public: + _FORCE_INLINE_ RID make_rid(T *p_ptr) { + return alloc.make_rid(p_ptr); + } + + _FORCE_INLINE_ T *getornull(const RID &p_rid) { + T **ptr = alloc.getornull(p_rid); + if (unlikely(!ptr)) { + return NULL; + } + return *ptr; + } + + _FORCE_INLINE_ bool owns(const RID &p_rid) { + return alloc.owns(p_rid); + } + + _FORCE_INLINE_ void free(const RID &p_rid) { + alloc.free(p_rid); + } + + _FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) { + return alloc.get_owned_list(p_owned); + } + + void set_description(const char *p_descrption) { + alloc.set_description(p_descrption); + } + RID_PtrOwner(uint32_t p_target_chunk_byte_size = 4096) : + alloc(p_target_chunk_byte_size) {} +}; + +template <class T, bool THREAD_SAFE = false> +class RID_Owner { + RID_Alloc<T, THREAD_SAFE> alloc; + +public: + _FORCE_INLINE_ RID make_rid(const T &p_ptr) { + return alloc.make_rid(p_ptr); + } + + _FORCE_INLINE_ T *getornull(const RID &p_rid) { + return alloc.getornull(p_rid); + } + + _FORCE_INLINE_ bool owns(const RID &p_rid) { + return alloc.owns(p_rid); + } + + _FORCE_INLINE_ void free(const RID &p_rid) { + alloc.free(p_rid); + } + + _FORCE_INLINE_ uint32_t get_rid_count() const { + return alloc.get_rid_count(); + } + + _FORCE_INLINE_ RID get_rid_by_index(uint32_t p_index) { + return alloc.get_rid_by_index(p_index); + } + + _FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) { + return alloc.get_ptr_by_index(p_index); + } + + _FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) { + return alloc.get_owned_list(p_owned); + } + + void set_description(const char *p_descrption) { + alloc.set_description(p_descrption); + } + RID_Owner(uint32_t p_target_chunk_byte_size = 4096) : + alloc(p_target_chunk_byte_size) {} +}; +#endif // RID_OWNER_H diff --git a/core/script_language.cpp b/core/script_language.cpp index 7392e7a0af..1149feac38 100644 --- a/core/script_language.cpp +++ b/core/script_language.cpp @@ -32,6 +32,7 @@ #include "core/core_string_names.h" #include "core/project_settings.h" +#include <stdint.h> ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES]; int ScriptServer::_language_count = 0; @@ -644,6 +645,14 @@ Variant PlaceHolderScriptInstance::property_get_fallback(const StringName &p_nam return Variant(); } +uint16_t PlaceHolderScriptInstance::get_rpc_method_id(const StringName &p_method) const { + return UINT16_MAX; +} + +uint16_t PlaceHolderScriptInstance::get_rset_property_id(const StringName &p_method) const { + return UINT16_MAX; +} + PlaceHolderScriptInstance::PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner) : owner(p_owner), language(p_language), diff --git a/core/script_language.h b/core/script_language.h index 22f83080bc..788f5d6976 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -40,6 +40,21 @@ class ScriptLanguage; typedef void (*ScriptEditRequestFunction)(const String &p_path); +struct ScriptNetData { + StringName name; + MultiplayerAPI::RPCMode mode; + bool operator==(ScriptNetData const &p_other) const { + return name == p_other.name; + } +}; + +struct SortNetData { + StringName::AlphCompare compare; + bool operator()(const ScriptNetData &p_a, const ScriptNetData &p_b) const { + return compare(p_a.name, p_b.name); + } +}; + class ScriptServer { enum { @@ -154,6 +169,18 @@ public: virtual bool is_placeholder_fallback_enabled() const { return false; } + virtual Vector<ScriptNetData> get_rpc_methods() const = 0; + virtual uint16_t get_rpc_method_id(const StringName &p_method) const = 0; + virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const = 0; + virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const = 0; + virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0; + + virtual Vector<ScriptNetData> get_rset_properties() const = 0; + virtual uint16_t get_rset_property_id(const StringName &p_property) const = 0; + virtual StringName get_rset_property(const uint16_t p_rset_property_id) const = 0; + virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const = 0; + virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0; + Script() {} }; @@ -195,7 +222,16 @@ 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 Vector<ScriptNetData> get_rpc_methods() const = 0; + virtual uint16_t get_rpc_method_id(const StringName &p_method) const = 0; + virtual StringName get_rpc_method(uint16_t p_id) const = 0; + virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const = 0; virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0; + + virtual Vector<ScriptNetData> get_rset_properties() const = 0; + virtual uint16_t get_rset_property_id(const StringName &p_variable) const = 0; + virtual StringName get_rset_property(uint16_t p_id) const = 0; + virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const = 0; virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0; virtual ScriptLanguage *get_language() = 0; @@ -409,7 +445,16 @@ public: virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid = NULL); virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid = NULL); + virtual Vector<ScriptNetData> get_rpc_methods() const { return Vector<ScriptNetData>(); } + virtual uint16_t get_rpc_method_id(const StringName &p_method) const; + virtual StringName get_rpc_method(uint16_t p_id) const { return StringName(); } + virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const { return MultiplayerAPI::RPC_MODE_DISABLED; } virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const { return MultiplayerAPI::RPC_MODE_DISABLED; } + + virtual Vector<ScriptNetData> get_rset_properties() const { return Vector<ScriptNetData>(); } + virtual uint16_t get_rset_property_id(const StringName &p_variable) const; + virtual StringName get_rset_property(uint16_t p_id) const { return StringName(); } + virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const { return MultiplayerAPI::RPC_MODE_DISABLED; } virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const { return MultiplayerAPI::RPC_MODE_DISABLED; } PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner); diff --git a/core/ref_ptr.h b/core/spin_lock.h index 4736106b4f..c48631f94a 100644 --- a/core/ref_ptr.h +++ b/core/spin_lock.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* ref_ptr.h */ +/* spin_lock.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,35 +28,23 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef REF_PTR_H -#define REF_PTR_H -/** - @author Juan Linietsky <reduzio@gmail.com> - * This class exists to workaround a limitation in C++ but keep the design OK. - * It's basically an opaque container of a Reference reference, so Variant can use it. -*/ +#ifndef SPIN_LOCK_H +#define SPIN_LOCK_H -#include "core/rid.h" +#include "core/typedefs.h" +#include <atomic> -class RefPtr { +class SpinLock { + std::atomic_flag locked = ATOMIC_FLAG_INIT; - enum { - - DATASIZE = sizeof(void *) //*4 -ref was shrunk - }; - - mutable char data[DATASIZE]; // too much probably, virtual class + pointer public: - bool is_null() const; - void operator=(const RefPtr &p_other); - bool operator==(const RefPtr &p_other) const; - bool operator!=(const RefPtr &p_other) const; - RID get_rid() const; - void unref(); - _FORCE_INLINE_ void *get_data() const { return data; } - RefPtr(const RefPtr &p_other); - RefPtr(); - ~RefPtr(); + _ALWAYS_INLINE_ void lock() { + while (locked.test_and_set(std::memory_order_acquire)) { + ; + } + } + _ALWAYS_INLINE_ void unlock() { + locked.clear(std::memory_order_release); + } }; - -#endif // REF_PTR_H +#endif // SPIN_LOCK_H diff --git a/core/ref_ptr.cpp b/core/thread_work_pool.cpp index 7e35bcc56c..c8311f102f 100644 --- a/core/ref_ptr.cpp +++ b/core/thread_work_pool.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* ref_ptr.cpp */ +/* thread_work_pool.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,76 +28,56 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "ref_ptr.h" +#include "thread_work_pool.h" +#include "core/os/os.h" -#include "core/reference.h" -#include "core/resource.h" +void ThreadWorkPool::_thread_function(ThreadData *p_thread) { -void RefPtr::operator=(const RefPtr &p_other) { - - Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]); - Ref<Reference> *ref_other = reinterpret_cast<Ref<Reference> *>(const_cast<char *>(&p_other.data[0])); - - *ref = *ref_other; -} - -bool RefPtr::operator==(const RefPtr &p_other) const { - - Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]); - Ref<Reference> *ref_other = reinterpret_cast<Ref<Reference> *>(const_cast<char *>(&p_other.data[0])); - - return *ref == *ref_other; -} - -bool RefPtr::operator!=(const RefPtr &p_other) const { - - Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]); - Ref<Reference> *ref_other = reinterpret_cast<Ref<Reference> *>(const_cast<char *>(&p_other.data[0])); - - return *ref != *ref_other; + while (true) { + p_thread->start.wait(); + if (p_thread->exit.load()) { + break; + } + p_thread->work->work(); + p_thread->completed.post(); + } } -RefPtr::RefPtr(const RefPtr &p_other) { - - memnew_placement(&data[0], Ref<Reference>); - - Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]); - Ref<Reference> *ref_other = reinterpret_cast<Ref<Reference> *>(const_cast<char *>(&p_other.data[0])); - - *ref = *ref_other; -} +void ThreadWorkPool::init(int p_thread_count) { + ERR_FAIL_COND(threads != nullptr); + if (p_thread_count < 0) { + p_thread_count = OS::get_singleton()->get_processor_count(); + } -bool RefPtr::is_null() const { + thread_count = p_thread_count; + threads = memnew_arr(ThreadData, thread_count); - Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]); - return ref->is_null(); + for (uint32_t i = 0; i < thread_count; i++) { + threads[i].exit.store(false); + threads[i].thread = memnew(std::thread(ThreadWorkPool::_thread_function, &threads[i])); + } } -RID RefPtr::get_rid() const { +void ThreadWorkPool::finish() { - Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]); - if (ref->is_null()) - return RID(); - Resource *res = Object::cast_to<Resource>(ref->ptr()); - if (res) - return res->get_rid(); - return RID(); -} - -void RefPtr::unref() { - - Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]); - ref->unref(); -} + if (threads == nullptr) { + return; + } -RefPtr::RefPtr() { + for (uint32_t i = 0; i < thread_count; i++) { + threads[i].exit.store(true); + threads[i].start.post(); + } + for (uint32_t i = 0; i < thread_count; i++) { + threads[i].thread->join(); + memdelete(threads[i].thread); + } - ERR_FAIL_COND(sizeof(Ref<Reference>) > DATASIZE); - memnew_placement(&data[0], Ref<Reference>); + memdelete_arr(threads); + threads = nullptr; } -RefPtr::~RefPtr() { +ThreadWorkPool::~ThreadWorkPool() { - Ref<Reference> *ref = reinterpret_cast<Ref<Reference> *>(&data[0]); - ref->~Ref<Reference>(); + finish(); } diff --git a/core/thread_work_pool.h b/core/thread_work_pool.h new file mode 100644 index 0000000000..214d2c4aa7 --- /dev/null +++ b/core/thread_work_pool.h @@ -0,0 +1,111 @@ +/*************************************************************************/ +/* thread_work_pool.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef THREAD_WORK_POOL_H +#define THREAD_WORK_POOL_H + +#include "core/os/memory.h" +#include "core/os/semaphore.h" +#include <atomic> +#include <thread> +class ThreadWorkPool { + + std::atomic<uint32_t> index; + + struct BaseWork { + std::atomic<uint32_t> *index; + uint32_t max_elements; + virtual void work() = 0; + virtual ~BaseWork() = default; + }; + + template <class C, class M, class U> + struct Work : public BaseWork { + C *instance; + M method; + U userdata; + virtual void work() { + + while (true) { + uint32_t work_index = index->fetch_add(1, std::memory_order_relaxed); + if (work_index >= max_elements) { + break; + } + (instance->*method)(work_index, userdata); + } + } + }; + + struct ThreadData { + std::thread *thread; + Semaphore start; + Semaphore completed; + std::atomic<bool> exit; + BaseWork *work; + }; + + ThreadData *threads = nullptr; + uint32_t thread_count = 0; + + static void _thread_function(ThreadData *p_thread); + +public: + template <class C, class M, class U> + void do_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) { + + ERR_FAIL_COND(!threads); //never initialized + + index.store(0); + + Work<C, M, U> *w = memnew((Work<C, M, U>)); + w->instance = p_instance; + w->userdata = p_userdata; + w->method = p_method; + w->index = &index; + w->max_elements = p_elements; + + for (uint32_t i = 0; i < thread_count; i++) { + threads[i].work = w; + threads[i].start.post(); + } + for (uint32_t i = 0; i < thread_count; i++) { + threads[i].completed.wait(); + threads[i].work = nullptr; + } + + memdelete(w); + } + + void init(int p_thread_count = -1); + void finish(); + ~ThreadWorkPool(); +}; + +#endif // THREAD_POOL_H diff --git a/core/translation.cpp b/core/translation.cpp index 02297cffc8..cf76de1c9e 100644 --- a/core/translation.cpp +++ b/core/translation.cpp @@ -966,7 +966,7 @@ void TranslationServer::set_locale(const String &p_locale) { print_verbose(vformat("Unsupported locale '%s', falling back to '%s'.", p_locale, trimmed_locale)); if (!is_locale_valid(trimmed_locale)) { - ERR_PRINTS(vformat("Unsupported locale '%s', falling back to 'en'.", trimmed_locale)); + ERR_PRINT(vformat("Unsupported locale '%s', falling back to 'en'.", trimmed_locale)); locale = "en"; } else { locale = trimmed_locale; diff --git a/core/type_info.h b/core/type_info.h index c9b2055241..9ca6d7fe73 100644 --- a/core/type_info.h +++ b/core/type_info.h @@ -169,21 +169,13 @@ MAKE_TYPE_INFO(IP_Address, Variant::STRING) class BSP_Tree; MAKE_TYPE_INFO(BSP_Tree, Variant::DICTIONARY) -//for RefPtr +//objectID template <> -struct GetTypeInfo<RefPtr> { - static const Variant::Type VARIANT_TYPE = Variant::OBJECT; - static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; - static inline PropertyInfo get_class_info() { - return PropertyInfo(Variant::OBJECT, String(), PROPERTY_HINT_RESOURCE_TYPE, "Reference"); - } -}; -template <> -struct GetTypeInfo<const RefPtr &> { - static const Variant::Type VARIANT_TYPE = Variant::OBJECT; +struct GetTypeInfo<ObjectID> { + static const Variant::Type VARIANT_TYPE = Variant::INT; static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; static inline PropertyInfo get_class_info() { - return PropertyInfo(Variant::OBJECT, String(), PROPERTY_HINT_RESOURCE_TYPE, "Reference"); + return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_INT_IS_OBJECTID); } }; @@ -277,7 +269,7 @@ struct GetTypeInfo<const T *, typename EnableIf<TypeInherits<Object, T>::value>: template <typename T> inline StringName __constant_get_enum_name(T param, const String &p_constant) { if (GetTypeInfo<T>::VARIANT_TYPE == Variant::NIL) - ERR_PRINTS("Missing VARIANT_ENUM_CAST for constant's enum: " + p_constant); + ERR_PRINT("Missing VARIANT_ENUM_CAST for constant's enum: " + p_constant); return GetTypeInfo<T>::get_class_info().class_name; } diff --git a/core/undo_redo.cpp b/core/undo_redo.cpp index 5d1144e1f5..577879d448 100644 --- a/core/undo_redo.cpp +++ b/core/undo_redo.cpp @@ -293,7 +293,7 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) { Variant::CallError ce; obj->call(op.name, (const Variant **)argptrs.ptr(), argc, ce); if (ce.error != Variant::CallError::CALL_OK) { - ERR_PRINTS("Error calling method from signal '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, (const Variant **)argptrs.ptr(), argc, ce)); + ERR_PRINT("Error calling method from signal '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, (const Variant **)argptrs.ptr(), argc, ce)); } #ifdef TOOLS_ENABLED Resource *res = Object::cast_to<Resource>(obj); diff --git a/core/ustring.cpp b/core/ustring.cpp index 8a1fbfd383..c4543b89da 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -146,9 +146,11 @@ void CharString::copy_from(const char *p_cstr) { return; } - resize(len + 1); // include terminating null char + Error err = resize(++len); // include terminating null char - strcpy(ptrw(), p_cstr); + ERR_FAIL_COND_MSG(err != OK, "Failed to copy C-string."); + + memcpy(ptrw(), p_cstr, len); } void String::copy_from(const char *p_cstr) { @@ -644,6 +646,17 @@ String String::camelcase_to_underscore(bool lowercase) const { return lowercase ? new_string.to_lower() : new_string; } +String String::get_with_code_lines() const { + Vector<String> lines = split("\n"); + String ret; + for (int i = 0; i < lines.size(); i++) { + if (i > 0) { + ret += "\n"; + } + ret += itos(i + 1) + " " + lines[i]; + } + return ret; +} int String::get_slice_count(String p_splitter) const { if (empty()) @@ -3324,7 +3337,7 @@ String String::humanize_size(uint64_t p_size) { int prefix_idx = 0; - while (prefix_idx < prefixes.size() && p_size > (_div * 1024)) { + while (prefix_idx < prefixes.size() - 1 && p_size > (_div * 1024)) { _div *= 1024; prefix_idx++; } diff --git a/core/ustring.h b/core/ustring.h index 5bf73001aa..e70b2bfe27 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -258,6 +258,7 @@ public: String capitalize() const; String camelcase_to_underscore(bool lowercase = true) const; + String get_with_code_lines() const; int get_slice_count(String p_splitter) const; String get_slice(String p_splitter, int p_slice) const; String get_slicec(CharType p_splitter, int p_slice) const; diff --git a/core/variant.cpp b/core/variant.cpp index f4e4cd5341..c2ffe3721f 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -908,6 +908,14 @@ bool Variant::is_one() const { return false; } +bool Variant::is_null() const { + if (type == OBJECT && _get_obj().obj) { + return false; + } else { + return true; + } +} + void Variant::reference(const Variant &p_variant) { switch (type) { @@ -999,7 +1007,20 @@ void Variant::reference(const Variant &p_variant) { } break; case OBJECT: { - memnew_placement(_data._mem, ObjData(p_variant._get_obj())); + memnew_placement(_data._mem, ObjData); + + if (p_variant._get_obj().obj && p_variant._get_obj().id.is_reference()) { + Reference *reference = static_cast<Reference *>(p_variant._get_obj().obj); + if (!reference->reference()) { + _get_obj().obj = nullptr; + _get_obj().id = ObjectID(); + break; + } + } + + _get_obj().obj = const_cast<Object *>(p_variant._get_obj().obj); + _get_obj().id = p_variant._get_obj().id; + } break; case NODE_PATH: { @@ -1114,8 +1135,15 @@ void Variant::clear() { } break; case OBJECT: { + if (_get_obj().id.is_reference()) { + //we are safe that there is a reference here + Reference *reference = static_cast<Reference *>(_get_obj().obj); + if (reference->unreference()) { + memdelete(reference); + } + } _get_obj().obj = NULL; - _get_obj().ref.unref(); + _get_obj().id = ObjectID(); } break; case _RID: { // not much need probably @@ -1248,6 +1276,14 @@ Variant::operator uint64_t() const { } } +Variant::operator ObjectID() const { + if (type == INT) { + return ObjectID(_data._int); + } else { + return ObjectID(); + } +} + #ifdef NEED_LONG_INT Variant::operator signed long() const { @@ -1581,14 +1617,11 @@ String Variant::stringify(List<const void *> &stack) const { case OBJECT: { if (_get_obj().obj) { -#ifdef DEBUG_ENABLED - if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) { - //only if debugging! - if (!ObjectDB::instance_validate(_get_obj().obj)) { - return "[Deleted Object]"; - }; + + if (!_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { + return "[Freed Object]"; }; -#endif + return _get_obj().obj->to_string(); } else return "[Object:null]"; @@ -1731,24 +1764,16 @@ Variant::operator NodePath() const { return NodePath(); } -Variant::operator RefPtr() const { - - if (type == OBJECT) - return _get_obj().ref; - else - return RefPtr(); -} - Variant::operator RID() const { if (type == _RID) return *reinterpret_cast<const RID *>(_data._mem); - else if (type == OBJECT && !_get_obj().ref.is_null()) { - return _get_obj().ref.get_rid(); + else if (type == OBJECT && _get_obj().obj == nullptr) { + return RID(); } else if (type == OBJECT && _get_obj().obj) { #ifdef DEBUG_ENABLED if (ScriptDebugger::get_singleton()) { - ERR_FAIL_COND_V_MSG(!ObjectDB::instance_validate(_get_obj().obj), RID(), "Invalid pointer (object was deleted)."); + ERR_FAIL_COND_V_MSG(ObjectDB::get_instance(_get_obj().id) == nullptr, RID(), "Invalid pointer (object was freed)."); }; #endif Variant::CallError ce; @@ -1769,6 +1794,25 @@ Variant::operator Object *() const { else return NULL; } + +Object *Variant::get_validated_object_with_check(bool &r_previously_freed) const { + if (type == OBJECT) { + Object *instance = ObjectDB::get_instance(_get_obj().id); + r_previously_freed = !instance && _get_obj().id != ObjectID(); + return instance; + } else { + r_previously_freed = false; + return NULL; + } +} + +Object *Variant::get_validated_object() const { + if (type == OBJECT) + return ObjectDB::get_instance(_get_obj().id); + else + return NULL; +} + Variant::operator Node *() const { if (type == OBJECT) @@ -2193,6 +2237,11 @@ Variant::Variant(double p_double) { _data._real = p_double; } +Variant::Variant(const ObjectID &p_id) { + type = INT; + _data._int = p_id; +} + Variant::Variant(const StringName &p_string) { type = STRING; @@ -2276,15 +2325,6 @@ Variant::Variant(const NodePath &p_node_path) { memnew_placement(_data._mem, NodePath(p_node_path)); } -Variant::Variant(const RefPtr &p_resource) { - - type = OBJECT; - memnew_placement(_data._mem, ObjData); - REF *ref = reinterpret_cast<REF *>(p_resource.get_data()); - _get_obj().obj = ref->ptr(); - _get_obj().ref = p_resource; -} - Variant::Variant(const RID &p_rid) { type = _RID; @@ -2296,7 +2336,24 @@ Variant::Variant(const Object *p_object) { type = OBJECT; memnew_placement(_data._mem, ObjData); - _get_obj().obj = const_cast<Object *>(p_object); + + if (p_object) { + + if (p_object->is_reference()) { + Reference *reference = const_cast<Reference *>(static_cast<const Reference *>(p_object)); + if (!reference->init_ref()) { + _get_obj().obj = nullptr; + _get_obj().id = ObjectID(); + return; + } + } + + _get_obj().obj = const_cast<Object *>(p_object); + _get_obj().id = p_object->get_instance_id(); + } else { + _get_obj().obj = nullptr; + _get_obj().id = ObjectID(); + } } Variant::Variant(const Dictionary &p_dictionary) { @@ -2607,7 +2664,26 @@ void Variant::operator=(const Variant &p_variant) { } break; case OBJECT: { - *reinterpret_cast<ObjData *>(_data._mem) = p_variant._get_obj(); + if (_get_obj().id.is_reference()) { + //we are safe that there is a reference here + Reference *reference = static_cast<Reference *>(_get_obj().obj); + if (reference->unreference()) { + memdelete(reference); + } + } + + if (p_variant._get_obj().obj && p_variant._get_obj().id.is_reference()) { + Reference *reference = static_cast<Reference *>(p_variant._get_obj().obj); + if (!reference->reference()) { + _get_obj().obj = nullptr; + _get_obj().id = ObjectID(); + break; + } + } + + _get_obj().obj = const_cast<Object *>(p_variant._get_obj().obj); + _get_obj().id = p_variant._get_obj().id; + } break; case NODE_PATH: { @@ -3118,7 +3194,7 @@ bool Variant::hash_compare(const Variant &p_variant) const { bool Variant::is_ref() const { - return type == OBJECT && !_get_obj().ref.is_null(); + return type == OBJECT && _get_obj().id.is_reference(); } Vector<Variant> varray() { diff --git a/core/variant.h b/core/variant.h index 06be914408..bb3840932d 100644 --- a/core/variant.h +++ b/core/variant.h @@ -44,12 +44,11 @@ #include "core/math/transform_2d.h" #include "core/math/vector3.h" #include "core/node_path.h" +#include "core/object_id.h" #include "core/pool_vector.h" -#include "core/ref_ptr.h" #include "core/rid.h" #include "core/ustring.h" -class RefPtr; class Object; class Node; // helper class Control; // helper @@ -127,12 +126,12 @@ private: struct ObjData { + ObjectID id; Object *obj; - RefPtr ref; }; - _FORCE_INLINE_ ObjData &_get_obj(); - _FORCE_INLINE_ const ObjData &_get_obj() const; + _ALWAYS_INLINE_ ObjData &_get_obj(); + _ALWAYS_INLINE_ const ObjData &_get_obj() const; union { bool _bool; @@ -161,6 +160,7 @@ public: bool is_shared() const; bool is_zero() const; bool is_one() const; + bool is_null() const; operator bool() const; operator signed int() const; @@ -177,6 +177,8 @@ public: operator unsigned long() const; #endif + operator ObjectID() const; + operator CharType() const; operator float() const; operator double() const; @@ -194,7 +196,6 @@ public: operator Color() const; operator NodePath() const; - operator RefPtr() const; operator RID() const; operator Object *() const; @@ -232,6 +233,9 @@ public: operator IP_Address() const; + Object *get_validated_object() const; + Object *get_validated_object_with_check(bool &r_previously_freed) const; + Variant(bool p_bool); Variant(signed int p_int); // real one Variant(unsigned int p_int); @@ -248,6 +252,7 @@ public: Variant(uint64_t p_int); Variant(float p_float); Variant(double p_double); + Variant(const ObjectID &p_id); Variant(const String &p_string); Variant(const StringName &p_string); Variant(const char *const p_cstring); @@ -263,7 +268,6 @@ public: Variant(const Transform &p_transform); Variant(const Color &p_color); Variant(const NodePath &p_node_path); - Variant(const RefPtr &p_resource); Variant(const RID &p_rid); Variant(const Object *p_object); Variant(const Dictionary &p_dictionary); diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 6b5b947185..ac995d1c78 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -488,7 +488,6 @@ struct _VariantCall { VCALL_LOCALMEM0R(Color, to_argb64); VCALL_LOCALMEM0R(Color, to_abgr64); VCALL_LOCALMEM0R(Color, to_rgba64); - VCALL_LOCALMEM0R(Color, gray); VCALL_LOCALMEM0R(Color, inverted); VCALL_LOCALMEM0R(Color, contrasted); VCALL_LOCALMEM2R(Color, linear_interpolate); @@ -1100,12 +1099,9 @@ void Variant::call_ptr(const StringName &p_method, const Variant **p_args, int p return; } #ifdef DEBUG_ENABLED - if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) { - //only if debugging! - if (!ObjectDB::instance_validate(obj)) { - r_error.error = CallError::CALL_ERROR_INSTANCE_IS_NULL; - return; - } + if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { + r_error.error = CallError::CALL_ERROR_INSTANCE_IS_NULL; + return; } #endif @@ -1275,18 +1271,11 @@ Variant Variant::construct(const Variant::Type p_type, const Variant **p_args, i bool Variant::has_method(const StringName &p_method) const { if (type == OBJECT) { - Object *obj = operator Object *(); + Object *obj = get_validated_object(); if (!obj) return false; -#ifdef DEBUG_ENABLED - if (ScriptDebugger::get_singleton()) { - if (ObjectDB::instance_validate(obj)) { -#endif - return obj->has_method(p_method); -#ifdef DEBUG_ENABLED - } - } -#endif + + return obj->has_method(p_method); } const _VariantCall::TypeFunc &tf = _VariantCall::type_funcs[type]; @@ -1737,7 +1726,6 @@ void register_variant_methods() { ADDFUNC0R(COLOR, INT, Color, to_argb64, varray()); ADDFUNC0R(COLOR, INT, Color, to_abgr64, varray()); ADDFUNC0R(COLOR, INT, Color, to_rgba64, varray()); - ADDFUNC0R(COLOR, REAL, Color, gray, varray()); ADDFUNC0R(COLOR, COLOR, Color, inverted, varray()); ADDFUNC0R(COLOR, COLOR, Color, contrasted, varray()); ADDFUNC2R(COLOR, COLOR, Color, linear_interpolate, COLOR, "b", REAL, "t", varray()); diff --git a/core/variant_op.cpp b/core/variant_op.cpp index 6caa224cfe..c7a52b0347 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -1515,7 +1515,7 @@ void Variant::set_named(const StringName &p_index, const Variant &p_value, bool #ifdef DEBUG_ENABLED if (!_get_obj().obj) { break; - } else if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) { + } else if (ScriptDebugger::get_singleton() && ObjectDB::get_instance(_get_obj().id) == nullptr) { break; } @@ -1684,7 +1684,7 @@ Variant Variant::get_named(const StringName &p_index, bool *r_valid) const { return "Instance base is null."; } else { - if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) { + if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { if (r_valid) *r_valid = false; return "Attempted use of stray pointer object."; @@ -2172,13 +2172,11 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid) if (obj) { #ifdef DEBUG_ENABLED - if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) { + if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { - if (!ObjectDB::instance_validate(obj)) { - WARN_PRINT("Attempted use of stray pointer object."); - valid = false; - return; - } + WARN_PRINT("Attempted use of previously freed pointer object."); + valid = false; + return; } #endif @@ -2546,12 +2544,10 @@ Variant Variant::get(const Variant &p_index, bool *r_valid) const { if (obj) { #ifdef DEBUG_ENABLED - if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) { - //only if debugging! - if (!ObjectDB::instance_validate(obj)) { - valid = false; - return "Attempted get on stray pointer."; - } + + if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { + valid = false; + return "Attempted get on previously freed instance."; } #endif @@ -2611,15 +2607,14 @@ bool Variant::in(const Variant &p_index, bool *r_valid) const { bool valid = false; #ifdef DEBUG_ENABLED - if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) { - //only if debugging! - if (!ObjectDB::instance_validate(obj)) { - if (r_valid) { - *r_valid = false; - } - return true; // Attempted get on stray pointer. + + if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { + if (r_valid) { + *r_valid = false; } + return true; // Attempted get on stray pointer. } + #endif if (p_index.get_type() != Variant::STRING) { @@ -2883,13 +2878,12 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const { Object *obj = _get_obj().obj; if (obj) { #ifdef DEBUG_ENABLED - if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null()) { - //only if debugging! - if (!ObjectDB::instance_validate(obj)) { - WARN_PRINT("Attempted get_property list on stray pointer."); - return; - } + + if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { + WARN_PRINT("Attempted get_property list on previously freed instance."); + return; } + #endif obj->get_property_list(p_list); @@ -2961,16 +2955,18 @@ bool Variant::iter_init(Variant &r_iter, bool &valid) const { } break; case OBJECT: { -#ifdef DEBUG_ENABLED if (!_get_obj().obj) { valid = false; return false; } - if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) { +#ifdef DEBUG_ENABLED + + if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { valid = false; return false; } + #endif Variant::CallError ce; ce.error = Variant::CallError::CALL_OK; @@ -3129,16 +3125,18 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const { } break; case OBJECT: { -#ifdef DEBUG_ENABLED if (!_get_obj().obj) { valid = false; return false; } - if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) { +#ifdef DEBUG_ENABLED + + if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { valid = false; return false; } + #endif Variant::CallError ce; ce.error = Variant::CallError::CALL_OK; @@ -3288,16 +3286,16 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const { } break; case OBJECT: { -#ifdef DEBUG_ENABLED if (!_get_obj().obj) { r_valid = false; return Variant(); } - - if (ScriptDebugger::get_singleton() && _get_obj().ref.is_null() && !ObjectDB::instance_validate(_get_obj().obj)) { +#ifdef DEBUG_ENABLED + if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) { r_valid = false; return Variant(); } + #endif Variant::CallError ce; ce.error = Variant::CallError::CALL_OK; diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp index 6ca9d6c246..996b25308e 100644 --- a/core/variant_parser.cpp +++ b/core/variant_parser.cpp @@ -51,10 +51,16 @@ bool VariantParser::StreamFile::is_eof() const { CharType VariantParser::StreamString::get_char() { - if (pos >= s.length()) + if (pos > s.length()) { return 0; - else + } else if (pos == s.length()) { + // You need to try to read again when you have reached the end for EOF to be reported, + // so this works the same as files (like StreamFile does) + pos++; + return 0; + } else { return s[pos++]; + } } bool VariantParser::StreamString::is_utf8() const { @@ -229,8 +235,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri case 'f': res = 12; break; case 'r': res = 13; break; case 'u': { - //hexnumbarh - oct is deprecated - + //hex number for (int j = 0; j < 4; j++) { CharType c = p_stream->get_char(); if (c == 0) { @@ -254,7 +259,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri v = c - 'A'; v += 10; } else { - ERR_PRINT("BUG"); + ERR_PRINT("Bug parsing hex constant."); v = 0; } @@ -263,13 +268,8 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } } break; - //case '\"': res='\"'; break; - //case '\\': res='\\'; break; - //case '/': res='/'; break; default: { res = next; - //r_err_str="Invalid escape sequence"; - //return ERR_PARSE_ERROR; } break; } @@ -860,198 +860,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, return ERR_PARSE_ERROR; } } -#ifndef DISABLE_DEPRECATED - } else if (id == "InputEvent") { - - get_token(p_stream, token, line, r_err_str); - if (token.type != TK_PARENTHESIS_OPEN) { - r_err_str = "Expected '('"; - return ERR_PARSE_ERROR; - } - - get_token(p_stream, token, line, r_err_str); - - if (token.type != TK_IDENTIFIER) { - r_err_str = "Expected identifier"; - return ERR_PARSE_ERROR; - } - - String id2 = token.value; - - Ref<InputEvent> ie; - - if (id2 == "NONE") { - - get_token(p_stream, token, line, r_err_str); - - if (token.type != TK_PARENTHESIS_CLOSE) { - r_err_str = "Expected ')'"; - return ERR_PARSE_ERROR; - } - - } else if (id2 == "KEY") { - - Ref<InputEventKey> key; - key.instance(); - ie = key; - - get_token(p_stream, token, line, r_err_str); - if (token.type != TK_COMMA) { - r_err_str = "Expected ','"; - return ERR_PARSE_ERROR; - } - - get_token(p_stream, token, line, r_err_str); - if (token.type == TK_IDENTIFIER) { - String name = token.value; - key->set_scancode(find_keycode(name)); - } else if (token.type == TK_NUMBER) { - - key->set_scancode(token.value); - } else { - - r_err_str = "Expected string or integer for keycode"; - return ERR_PARSE_ERROR; - } - - get_token(p_stream, token, line, r_err_str); - - if (token.type == TK_COMMA) { - - get_token(p_stream, token, line, r_err_str); - - if (token.type != TK_IDENTIFIER) { - r_err_str = "Expected identifier with modifier flas"; - return ERR_PARSE_ERROR; - } - - String mods = token.value; - - if (mods.findn("C") != -1) - key->set_control(true); - if (mods.findn("A") != -1) - key->set_alt(true); - if (mods.findn("S") != -1) - key->set_shift(true); - if (mods.findn("M") != -1) - key->set_metakey(true); - - get_token(p_stream, token, line, r_err_str); - if (token.type != TK_PARENTHESIS_CLOSE) { - r_err_str = "Expected ')'"; - return ERR_PARSE_ERROR; - } - - } else if (token.type != TK_PARENTHESIS_CLOSE) { - - r_err_str = "Expected ')' or modifier flags."; - return ERR_PARSE_ERROR; - } - - } else if (id2 == "MBUTTON") { - - Ref<InputEventMouseButton> mb; - mb.instance(); - ie = mb; - - get_token(p_stream, token, line, r_err_str); - if (token.type != TK_COMMA) { - r_err_str = "Expected ','"; - return ERR_PARSE_ERROR; - } - - get_token(p_stream, token, line, r_err_str); - if (token.type != TK_NUMBER) { - r_err_str = "Expected button index"; - return ERR_PARSE_ERROR; - } - - mb->set_button_index(token.value); - - get_token(p_stream, token, line, r_err_str); - if (token.type != TK_PARENTHESIS_CLOSE) { - r_err_str = "Expected ')'"; - return ERR_PARSE_ERROR; - } - - } else if (id2 == "JBUTTON") { - - Ref<InputEventJoypadButton> jb; - jb.instance(); - ie = jb; - - get_token(p_stream, token, line, r_err_str); - if (token.type != TK_COMMA) { - r_err_str = "Expected ','"; - return ERR_PARSE_ERROR; - } - - get_token(p_stream, token, line, r_err_str); - if (token.type != TK_NUMBER) { - r_err_str = "Expected button index"; - return ERR_PARSE_ERROR; - } - - jb->set_button_index(token.value); - - get_token(p_stream, token, line, r_err_str); - if (token.type != TK_PARENTHESIS_CLOSE) { - r_err_str = "Expected ')'"; - return ERR_PARSE_ERROR; - } - - } else if (id2 == "JAXIS") { - - Ref<InputEventJoypadMotion> jm; - jm.instance(); - ie = jm; - - get_token(p_stream, token, line, r_err_str); - if (token.type != TK_COMMA) { - r_err_str = "Expected ','"; - return ERR_PARSE_ERROR; - } - - get_token(p_stream, token, line, r_err_str); - if (token.type != TK_NUMBER) { - r_err_str = "Expected axis index"; - return ERR_PARSE_ERROR; - } - - jm->set_axis(token.value); - - get_token(p_stream, token, line, r_err_str); - - if (token.type != TK_COMMA) { - r_err_str = "Expected ',' after axis index"; - return ERR_PARSE_ERROR; - } - - get_token(p_stream, token, line, r_err_str); - if (token.type != TK_NUMBER) { - r_err_str = "Expected axis sign"; - return ERR_PARSE_ERROR; - } - - jm->set_axis_value(token.value); - - get_token(p_stream, token, line, r_err_str); - - if (token.type != TK_PARENTHESIS_CLOSE) { - r_err_str = "Expected ')' for jaxis"; - return ERR_PARSE_ERROR; - } - - } else { - - r_err_str = "Invalid input event type."; - return ERR_PARSE_ERROR; - } - - value = ie; - return OK; -#endif } else if (id == "PoolByteArray" || id == "ByteArray") { Vector<uint8_t> args; |