diff options
Diffstat (limited to 'core')
211 files changed, 7127 insertions, 4710 deletions
diff --git a/core/SCsub b/core/SCsub index 00d0bcac24..755c5c65c6 100644 --- a/core/SCsub +++ b/core/SCsub @@ -47,17 +47,14 @@ env_thirdparty.disable_warnings() thirdparty_misc_dir = "#thirdparty/misc/" thirdparty_misc_sources = [ # C sources - "base64.c", "fastlz.c", - "sha256.c", "smaz.c", # C++ sources - "aes256.cpp", "hq2x.cpp", - "md5.cpp", "pcg.cpp", "triangulator.cpp", + "clipper.cpp", ] thirdparty_misc_sources = [thirdparty_misc_dir + file for file in thirdparty_misc_sources] env_thirdparty.add_source_files(env.core_sources, thirdparty_misc_sources) @@ -80,9 +77,11 @@ if env['builtin_zlib']: ] thirdparty_zlib_sources = [thirdparty_zlib_dir + file for file in thirdparty_zlib_sources] - env_thirdparty.Append(CPPPATH=[thirdparty_zlib_dir]) + env_thirdparty.Prepend(CPPPATH=[thirdparty_zlib_dir]) # Needs to be available in main env too - env.Append(CPPPATH=[thirdparty_zlib_dir]) + env.Prepend(CPPPATH=[thirdparty_zlib_dir]) + if (env['target'] == 'debug'): + env_thirdparty.Append(CPPDEFINES=['ZLIB_DEBUG']) env_thirdparty.add_source_files(env.core_sources, thirdparty_zlib_sources) @@ -121,6 +120,8 @@ if env['builtin_zstd']: "compress/zstd_ldm.c", "compress/zstd_opt.c", "compress/zstdmt_compress.c", + "compress/zstd_compress_literals.c", + "compress/zstd_compress_sequences.c", "decompress/huf_decompress.c", "decompress/zstd_ddict.c", "decompress/zstd_decompress_block.c", @@ -128,11 +129,11 @@ if env['builtin_zstd']: ] thirdparty_zstd_sources = [thirdparty_zstd_dir + file for file in thirdparty_zstd_sources] - env_thirdparty.Append(CPPPATH=[thirdparty_zstd_dir, thirdparty_zstd_dir + "common"]) - env_thirdparty.Append(CCFLAGS="-DZSTD_STATIC_LINKING_ONLY") - env.Append(CPPPATH=thirdparty_zstd_dir) + env_thirdparty.Prepend(CPPPATH=[thirdparty_zstd_dir, thirdparty_zstd_dir + "common"]) + env_thirdparty.Append(CPPDEFINES=["ZSTD_STATIC_LINKING_ONLY"]) + env.Prepend(CPPPATH=thirdparty_zstd_dir) # Also needed in main env includes will trigger warnings - env.Append(CCFLAGS="-DZSTD_STATIC_LINKING_ONLY") + env.Append(CPPDEFINES=["ZSTD_STATIC_LINKING_ONLY"]) env_thirdparty.add_source_files(env.core_sources, thirdparty_zstd_sources) @@ -145,7 +146,7 @@ env.Depends("#core/io/certs_compressed.gen.h", ["#thirdparty/certs/ca-certificat env.CommandNoCache("#core/io/certs_compressed.gen.h", "#thirdparty/certs/ca-certificates.crt", run_in_subprocess(core_builders.make_certs_header)) # Make binders -env.CommandNoCache(['method_bind.gen.inc', 'method_bind_ext.gen.inc'], 'make_binders.py', run_in_subprocess(make_binders.run)) +env.CommandNoCache(['method_bind.gen.inc', 'method_bind_ext.gen.inc', 'method_bind_free_func.gen.inc'], 'make_binders.py', run_in_subprocess(make_binders.run)) # Authors env.Depends('#core/authors.gen.h', "../AUTHORS.md") @@ -162,6 +163,7 @@ env.CommandNoCache('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"] # Chain load SCsubs SConscript('os/SCsub') SConscript('math/SCsub') +SConscript('crypto/SCsub') SConscript('io/SCsub') SConscript('bind/SCsub') diff --git a/core/array.cpp b/core/array.cpp index 649e610a69..fd507f46c3 100644 --- a/core/array.cpp +++ b/core/array.cpp @@ -133,12 +133,12 @@ void Array::erase(const Variant &p_value) { } Variant Array::front() const { - ERR_FAIL_COND_V(_p->array.size() == 0, Variant()); + ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array."); return operator[](0); } Variant Array::back() const { - ERR_FAIL_COND_V(_p->array.size() == 0, Variant()); + ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array."); return operator[](_p->array.size() - 1); } @@ -165,8 +165,8 @@ int Array::rfind(const Variant &p_value, int p_from) const { if (_p->array[i] == p_value) { return i; - }; - }; + } + } return -1; } @@ -186,8 +186,8 @@ int Array::count(const Variant &p_value) const { if (_p->array[i] == p_value) { amount++; - }; - }; + } + } return amount; } @@ -222,6 +222,66 @@ Array Array::duplicate(bool p_deep) const { return new_arr; } + +int Array::_fix_slice_index(int p_index, int p_arr_len, int p_top_mod) { + p_index = CLAMP(p_index, -p_arr_len, p_arr_len + p_top_mod); + if (p_index < 0) { + p_index = (p_index % p_arr_len + p_arr_len) % p_arr_len; // positive modulo + } + return p_index; +} + +int Array::_clamp_index(int p_index) const { + return CLAMP(p_index, -size() + 1, size() - 1); +} + +#define ARRAY_GET_DEEP(idx, is_deep) is_deep ? get(idx).duplicate(is_deep) : get(idx) + +Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const { // like python, but inclusive on upper bound + Array new_arr; + + if (empty()) // Don't try to slice empty arrays. + return new_arr; + + p_begin = Array::_fix_slice_index(p_begin, size(), -1); // can't start out of range + p_end = Array::_fix_slice_index(p_end, size(), 0); + + int x = p_begin; + int new_arr_i = 0; + + ERR_FAIL_COND_V(p_step == 0, new_arr); + if (Array::_clamp_index(p_begin) == Array::_clamp_index(p_end)) { // don't include element twice + new_arr.resize(1); + // new_arr[0] = 1; + new_arr[0] = ARRAY_GET_DEEP(Array::_clamp_index(p_begin), p_deep); + return new_arr; + } else { + int element_count = ceil((int)MAX(0, (p_end - p_begin) / p_step)) + 1; + if (element_count == 1) { // delta going in wrong direction to reach end + new_arr.resize(0); + return new_arr; + } + new_arr.resize(element_count); + } + + // if going backwards, have to have a different terminating condition + if (p_step < 0) { + while (x >= p_end) { + new_arr[new_arr_i] = ARRAY_GET_DEEP(Array::_clamp_index(x), p_deep); + x += p_step; + new_arr_i += 1; + } + } else if (p_step > 0) { + while (x <= p_end) { + new_arr[new_arr_i] = ARRAY_GET_DEEP(Array::_clamp_index(x), p_deep); + x += p_step; + new_arr_i += 1; + } + } + + return new_arr; +} + struct _ArrayVariantSort { _FORCE_INLINE_ bool operator()(const Variant &p_l, const Variant &p_r) const { @@ -401,6 +461,10 @@ Variant Array::max() const { return maxval; } +const void *Array::id() const { + return _p->array.ptr(); +} + Array::Array(const Array &p_from) { _p = NULL; diff --git a/core/array.h b/core/array.h index 6158db4065..7a754d53ea 100644 --- a/core/array.h +++ b/core/array.h @@ -44,6 +44,9 @@ class Array { void _ref(const Array &p_from) const; void _unref() const; + int _clamp_index(int p_index) const; + static int _fix_slice_index(int p_index, int p_arr_len, int p_top_mod); + public: Variant &operator[](int p_idx); const Variant &operator[](int p_idx) const; @@ -91,9 +94,13 @@ public: Array duplicate(bool p_deep = false) const; + Array slice(int p_begin, int p_end, int p_step = 1, bool p_deep = false) const; + Variant min() const; Variant max() const; + const void *id() const; + Array(const Array &p_from); Array(); ~Array(); diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 98d3e77f66..4c8dcc20ea 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -30,6 +30,7 @@ #include "core_bind.h" +#include "core/crypto/crypto_core.h" #include "core/io/file_access_compressed.h" #include "core/io/file_access_encrypted.h" #include "core/io/json.h" @@ -39,8 +40,6 @@ #include "core/os/os.h" #include "core/project_settings.h" -#include "thirdparty/misc/base64.h" - /** * Time constants borrowed from loc_time.h */ @@ -74,10 +73,7 @@ RES _ResourceLoader::load(const String &p_path, const String &p_type_hint, bool Error err = OK; RES ret = ResourceLoader::load(p_path, p_type_hint, p_no_cache, &err); - if (err != OK) { - ERR_EXPLAIN("Error loading resource: '" + p_path + "'"); - ERR_FAIL_COND_V(err != OK, ret); - } + ERR_FAIL_COND_V_MSG(err != OK, ret, "Error loading resource: '" + p_path + "'."); return ret; } @@ -149,14 +145,13 @@ _ResourceLoader::_ResourceLoader() { } Error _ResourceSaver::save(const String &p_path, const RES &p_resource, SaverFlags p_flags) { - - ERR_FAIL_COND_V(p_resource.is_null(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, "Can't save empty resource to path '" + String(p_path) + "'."); return ResourceSaver::save(p_path, p_resource, p_flags); } PoolVector<String> _ResourceSaver::get_recognized_extensions(const RES &p_resource) { - ERR_FAIL_COND_V(p_resource.is_null(), PoolVector<String>()); + ERR_FAIL_COND_V_MSG(p_resource.is_null(), PoolVector<String>(), "It's not a reference to a valid Resource object."); List<String> exts; ResourceSaver::get_recognized_extensions(p_resource, &exts); PoolVector<String> ret; @@ -190,10 +185,31 @@ _ResourceSaver::_ResourceSaver() { /////////////////OS +void _OS::global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta) { + + OS::get_singleton()->global_menu_add_item(p_menu, p_label, p_signal, p_meta); +} + +void _OS::global_menu_add_separator(const String &p_menu) { + + OS::get_singleton()->global_menu_add_separator(p_menu); +} + +void _OS::global_menu_remove_item(const String &p_menu, int p_idx) { + + OS::get_singleton()->global_menu_remove_item(p_menu, p_idx); +} + +void _OS::global_menu_clear(const String &p_menu) { + + OS::get_singleton()->global_menu_clear(p_menu); +} + Point2 _OS::get_mouse_position() const { return OS::get_singleton()->get_mouse_position(); } + void _OS::set_window_title(const String &p_title) { OS::get_singleton()->set_window_title(p_title); @@ -207,6 +223,7 @@ int _OS::get_mouse_button_state() const { String _OS::get_unique_id() const { return OS::get_singleton()->get_unique_id(); } + bool _OS::has_touchscreen_ui_hint() const { return OS::get_singleton()->has_touchscreen_ui_hint(); @@ -216,6 +233,7 @@ void _OS::set_clipboard(const String &p_text) { OS::get_singleton()->set_clipboard(p_text); } + String _OS::get_clipboard() const { return OS::get_singleton()->get_clipboard(); @@ -246,11 +264,11 @@ PoolStringArray _OS::get_connected_midi_inputs() { } void _OS::open_midi_inputs() { - return OS::get_singleton()->open_midi_inputs(); + OS::get_singleton()->open_midi_inputs(); } void _OS::close_midi_inputs() { - return OS::get_singleton()->close_midi_inputs(); + OS::get_singleton()->close_midi_inputs(); } void _OS::set_video_mode(const Size2 &p_size, bool p_fullscreen, bool p_resizeable, int p_screen) { @@ -262,12 +280,14 @@ void _OS::set_video_mode(const Size2 &p_size, bool p_fullscreen, bool p_resizeab vm.resizable = p_resizeable; OS::get_singleton()->set_video_mode(vm, p_screen); } + Size2 _OS::get_video_mode(int p_screen) const { OS::VideoMode vm; vm = OS::get_singleton()->get_video_mode(p_screen); return Size2(vm.width, vm.height); } + bool _OS::is_video_mode_fullscreen(int p_screen) const { OS::VideoMode vm; @@ -308,6 +328,14 @@ void _OS::set_window_position(const Point2 &p_position) { OS::get_singleton()->set_window_position(p_position); } +Size2 _OS::get_max_window_size() const { + return OS::get_singleton()->get_max_window_size(); +} + +Size2 _OS::get_min_window_size() const { + return OS::get_singleton()->get_min_window_size(); +} + Size2 _OS::get_window_size() const { return OS::get_singleton()->get_window_size(); } @@ -316,6 +344,14 @@ Size2 _OS::get_real_window_size() const { return OS::get_singleton()->get_real_window_size(); } +void _OS::set_max_window_size(const Size2 &p_size) { + OS::get_singleton()->set_max_window_size(p_size); +} + +void _OS::set_min_window_size(const Size2 &p_size) { + OS::get_singleton()->set_min_window_size(p_size); +} + void _OS::set_window_size(const Size2 &p_size) { OS::get_singleton()->set_window_size(p_size); } @@ -432,6 +468,16 @@ bool _OS::is_in_low_processor_usage_mode() const { return OS::get_singleton()->is_in_low_processor_usage_mode(); } +void _OS::set_low_processor_usage_mode_sleep_usec(int p_usec) { + + OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(p_usec); +} + +int _OS::get_low_processor_usage_mode_sleep_usec() const { + + return OS::get_singleton()->get_low_processor_usage_mode_sleep_usec(); +} + String _OS::get_executable_path() const { return OS::get_singleton()->get_executable_path(); @@ -442,18 +488,21 @@ Error _OS::shell_open(String p_uri) { return OS::get_singleton()->shell_open(p_uri); }; -int _OS::execute(const String &p_path, const Vector<String> &p_arguments, bool p_blocking, Array p_output) { +int _OS::execute(const String &p_path, const Vector<String> &p_arguments, bool p_blocking, Array p_output, bool p_read_stderr) { OS::ProcessID pid = -2; + int exitcode = 0; List<String> args; for (int i = 0; i < p_arguments.size(); i++) args.push_back(p_arguments[i]); String pipe; - Error err = OS::get_singleton()->execute(p_path, args, p_blocking, &pid, &pipe); + Error err = OS::get_singleton()->execute(p_path, args, p_blocking, &pid, &pipe, &exitcode, p_read_stderr); p_output.clear(); p_output.push_back(pipe); if (err != OK) return -1; + else if (p_blocking) + return exitcode; else return pid; } @@ -535,6 +584,15 @@ bool _OS::is_vsync_enabled() const { return OS::get_singleton()->is_vsync_enabled(); } +void _OS::set_vsync_via_compositor(bool p_enable) { + OS::get_singleton()->set_vsync_via_compositor(p_enable); +} + +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()); } @@ -611,6 +669,11 @@ uint64_t _OS::get_dynamic_memory_usage() const { return OS::get_singleton()->get_dynamic_memory_usage(); } +void _OS::set_native_icon(const String &p_filename) { + + OS::get_singleton()->set_native_icon(p_filename); +} + void _OS::set_icon(const Ref<Image> &p_icon) { OS::get_singleton()->set_icon(p_icon); @@ -705,22 +768,16 @@ int64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const { { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } }; - ERR_EXPLAIN("Invalid second value of: " + itos(second)); - ERR_FAIL_COND_V(second > 59, 0); + ERR_FAIL_COND_V_MSG(second > 59, 0, "Invalid second value of: " + itos(second) + "."); - ERR_EXPLAIN("Invalid minute value of: " + itos(minute)); - ERR_FAIL_COND_V(minute > 59, 0); + ERR_FAIL_COND_V_MSG(minute > 59, 0, "Invalid minute value of: " + itos(minute) + "."); - ERR_EXPLAIN("Invalid hour value of: " + itos(hour)); - ERR_FAIL_COND_V(hour > 23, 0); + ERR_FAIL_COND_V_MSG(hour > 23, 0, "Invalid hour value of: " + itos(hour) + "."); - ERR_EXPLAIN("Invalid month value of: " + itos(month)); - ERR_FAIL_COND_V(month > 12 || month == 0, 0); + ERR_FAIL_COND_V_MSG(month > 12 || month == 0, 0, "Invalid month value of: " + itos(month) + "."); // Do this check after month is tested as valid - ERR_EXPLAIN("Invalid day value of: " + itos(day) + " which is larger than " + itos(MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1]) + " or 0"); - ERR_FAIL_COND_V(day > MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1] || day == 0, 0); - + ERR_FAIL_COND_V_MSG(day > MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1] || day == 0, 0, "Invalid day value of '" + itos(day) + "' which is larger than '" + itos(MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1]) + "' or 0."); // Calculate all the seconds from months past in this year uint64_t SECONDS_FROM_MONTHS_PAST_THIS_YEAR = DAYS_PAST_THIS_YEAR_TABLE[LEAPYEAR(year)][month - 1] * SECONDS_PER_DAY; @@ -1098,6 +1155,16 @@ bool _OS::request_permission(const String &p_name) { return OS::get_singleton()->request_permission(p_name); } +bool _OS::request_permissions() { + + return OS::get_singleton()->request_permissions(); +} + +Vector<String> _OS::get_granted_permissions() const { + + return OS::get_singleton()->get_granted_permissions(); +} + _OS *_OS::singleton = NULL; void _OS::_bind_methods() { @@ -1115,6 +1182,11 @@ void _OS::_bind_methods() { //ClassDB::bind_method(D_METHOD("is_video_mode_resizable","screen"),&_OS::is_video_mode_resizable,DEFVAL(0)); //ClassDB::bind_method(D_METHOD("get_fullscreen_mode_list","screen"),&_OS::get_fullscreen_mode_list,DEFVAL(0)); + ClassDB::bind_method(D_METHOD("global_menu_add_item", "menu", "label", "id", "meta"), &_OS::global_menu_add_item); + ClassDB::bind_method(D_METHOD("global_menu_add_separator", "menu"), &_OS::global_menu_add_separator); + ClassDB::bind_method(D_METHOD("global_menu_remove_item", "menu", "idx"), &_OS::global_menu_remove_item); + ClassDB::bind_method(D_METHOD("global_menu_clear", "menu"), &_OS::global_menu_clear); + ClassDB::bind_method(D_METHOD("get_video_driver_count"), &_OS::get_video_driver_count); ClassDB::bind_method(D_METHOD("get_video_driver_name", "driver"), &_OS::get_video_driver_name); ClassDB::bind_method(D_METHOD("get_current_video_driver"), &_OS::get_current_video_driver); @@ -1134,6 +1206,10 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_window_position"), &_OS::get_window_position); ClassDB::bind_method(D_METHOD("set_window_position", "position"), &_OS::set_window_position); ClassDB::bind_method(D_METHOD("get_window_size"), &_OS::get_window_size); + ClassDB::bind_method(D_METHOD("get_max_window_size"), &_OS::get_max_window_size); + ClassDB::bind_method(D_METHOD("get_min_window_size"), &_OS::get_min_window_size); + ClassDB::bind_method(D_METHOD("set_max_window_size", "size"), &_OS::set_max_window_size); + ClassDB::bind_method(D_METHOD("set_min_window_size", "size"), &_OS::set_min_window_size); ClassDB::bind_method(D_METHOD("set_window_size", "size"), &_OS::set_window_size); ClassDB::bind_method(D_METHOD("get_window_safe_area"), &_OS::get_window_safe_area); ClassDB::bind_method(D_METHOD("set_window_fullscreen", "enabled"), &_OS::set_window_fullscreen); @@ -1175,10 +1251,13 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("set_low_processor_usage_mode", "enable"), &_OS::set_low_processor_usage_mode); ClassDB::bind_method(D_METHOD("is_in_low_processor_usage_mode"), &_OS::is_in_low_processor_usage_mode); + ClassDB::bind_method(D_METHOD("set_low_processor_usage_mode_sleep_usec", "usec"), &_OS::set_low_processor_usage_mode_sleep_usec); + ClassDB::bind_method(D_METHOD("get_low_processor_usage_mode_sleep_usec"), &_OS::get_low_processor_usage_mode_sleep_usec); + ClassDB::bind_method(D_METHOD("get_processor_count"), &_OS::get_processor_count); ClassDB::bind_method(D_METHOD("get_executable_path"), &_OS::get_executable_path); - ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "blocking", "output"), &_OS::execute, DEFVAL(Array())); + ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "blocking", "output", "read_stderr"), &_OS::execute, DEFVAL(Array()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("kill", "pid"), &_OS::kill); ClassDB::bind_method(D_METHOD("shell_open", "uri"), &_OS::shell_open); ClassDB::bind_method(D_METHOD("get_process_id"), &_OS::get_process_id); @@ -1199,6 +1278,7 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_system_time_secs"), &_OS::get_system_time_secs); ClassDB::bind_method(D_METHOD("get_system_time_msecs"), &_OS::get_system_time_msecs); + ClassDB::bind_method(D_METHOD("set_native_icon", "filename"), &_OS::set_native_icon); ClassDB::bind_method(D_METHOD("set_icon", "icon"), &_OS::set_icon); ClassDB::bind_method(D_METHOD("get_exit_code"), &_OS::get_exit_code); @@ -1264,6 +1344,9 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("set_use_vsync", "enable"), &_OS::set_use_vsync); ClassDB::bind_method(D_METHOD("is_vsync_enabled"), &_OS::is_vsync_enabled); + ClassDB::bind_method(D_METHOD("set_vsync_via_compositor", "enable"), &_OS::set_vsync_via_compositor); + ClassDB::bind_method(D_METHOD("is_vsync_via_compositor_enabled"), &_OS::is_vsync_via_compositor_enabled); + ClassDB::bind_method(D_METHOD("has_feature", "tag_name"), &_OS::has_feature); ClassDB::bind_method(D_METHOD("get_power_state"), &_OS::get_power_state); @@ -1271,13 +1354,19 @@ void _OS::_bind_methods() { 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); ADD_PROPERTY(PropertyInfo(Variant::STRING, "clipboard"), "set_clipboard", "get_clipboard"); ADD_PROPERTY(PropertyInfo(Variant::INT, "current_screen"), "set_current_screen", "get_current_screen"); ADD_PROPERTY(PropertyInfo(Variant::INT, "exit_code"), "set_exit_code", "get_exit_code"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vsync_enabled"), "set_use_vsync", "is_vsync_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vsync_via_compositor"), "set_vsync_via_compositor", "is_vsync_via_compositor_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "low_processor_usage_mode"), "set_low_processor_usage_mode", "is_in_low_processor_usage_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "low_processor_usage_mode_sleep_usec"), "set_low_processor_usage_mode_sleep_usec", "get_low_processor_usage_mode_sleep_usec"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_screen_on"), "set_keep_screen_on", "is_keep_screen_on"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "min_window_size"), "set_min_window_size", "get_min_window_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "max_window_size"), "set_max_window_size", "get_max_window_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait,Reverse Landscape,Reverse Portrait,Sensor Landscape,Sensor Portrait,Sensor"), "set_screen_orientation", "get_screen_orientation"); ADD_GROUP("Window", "window_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_borderless"), "set_borderless_window", "get_borderless_window"); @@ -1289,6 +1378,28 @@ void _OS::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "window_position"), "set_window_position", "get_window_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "window_size"), "set_window_size", "get_window_size"); + // Those default values need to be specified for the docs generator, + // to avoid using values from the documentation writer's own OS instance. + ADD_PROPERTY_DEFAULT("clipboard", ""); + ADD_PROPERTY_DEFAULT("current_screen", 0); + ADD_PROPERTY_DEFAULT("exit_code", 0); + ADD_PROPERTY_DEFAULT("vsync_enabled", true); + ADD_PROPERTY_DEFAULT("vsync_via_compositor", false); + ADD_PROPERTY_DEFAULT("low_processor_usage_mode", false); + ADD_PROPERTY_DEFAULT("low_processor_usage_mode_sleep_usec", 6900); + ADD_PROPERTY_DEFAULT("keep_screen_on", true); + ADD_PROPERTY_DEFAULT("min_window_size", Vector2()); + ADD_PROPERTY_DEFAULT("max_window_size", Vector2()); + ADD_PROPERTY_DEFAULT("screen_orientation", 0); + ADD_PROPERTY_DEFAULT("window_borderless", false); + ADD_PROPERTY_DEFAULT("window_per_pixel_transparency_enabled", false); + ADD_PROPERTY_DEFAULT("window_fullscreen", false); + ADD_PROPERTY_DEFAULT("window_maximized", false); + ADD_PROPERTY_DEFAULT("window_minimized", false); + ADD_PROPERTY_DEFAULT("window_resizable", true); + ADD_PROPERTY_DEFAULT("window_position", Vector2()); + ADD_PROPERTY_DEFAULT("window_size", Vector2()); + BIND_ENUM_CONSTANT(VIDEO_DRIVER_GLES2); BIND_ENUM_CONSTANT(VIDEO_DRIVER_GLES3); @@ -1365,6 +1476,11 @@ PoolVector<Plane> _Geometry::build_capsule_planes(float p_radius, float p_height return Geometry::build_capsule_planes(p_radius, p_height, p_sides, p_lats, p_axis); } +bool _Geometry::is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius) { + + return Geometry::is_point_in_circle(p_point, p_circle_pos, p_circle_radius); +} + real_t _Geometry::segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) { return Geometry::segment_intersects_circle(p_from, p_to, p_circle_pos, p_circle_radius); @@ -1491,11 +1607,26 @@ PoolVector<Vector3> _Geometry::segment_intersects_convex(const Vector3 &p_from, return r; } +bool _Geometry::is_polygon_clockwise(const Vector<Vector2> &p_polygon) { + + return Geometry::is_polygon_clockwise(p_polygon); +} + +bool _Geometry::is_point_in_polygon(const Point2 &p_point, const Vector<Vector2> &p_polygon) { + + return Geometry::is_point_in_polygon(p_point, p_polygon); +} + Vector<int> _Geometry::triangulate_polygon(const Vector<Vector2> &p_polygon) { return Geometry::triangulate_polygon(p_polygon); } +Vector<int> _Geometry::triangulate_delaunay_2d(const Vector<Vector2> &p_points) { + + return Geometry::triangulate_delaunay_2d(p_points); +} + Vector<Point2> _Geometry::convex_hull_2d(const Vector<Point2> &p_points) { return Geometry::convex_hull_2d(p_points); @@ -1506,6 +1637,102 @@ Vector<Vector3> _Geometry::clip_polygon(const Vector<Vector3> &p_points, const P return Geometry::clip_polygon(p_points, p_plane); } +Array _Geometry::merge_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { + + Vector<Vector<Point2> > polys = Geometry::merge_polygons_2d(p_polygon_a, p_polygon_b); + + Array ret; + + for (int i = 0; i < polys.size(); ++i) { + ret.push_back(polys[i]); + } + return ret; +} + +Array _Geometry::clip_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { + + Vector<Vector<Point2> > polys = Geometry::clip_polygons_2d(p_polygon_a, p_polygon_b); + + Array ret; + + for (int i = 0; i < polys.size(); ++i) { + ret.push_back(polys[i]); + } + return ret; +} + +Array _Geometry::intersect_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { + + Vector<Vector<Point2> > polys = Geometry::intersect_polygons_2d(p_polygon_a, p_polygon_b); + + Array ret; + + for (int i = 0; i < polys.size(); ++i) { + ret.push_back(polys[i]); + } + return ret; +} + +Array _Geometry::exclude_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { + + Vector<Vector<Point2> > polys = Geometry::exclude_polygons_2d(p_polygon_a, p_polygon_b); + + Array ret; + + for (int i = 0; i < polys.size(); ++i) { + ret.push_back(polys[i]); + } + return ret; +} + +Array _Geometry::clip_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { + + Vector<Vector<Point2> > polys = Geometry::clip_polyline_with_polygon_2d(p_polyline, p_polygon); + + Array ret; + + for (int i = 0; i < polys.size(); ++i) { + ret.push_back(polys[i]); + } + return ret; +} + +Array _Geometry::intersect_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { + + Vector<Vector<Point2> > polys = Geometry::intersect_polyline_with_polygon_2d(p_polyline, p_polygon); + + Array ret; + + for (int i = 0; i < polys.size(); ++i) { + ret.push_back(polys[i]); + } + return ret; +} + +Array _Geometry::offset_polygon_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) { + + Vector<Vector<Point2> > polys = Geometry::offset_polygon_2d(p_polygon, p_delta, Geometry::PolyJoinType(p_join_type)); + + Array ret; + + for (int i = 0; i < polys.size(); ++i) { + ret.push_back(polys[i]); + } + return ret; +} + +Array _Geometry::offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { + + Vector<Vector<Point2> > polys = Geometry::offset_polyline_2d(p_polygon, p_delta, Geometry::PolyJoinType(p_join_type), Geometry::PolyEndType(p_end_type)); + + Array ret; + + for (int i = 0; i < polys.size(); ++i) { + ret.push_back(polys[i]); + } + return ret; +} + Dictionary _Geometry::make_atlas(const Vector<Size2> &p_rects) { Dictionary ret; @@ -1544,6 +1771,7 @@ void _Geometry::_bind_methods() { ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &_Geometry::build_box_planes); ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &_Geometry::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z)); ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &_Geometry::build_capsule_planes, DEFVAL(Vector3::AXIS_Z)); + ClassDB::bind_method(D_METHOD("is_point_in_circle", "point", "circle_position", "circle_radius"), &_Geometry::is_point_in_circle); ClassDB::bind_method(D_METHOD("segment_intersects_circle", "segment_from", "segment_to", "circle_position", "circle_radius"), &_Geometry::segment_intersects_circle); ClassDB::bind_method(D_METHOD("segment_intersects_segment_2d", "from_a", "to_a", "from_b", "to_b"), &_Geometry::segment_intersects_segment_2d); ClassDB::bind_method(D_METHOD("line_intersects_line_2d", "from_a", "dir_a", "from_b", "dir_b"), &_Geometry::line_intersects_line_2d); @@ -1566,11 +1794,40 @@ void _Geometry::_bind_methods() { ClassDB::bind_method(D_METHOD("segment_intersects_convex", "from", "to", "planes"), &_Geometry::segment_intersects_convex); ClassDB::bind_method(D_METHOD("point_is_inside_triangle", "point", "a", "b", "c"), &_Geometry::point_is_inside_triangle); + ClassDB::bind_method(D_METHOD("is_polygon_clockwise", "polygon"), &_Geometry::is_polygon_clockwise); + ClassDB::bind_method(D_METHOD("is_point_in_polygon", "point", "polygon"), &_Geometry::is_point_in_polygon); ClassDB::bind_method(D_METHOD("triangulate_polygon", "polygon"), &_Geometry::triangulate_polygon); + ClassDB::bind_method(D_METHOD("triangulate_delaunay_2d", "points"), &_Geometry::triangulate_delaunay_2d); ClassDB::bind_method(D_METHOD("convex_hull_2d", "points"), &_Geometry::convex_hull_2d); ClassDB::bind_method(D_METHOD("clip_polygon", "points", "plane"), &_Geometry::clip_polygon); + ClassDB::bind_method(D_METHOD("merge_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::merge_polygons_2d); + ClassDB::bind_method(D_METHOD("clip_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::clip_polygons_2d); + ClassDB::bind_method(D_METHOD("intersect_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::intersect_polygons_2d); + ClassDB::bind_method(D_METHOD("exclude_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::exclude_polygons_2d); + + ClassDB::bind_method(D_METHOD("clip_polyline_with_polygon_2d", "polyline", "polygon"), &_Geometry::clip_polyline_with_polygon_2d); + ClassDB::bind_method(D_METHOD("intersect_polyline_with_polygon_2d", "polyline", "polygon"), &_Geometry::intersect_polyline_with_polygon_2d); + + ClassDB::bind_method(D_METHOD("offset_polygon_2d", "polygon", "delta", "join_type"), &_Geometry::offset_polygon_2d, DEFVAL(JOIN_SQUARE)); + ClassDB::bind_method(D_METHOD("offset_polyline_2d", "polyline", "delta", "join_type", "end_type"), &_Geometry::offset_polyline_2d, DEFVAL(JOIN_SQUARE), DEFVAL(END_SQUARE)); + ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &_Geometry::make_atlas); + + BIND_ENUM_CONSTANT(OPERATION_UNION); + BIND_ENUM_CONSTANT(OPERATION_DIFFERENCE); + BIND_ENUM_CONSTANT(OPERATION_INTERSECTION); + BIND_ENUM_CONSTANT(OPERATION_XOR); + + BIND_ENUM_CONSTANT(JOIN_SQUARE); + BIND_ENUM_CONSTANT(JOIN_ROUND); + BIND_ENUM_CONSTANT(JOIN_MITER); + + BIND_ENUM_CONSTANT(END_POLYGON); + BIND_ENUM_CONSTANT(END_JOINED); + BIND_ENUM_CONSTANT(END_BUTT); + BIND_ENUM_CONSTANT(END_SQUARE); + BIND_ENUM_CONSTANT(END_ROUND); } _Geometry::_Geometry() { @@ -1653,96 +1910,98 @@ bool _File::is_open() const { } String _File::get_path() const { - ERR_FAIL_COND_V(!f, ""); + ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); return f->get_path(); } String _File::get_path_absolute() const { - ERR_FAIL_COND_V(!f, ""); + ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); return f->get_path_absolute(); } void _File::seek(int64_t p_position) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->seek(p_position); } void _File::seek_end(int64_t p_position) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->seek_end(p_position); } int64_t _File::get_position() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_position(); } int64_t _File::get_len() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_len(); } bool _File::eof_reached() const { - ERR_FAIL_COND_V(!f, false); + ERR_FAIL_COND_V_MSG(!f, false, "File must be opened before use."); return f->eof_reached(); } uint8_t _File::get_8() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_8(); } uint16_t _File::get_16() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_16(); } uint32_t _File::get_32() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_32(); } uint64_t _File::get_64() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_64(); } float _File::get_float() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_float(); } double _File::get_double() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_double(); } real_t _File::get_real() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_real(); } PoolVector<uint8_t> _File::get_buffer(int p_length) const { PoolVector<uint8_t> data; - ERR_FAIL_COND_V(!f, data); + ERR_FAIL_COND_V_MSG(!f, data, "File must be opened before use."); - ERR_FAIL_COND_V(p_length < 0, data); + ERR_FAIL_COND_V_MSG(p_length < 0, data, "Length of buffer cannot be smaller than 0."); if (p_length == 0) return data; + Error err = data.resize(p_length); - ERR_FAIL_COND_V(err != OK, data); + ERR_FAIL_COND_V_MSG(err != OK, data, "Can't resize data to " + itos(p_length) + " elements."); + PoolVector<uint8_t>::Write w = data.write(); int len = f->get_buffer(&w[0], p_length); ERR_FAIL_COND_V(len < 0, PoolVector<uint8_t>()); - w = PoolVector<uint8_t>::Write(); + w.release(); if (len < p_length) data.resize(p_length); @@ -1752,7 +2011,7 @@ PoolVector<uint8_t> _File::get_buffer(int p_length) const { String _File::get_as_text() const { - ERR_FAIL_COND_V(!f, String()); + ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use."); String text; size_t original_pos = f->get_position(); @@ -1782,12 +2041,12 @@ String _File::get_sha256(const String &p_path) const { String _File::get_line() const { - ERR_FAIL_COND_V(!f, String()); + ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use."); return f->get_line(); } Vector<String> _File::get_csv_line(const String &p_delim) const { - ERR_FAIL_COND_V(!f, Vector<String>()); + ERR_FAIL_COND_V_MSG(!f, Vector<String>(), "File must be opened before use."); return f->get_csv_line(p_delim); } @@ -1816,83 +2075,83 @@ Error _File::get_error() const { void _File::store_8(uint8_t p_dest) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_8(p_dest); } void _File::store_16(uint16_t p_dest) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_16(p_dest); } void _File::store_32(uint32_t p_dest) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_32(p_dest); } void _File::store_64(uint64_t p_dest) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_64(p_dest); } void _File::store_float(float p_dest) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_float(p_dest); } void _File::store_double(double p_dest) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_double(p_dest); } void _File::store_real(real_t p_real) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_real(p_real); } void _File::store_string(const String &p_string) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_string(p_string); } void _File::store_pascal_string(const String &p_string) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_pascal_string(p_string); }; String _File::get_pascal_string() { - ERR_FAIL_COND_V(!f, ""); + ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); return f->get_pascal_string(); }; void _File::store_line(const String &p_string) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_line(p_string); } void _File::store_csv_line(const Vector<String> &p_values, const String &p_delim) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_csv_line(p_values, p_delim); } void _File::store_buffer(const PoolVector<uint8_t> &p_buffer) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); int len = p_buffer.size(); if (len == 0) @@ -1910,18 +2169,18 @@ bool _File::file_exists(const String &p_name) const { void _File::store_var(const Variant &p_var, bool p_full_objects) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); int len; Error err = encode_variant(p_var, NULL, len, p_full_objects); - ERR_FAIL_COND(err != OK); + ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant."); PoolVector<uint8_t> buff; buff.resize(len); - PoolVector<uint8_t>::Write w = buff.write(); + PoolVector<uint8_t>::Write w = buff.write(); err = encode_variant(p_var, &w[0], len, p_full_objects); - ERR_FAIL_COND(err != OK); - w = PoolVector<uint8_t>::Write(); + ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant."); + w.release(); store_32(len); store_buffer(buff); @@ -1929,7 +2188,7 @@ void _File::store_var(const Variant &p_var, bool p_full_objects) { Variant _File::get_var(bool p_allow_objects) const { - ERR_FAIL_COND_V(!f, Variant()); + ERR_FAIL_COND_V_MSG(!f, Variant(), "File must be opened before use."); uint32_t len = get_32(); PoolVector<uint8_t> buff = get_buffer(len); ERR_FAIL_COND_V((uint32_t)buff.size() != len, Variant()); @@ -1938,7 +2197,7 @@ Variant _File::get_var(bool p_allow_objects) const { Variant v; Error err = decode_variant(v, &r[0], len, NULL, p_allow_objects); - ERR_FAIL_COND_V(err != OK, Variant()); + ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to encode Variant."); return v; } @@ -2043,7 +2302,7 @@ Error _Directory::open(const String &p_path) { Error _Directory::list_dir_begin(bool p_skip_navigational, bool p_skip_hidden) { - ERR_FAIL_COND_V(!d, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); _list_skip_navigational = p_skip_navigational; _list_skip_hidden = p_skip_hidden; @@ -2053,7 +2312,7 @@ Error _Directory::list_dir_begin(bool p_skip_navigational, bool p_skip_hidden) { String _Directory::get_next() { - ERR_FAIL_COND_V(!d, ""); + ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use."); String next = d->get_next(); while (next != "" && ((_list_skip_navigational && (next == "." || next == "..")) || (_list_skip_hidden && d->current_is_hidden()))) { @@ -2064,44 +2323,44 @@ String _Directory::get_next() { } bool _Directory::current_is_dir() const { - ERR_FAIL_COND_V(!d, false); + ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use."); return d->current_is_dir(); } void _Directory::list_dir_end() { - ERR_FAIL_COND(!d); - return d->list_dir_end(); + ERR_FAIL_COND_MSG(!d, "Directory must be opened before use."); + d->list_dir_end(); } int _Directory::get_drive_count() { - ERR_FAIL_COND_V(!d, 0); + ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use."); return d->get_drive_count(); } String _Directory::get_drive(int p_drive) { - ERR_FAIL_COND_V(!d, ""); + ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use."); return d->get_drive(p_drive); } int _Directory::get_current_drive() { - ERR_FAIL_COND_V(!d, 0); + ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use."); return d->get_current_drive(); } Error _Directory::change_dir(String p_dir) { - ERR_FAIL_COND_V(!d, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); return d->change_dir(p_dir); } String _Directory::get_current_dir() { - ERR_FAIL_COND_V(!d, ""); + ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use."); return d->get_current_dir(); } Error _Directory::make_dir(String p_dir) { - ERR_FAIL_COND_V(!d, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); if (!p_dir.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); Error err = d->make_dir(p_dir); @@ -2112,7 +2371,7 @@ Error _Directory::make_dir(String p_dir) { } Error _Directory::make_dir_recursive(String p_dir) { - ERR_FAIL_COND_V(!d, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); if (!p_dir.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); Error err = d->make_dir_recursive(p_dir); @@ -2124,7 +2383,7 @@ Error _Directory::make_dir_recursive(String p_dir) { bool _Directory::file_exists(String p_file) { - ERR_FAIL_COND_V(!d, false); + ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use."); if (!p_file.is_rel_path()) { return FileAccess::exists(p_file); @@ -2134,7 +2393,7 @@ bool _Directory::file_exists(String p_file) { } bool _Directory::dir_exists(String p_dir) { - ERR_FAIL_COND_V(!d, false); + ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use."); if (!p_dir.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); @@ -2149,18 +2408,18 @@ bool _Directory::dir_exists(String p_dir) { int _Directory::get_space_left() { - ERR_FAIL_COND_V(!d, 0); + ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use."); return d->get_space_left() / 1024 * 1024; //return value in megabytes, given binding is int } Error _Directory::copy(String p_from, String p_to) { - ERR_FAIL_COND_V(!d, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); return d->copy(p_from, p_to); } Error _Directory::rename(String p_from, String p_to) { - ERR_FAIL_COND_V(!d, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); if (!p_from.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_from); Error err = d->rename(p_from, p_to); @@ -2172,7 +2431,7 @@ Error _Directory::rename(String p_from, String p_to) { } Error _Directory::remove(String p_name) { - ERR_FAIL_COND_V(!d, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); if (!p_name.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_name); Error err = d->remove(p_name); @@ -2227,24 +2486,17 @@ String _Marshalls::variant_to_base64(const Variant &p_var, bool p_full_objects) int len; Error err = encode_variant(p_var, NULL, len, p_full_objects); - ERR_FAIL_COND_V(err != OK, ""); + ERR_FAIL_COND_V_MSG(err != OK, "", "Error when trying to encode Variant."); PoolVector<uint8_t> buff; buff.resize(len); PoolVector<uint8_t>::Write w = buff.write(); err = encode_variant(p_var, &w[0], len, p_full_objects); - ERR_FAIL_COND_V(err != OK, ""); + ERR_FAIL_COND_V_MSG(err != OK, "", "Error when trying to encode Variant."); - int b64len = len / 3 * 4 + 4 + 1; - PoolVector<uint8_t> b64buff; - b64buff.resize(b64len); - PoolVector<uint8_t>::Write w64 = b64buff.write(); - - int strlen = base64_encode((char *)(&w64[0]), (char *)(&w[0]), len); - //OS::get_singleton()->print("len is %i, vector size is %i\n", b64len, strlen); - w64[strlen] = 0; - String ret = (char *)&w64[0]; + String ret = CryptoCore::b64_encode_str(&w[0], len); + ERR_FAIL_COND_V(ret == "", ret); return ret; }; @@ -2258,29 +2510,20 @@ Variant _Marshalls::base64_to_variant(const String &p_str, bool p_allow_objects) buf.resize(strlen / 4 * 3 + 1); PoolVector<uint8_t>::Write w = buf.write(); - int len = base64_decode((char *)(&w[0]), (char *)cstr.get_data(), strlen); + size_t len = 0; + ERR_FAIL_COND_V(CryptoCore::b64_decode(&w[0], buf.size(), &len, (unsigned char *)cstr.get_data(), strlen) != OK, Variant()); Variant v; Error err = decode_variant(v, &w[0], len, NULL, p_allow_objects); - ERR_FAIL_COND_V(err != OK, Variant()); + ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to decode Variant."); return v; }; String _Marshalls::raw_to_base64(const PoolVector<uint8_t> &p_arr) { - int len = p_arr.size(); - PoolVector<uint8_t>::Read r = p_arr.read(); - - int b64len = len / 3 * 4 + 4 + 1; - PoolVector<uint8_t> b64buff; - b64buff.resize(b64len); - PoolVector<uint8_t>::Write w64 = b64buff.write(); - - int strlen = base64_encode((char *)(&w64[0]), (char *)(&r[0]), len); - w64[strlen] = 0; - String ret = (char *)&w64[0]; - + String ret = CryptoCore::b64_encode_str(p_arr.read().ptr(), p_arr.size()); + ERR_FAIL_COND_V(ret == "", ret); return ret; }; @@ -2289,35 +2532,24 @@ PoolVector<uint8_t> _Marshalls::base64_to_raw(const String &p_str) { int strlen = p_str.length(); CharString cstr = p_str.ascii(); - int arr_len; + size_t arr_len = 0; PoolVector<uint8_t> buf; { buf.resize(strlen / 4 * 3 + 1); PoolVector<uint8_t>::Write w = buf.write(); - arr_len = base64_decode((char *)(&w[0]), (char *)cstr.get_data(), strlen); - }; + ERR_FAIL_COND_V(CryptoCore::b64_decode(&w[0], buf.size(), &arr_len, (unsigned char *)cstr.get_data(), strlen) != OK, PoolVector<uint8_t>()); + } buf.resize(arr_len); - // conversion from PoolVector<uint8_t> to raw array? return buf; }; String _Marshalls::utf8_to_base64(const String &p_str) { CharString cstr = p_str.utf8(); - int len = cstr.length(); - - int b64len = len / 3 * 4 + 4 + 1; - PoolVector<uint8_t> b64buff; - b64buff.resize(b64len); - PoolVector<uint8_t>::Write w64 = b64buff.write(); - - int strlen = base64_encode((char *)(&w64[0]), (char *)cstr.get_data(), len); - - w64[strlen] = 0; - String ret = (char *)&w64[0]; - + String ret = CryptoCore::b64_encode_str((unsigned char *)cstr.get_data(), cstr.length()); + ERR_FAIL_COND_V(ret == "", ret); return ret; }; @@ -2330,7 +2562,8 @@ String _Marshalls::base64_to_utf8(const String &p_str) { buf.resize(strlen / 4 * 3 + 1 + 1); PoolVector<uint8_t>::Write w = buf.write(); - int len = base64_decode((char *)(&w[0]), (char *)cstr.get_data(), strlen); + size_t len = 0; + ERR_FAIL_COND_V(CryptoCore::b64_decode(&w[0], buf.size(), &len, (unsigned char *)cstr.get_data(), strlen) != OK, String()); w[len] = 0; String ret = String::utf8((char *)&w[0]); @@ -2445,17 +2678,17 @@ void _Thread::_start_func(void *ud) { reason = "Method Not Found"; } break; - default: {} + default: { + } } - ERR_EXPLAIN("Could not call function '" + t->target_method.operator String() + "'' starting thread ID: " + t->get_id() + " Reason: " + reason); - ERR_FAIL(); + ERR_FAIL_MSG("Could not call function '" + t->target_method.operator String() + "' to start thread " + t->get_id() + ": " + reason + "."); } } Error _Thread::start(Object *p_instance, const StringName &p_method, const Variant &p_userdata, Priority p_priority) { - ERR_FAIL_COND_V(active, ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V_MSG(active, ERR_ALREADY_IN_USE, "Thread already started."); ERR_FAIL_COND_V(!p_instance, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_method == StringName(), ERR_INVALID_PARAMETER); ERR_FAIL_INDEX_V(p_priority, PRIORITY_MAX, ERR_INVALID_PARAMETER); @@ -2496,14 +2729,16 @@ bool _Thread::is_active() const { } Variant _Thread::wait_to_finish() { - ERR_FAIL_COND_V(!thread, Variant()); - ERR_FAIL_COND_V(!active, Variant()); + ERR_FAIL_COND_V_MSG(!thread, Variant(), "Thread must exist to wait for its completion."); + ERR_FAIL_COND_V_MSG(!active, Variant(), "Thread must be active to wait for its completion."); Thread::wait_to_finish(thread); Variant r = ret; active = false; target_method = StringName(); target_instance = NULL; userdata = Variant(); + if (thread) + memdelete(thread); thread = NULL; return r; @@ -2529,10 +2764,7 @@ _Thread::_Thread() { _Thread::~_Thread() { - if (active) { - ERR_EXPLAIN("Reference to a Thread object object was lost while the thread is still running..."); - } - ERR_FAIL_COND(active); + ERR_FAIL_COND_MSG(active, "Reference to a Thread object object was lost while the thread is still running..."); } ///////////////////////////////////// @@ -2766,6 +2998,10 @@ float _Engine::get_physics_jitter_fix() const { return Engine::get_singleton()->get_physics_jitter_fix(); } +float _Engine::get_physics_interpolation_fraction() const { + return Engine::get_singleton()->get_physics_interpolation_fraction(); +} + void _Engine::set_target_fps(int p_fps) { Engine::get_singleton()->set_target_fps(p_fps); } @@ -2854,6 +3090,7 @@ void _Engine::_bind_methods() { ClassDB::bind_method(D_METHOD("get_iterations_per_second"), &_Engine::get_iterations_per_second); ClassDB::bind_method(D_METHOD("set_physics_jitter_fix", "physics_jitter_fix"), &_Engine::set_physics_jitter_fix); ClassDB::bind_method(D_METHOD("get_physics_jitter_fix"), &_Engine::get_physics_jitter_fix); + ClassDB::bind_method(D_METHOD("get_physics_interpolation_fraction"), &_Engine::get_physics_interpolation_fraction); ClassDB::bind_method(D_METHOD("set_target_fps", "target_fps"), &_Engine::set_target_fps); ClassDB::bind_method(D_METHOD("get_target_fps"), &_Engine::get_target_fps); @@ -2957,6 +3194,9 @@ 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)); + } return result; } diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 2906de4a4a..d57da36ca5 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -109,11 +109,11 @@ public: }; 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 */ + 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 Weekday { @@ -127,8 +127,8 @@ public: }; enum Month { - /// Start at 1 to follow Windows SYSTEMTIME structure - /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx + // Start at 1 to follow Windows SYSTEMTIME structure + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx MONTH_JANUARY = 1, MONTH_FEBRUARY, MONTH_MARCH, @@ -143,6 +143,11 @@ public: MONTH_DECEMBER }; + void global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta); + void global_menu_add_separator(const String &p_menu); + void global_menu_remove_item(const String &p_menu, int p_idx); + void global_menu_clear(const String &p_menu); + Point2 get_mouse_position() const; void set_window_title(const String &p_title); int get_mouse_button_state() const; @@ -175,9 +180,13 @@ public: virtual int get_screen_dpi(int p_screen = -1) const; virtual Point2 get_window_position() const; virtual void set_window_position(const Point2 &p_position); + virtual Size2 get_max_window_size() const; + virtual Size2 get_min_window_size() const; virtual Size2 get_window_size() const; virtual Size2 get_real_window_size() const; virtual Rect2 get_window_safe_area() const; + virtual void set_max_window_size(const Size2 &p_size); + virtual void set_min_window_size(const Size2 &p_size); virtual void set_window_size(const Size2 &p_size); virtual void set_window_fullscreen(bool p_enabled); virtual bool is_window_fullscreen() const; @@ -213,8 +222,11 @@ public: void set_low_processor_usage_mode(bool p_enabled); bool is_in_low_processor_usage_mode() const; + void set_low_processor_usage_mode_sleep_usec(int p_usec); + int get_low_processor_usage_mode_sleep_usec() const; + String get_executable_path() const; - int execute(const String &p_path, const Vector<String> &p_arguments, bool p_blocking, Array p_output = Array()); + int execute(const String &p_path, const Vector<String> &p_arguments, bool p_blocking, Array p_output = Array(), bool p_read_stderr = false); Error kill(int p_pid); Error shell_open(String p_uri); @@ -255,26 +267,9 @@ public: bool is_scancode_unicode(uint32_t p_unicode) const; int find_scancode_from_string(const String &p_code) const; - /* - struct Date { - - int year; - Month month; - int day; - Weekday weekday; - bool dst; - }; - - struct Time { - - int hour; - int min; - int sec; - }; -*/ - void set_use_file_access_save_and_swap(bool p_enable); + void set_native_icon(const String &p_filename); void set_icon(const Ref<Image> &p_icon); int get_exit_code() const; @@ -350,6 +345,9 @@ public: void set_use_vsync(bool p_enable); bool is_vsync_enabled() const; + 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(); @@ -357,6 +355,8 @@ public: bool has_feature(const String &p_feature) const; bool request_permission(const String &p_name); + bool request_permissions(); + Vector<String> get_granted_permissions() const; static _OS *get_singleton() { return singleton; } @@ -399,18 +399,58 @@ public: PoolVector<Vector3> segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius); PoolVector<Vector3> segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius); PoolVector<Vector3> segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector<Plane> &p_planes); + bool is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius); real_t segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius); int get_uv84_normal_bit(const Vector3 &p_vector); + bool is_polygon_clockwise(const Vector<Vector2> &p_polygon); + bool is_point_in_polygon(const Point2 &p_point, const Vector<Vector2> &p_polygon); Vector<int> triangulate_polygon(const Vector<Vector2> &p_polygon); + Vector<int> triangulate_delaunay_2d(const Vector<Vector2> &p_points); Vector<Point2> convex_hull_2d(const Vector<Point2> &p_points); Vector<Vector3> clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane); + enum PolyBooleanOperation { + OPERATION_UNION, + OPERATION_DIFFERENCE, + OPERATION_INTERSECTION, + OPERATION_XOR + }; + // 2D polygon boolean operations. + Array merge_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Union (add). + Array clip_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Difference (subtract). + Array intersect_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Common area (multiply). + Array exclude_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // All but common area (xor). + + // 2D polyline vs polygon operations. + Array clip_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // Cut. + Array intersect_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // Chop. + + // 2D offset polygons/polylines. + enum PolyJoinType { + JOIN_SQUARE, + JOIN_ROUND, + JOIN_MITER + }; + enum PolyEndType { + END_POLYGON, + END_JOINED, + END_BUTT, + END_SQUARE, + END_ROUND + }; + Array offset_polygon_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE); + Array offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE, PolyEndType p_end_type = END_SQUARE); + Dictionary make_atlas(const Vector<Size2> &p_rects); _Geometry(); }; +VARIANT_ENUM_CAST(_Geometry::PolyBooleanOperation); +VARIANT_ENUM_CAST(_Geometry::PolyJoinType); +VARIANT_ENUM_CAST(_Geometry::PolyEndType); + class _File : public Reference { GDCLASS(_File, Reference); @@ -440,24 +480,24 @@ public: Error open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass); Error open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode = COMPRESSION_FASTLZ); - Error open(const String &p_path, ModeFlags p_mode_flags); ///< open a file - void close(); ///< close a file - bool is_open() const; ///< true when file is open + Error open(const String &p_path, ModeFlags p_mode_flags); // open a file. + void close(); // Close a file. + bool is_open() const; // True when file is open. - String get_path() const; /// returns the path for the current open file - String get_path_absolute() const; /// returns the absolute path for the current open file + String get_path() const; // Returns the path for the current open file. + String get_path_absolute() const; // Returns the absolute path for the current open file. - void seek(int64_t p_position); ///< seek to a given position - void seek_end(int64_t p_position = 0); ///< seek from the end of file - int64_t get_position() const; ///< get position in the file - int64_t get_len() const; ///< get size of the file + void seek(int64_t p_position); // Seek to a given position. + void seek_end(int64_t p_position = 0); // Seek from the end of file. + int64_t get_position() const; // Get position in the file. + int64_t get_len() const; // Get size of the file. - bool eof_reached() const; ///< reading passed EOF + bool eof_reached() const; // Reading passed EOF. - uint8_t get_8() const; ///< get a byte - uint16_t get_16() const; ///< get 16 bits uint - uint32_t get_32() const; ///< get 32 bits uint - uint64_t get_64() const; ///< get 64 bits uint + uint8_t get_8() const; // Get a byte. + uint16_t get_16() const; // Get 16 bits uint. + uint32_t get_32() const; // Get 32 bits uint. + uint64_t get_64() const; // Get 64 bits uint. float get_float() const; double get_double() const; @@ -465,27 +505,27 @@ public: Variant get_var(bool p_allow_objects = false) const; - PoolVector<uint8_t> get_buffer(int p_length) const; ///< get an array of bytes + PoolVector<uint8_t> get_buffer(int p_length) const; // Get an array of bytes. String get_line() const; Vector<String> get_csv_line(const String &p_delim = ",") const; String get_as_text() const; String get_md5(const String &p_path) const; String get_sha256(const String &p_path) const; - /**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac) + /* Use this for files WRITTEN in _big_ endian machines (ie, amiga/mac). * It's not about the current CPU type but file formats. - * this flags get reset to false (little endian) on each open + * This flags get reset to false (little endian) on each open. */ void set_endian_swap(bool p_swap); bool get_endian_swap(); - Error get_error() const; ///< get last error + Error get_error() const; // Get last error. - void store_8(uint8_t p_dest); ///< store a byte - void store_16(uint16_t p_dest); ///< store 16 bits uint - void store_32(uint32_t p_dest); ///< store 32 bits uint - void store_64(uint64_t p_dest); ///< store 64 bits uint + void store_8(uint8_t p_dest); // Store a byte. + void store_16(uint16_t p_dest); // Store 16 bits uint. + void store_32(uint32_t p_dest); // Store 32 bits uint. + void store_64(uint64_t p_dest); // Store 64 bits uint. void store_float(float p_dest); void store_double(double p_dest); @@ -498,11 +538,11 @@ public: virtual void store_pascal_string(const String &p_string); virtual String get_pascal_string(); - void store_buffer(const PoolVector<uint8_t> &p_buffer); ///< store an array of bytes + void store_buffer(const PoolVector<uint8_t> &p_buffer); // Store an array of bytes. void store_var(const Variant &p_var, bool p_full_objects = false); - bool file_exists(const String &p_name) const; ///< return true if a file exists + bool file_exists(const String &p_name) const; // Return true if a file exists. uint64_t get_modified_time(const String &p_file) const; @@ -524,18 +564,18 @@ protected: public: Error open(const String &p_path); - Error list_dir_begin(bool p_skip_navigational = false, bool p_skip_hidden = false); ///< This starts dir listing + Error list_dir_begin(bool p_skip_navigational = false, bool p_skip_hidden = false); // This starts dir listing. String get_next(); bool current_is_dir() const; - void list_dir_end(); ///< + void list_dir_end(); int get_drive_count(); String get_drive(int p_drive); int get_current_drive(); - Error change_dir(String p_dir); ///< can be relative or absolute, return false on success - String get_current_dir(); ///< return current dir location + Error change_dir(String p_dir); // Can be relative or absolute, return false on success. + String get_current_dir(); // Return current dir location. Error make_dir(String p_dir); Error make_dir_recursive(String p_dir); @@ -649,7 +689,7 @@ VARIANT_ENUM_CAST(_Thread::Priority); class _ClassDB : public Object { - GDCLASS(_ClassDB, Object) + GDCLASS(_ClassDB, Object); protected: static void _bind_methods(); @@ -700,6 +740,7 @@ public: void set_physics_jitter_fix(float p_threshold); float get_physics_jitter_fix() const; + float get_physics_interpolation_fraction() const; void set_target_fps(int p_fps); int get_target_fps() const; @@ -734,7 +775,7 @@ public: class _JSON; class JSONParseResult : public Reference { - GDCLASS(JSONParseResult, Reference) + GDCLASS(JSONParseResult, Reference); friend class _JSON; @@ -759,10 +800,13 @@ public: void set_result(const Variant &p_result); Variant get_result() const; + + JSONParseResult() : + error_line(-1) {} }; class _JSON : public Object { - GDCLASS(_JSON, Object) + GDCLASS(_JSON, Object); protected: static void _bind_methods(); diff --git a/core/class_db.cpp b/core/class_db.cpp index f7b446707d..f52937bdca 100644 --- a/core/class_db.cpp +++ b/core/class_db.cpp @@ -312,6 +312,19 @@ void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringNa } } +void ClassDB::get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) { + + OBJTYPE_RLOCK; + + const StringName *k = NULL; + + while ((k = classes.next(k))) { + + if (*k != p_class && get_parent_class(*k) == p_class) + p_classes->push_back(*k); + } +} + StringName ClassDB::get_parent_class_nocheck(const StringName &p_class) { OBJTYPE_RLOCK; @@ -327,7 +340,7 @@ StringName ClassDB::get_parent_class(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_COND_V(!ti, StringName()); + ERR_FAIL_COND_V_MSG(!ti, StringName(), "Cannot get class '" + String(p_class) + "'."); return ti->inherits; } @@ -337,7 +350,7 @@ ClassDB::APIType ClassDB::get_api_type(const StringName &p_class) { ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_COND_V(!ti, API_NONE); + ERR_FAIL_COND_V_MSG(!ti, API_NONE, "Cannot get class '" + String(p_class) + "'."); return ti->api; } @@ -362,7 +375,7 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { for (List<StringName>::Element *E = names.front(); E; E = E->next()) { ClassInfo *t = classes.getptr(E->get()); - ERR_FAIL_COND_V(!t, 0); + ERR_FAIL_COND_V_MSG(!t, 0, "Cannot get class '" + String(E->get()) + "'."); if (t->api != p_api || !t->exposed) continue; hash = hash_djb2_one_64(t->name.hash(), hash); @@ -467,6 +480,7 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { PropertySetGet *psg = t->property_setget.getptr(F->get()); + ERR_FAIL_COND_V(!psg, 0); hash = hash_djb2_one_64(F->get().hash(), hash); hash = hash_djb2_one_64(psg->setter.hash(), hash); @@ -514,8 +528,8 @@ Object *ClassDB::instance(const StringName &p_class) { ti = classes.getptr(compat_classes[p_class]); } } - ERR_FAIL_COND_V(!ti, NULL); - ERR_FAIL_COND_V(ti->disabled, NULL); + ERR_FAIL_COND_V_MSG(!ti, NULL, "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_COND_V_MSG(ti->disabled, NULL, "Class '" + String(p_class) + "' is disabled."); ERR_FAIL_COND_V(!ti->creation_func, NULL); } #ifdef TOOLS_ENABLED @@ -531,7 +545,12 @@ bool ClassDB::can_instance(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_COND_V(!ti, false); + ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'."); +#ifdef TOOLS_ENABLED + if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { + return false; + } +#endif return (!ti->disabled && ti->creation_func != NULL); } @@ -539,9 +558,9 @@ void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherit OBJTYPE_WLOCK; - StringName name = p_class; + const StringName &name = p_class; - ERR_FAIL_COND(classes.has(name)); + ERR_FAIL_COND_MSG(classes.has(name), "Class '" + String(p_class) + "' already exists."); classes[name] = ClassInfo(); ClassInfo &ti = classes[name]; @@ -653,10 +672,8 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); - if (!type) { - ERR_FAIL_COND(!type); - } + ERR_FAIL_COND(!type); if (type->constant_map.has(p_name)) { @@ -819,10 +836,7 @@ void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) { #ifdef DEBUG_METHODS_ENABLED ClassInfo *check = type; while (check) { - if (check->signal_map.has(sname)) { - ERR_EXPLAIN("Type " + String(p_class) + " already has signal: " + String(sname)); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(check->signal_map.has(sname), "Class '" + String(p_class) + "' already has signal '" + String(sname) + "'."); check = check->inherits_ptr; } #endif @@ -907,16 +921,11 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons if (p_setter) { mb_set = get_method(p_class, p_setter); #ifdef DEBUG_METHODS_ENABLED - if (!mb_set) { - ERR_EXPLAIN("Invalid Setter: " + p_class + "::" + p_setter + " for property: " + p_pinfo.name); - ERR_FAIL_COND(!mb_set); - } else { - int exp_args = 1 + (p_index >= 0 ? 1 : 0); - if (mb_set->get_argument_count() != exp_args) { - ERR_EXPLAIN("Invalid Function for Setter: " + p_class + "::" + p_setter + " for property: " + p_pinfo.name); - ERR_FAIL(); - } - } + + ERR_FAIL_COND_MSG(!mb_set, "Invalid setter '" + p_class + "::" + p_setter + "' for property '" + p_pinfo.name + "'."); + + int exp_args = 1 + (p_index >= 0 ? 1 : 0); + ERR_FAIL_COND_MSG(mb_set->get_argument_count() != exp_args, "Invalid function for setter '" + p_class + "::" + p_setter + " for property '" + p_pinfo.name + "'."); #endif } @@ -926,25 +935,15 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons mb_get = get_method(p_class, p_getter); #ifdef DEBUG_METHODS_ENABLED - if (!mb_get) { - ERR_EXPLAIN("Invalid Getter: " + p_class + "::" + p_getter + " for property: " + p_pinfo.name); - ERR_FAIL_COND(!mb_get); - } else { + ERR_FAIL_COND_MSG(!mb_get, "Invalid getter '" + p_class + "::" + p_getter + "' for property '" + p_pinfo.name + "'."); - int exp_args = 0 + (p_index >= 0 ? 1 : 0); - if (mb_get->get_argument_count() != exp_args) { - ERR_EXPLAIN("Invalid Function for Getter: " + p_class + "::" + p_getter + " for property: " + p_pinfo.name); - ERR_FAIL(); - } - } + int exp_args = 0 + (p_index >= 0 ? 1 : 0); + ERR_FAIL_COND_MSG(mb_get->get_argument_count() != exp_args, "Invalid function for getter '" + p_class + "::" + p_getter + "' for property: '" + p_pinfo.name + "'."); #endif } #ifdef DEBUG_METHODS_ENABLED - if (type->property_setget.has(p_pinfo.name)) { - ERR_EXPLAIN("Object " + p_class + " already has property: " + p_pinfo.name); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(type->property_setget.has(p_pinfo.name), "Object '" + p_class + "' already has property '" + p_pinfo.name + "'."); #endif OBJTYPE_WLOCK @@ -969,6 +968,13 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons type->property_setget[p_pinfo.name] = psg; } +void ClassDB::set_property_default_value(StringName p_class, const StringName &p_name, const Variant &p_default) { + if (!default_values.has(p_class)) { + default_values[p_class] = HashMap<StringName, Variant>(); + } + default_values[p_class][p_name] = p_default; +} + void ClassDB::get_property_list(StringName p_class, List<PropertyInfo> *p_list, bool p_no_inheritance, const Object *p_validator) { OBJTYPE_RLOCK; @@ -1125,7 +1131,7 @@ Variant::Type ClassDB::get_property_type(const StringName &p_class, const String return Variant::NIL; } -StringName ClassDB::get_property_setter(StringName p_class, const StringName p_property) { +StringName ClassDB::get_property_setter(StringName p_class, const StringName &p_property) { ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; @@ -1142,7 +1148,7 @@ StringName ClassDB::get_property_setter(StringName p_class, const StringName p_p return StringName(); } -StringName ClassDB::get_property_getter(StringName p_class, const StringName p_property) { +StringName ClassDB::get_property_getter(StringName p_class, const StringName &p_property) { ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; @@ -1216,32 +1222,26 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c #ifdef DEBUG_ENABLED - if (has_method(instance_type, mdname)) { - ERR_EXPLAIN("Class " + String(instance_type) + " already has a method " + String(mdname)); - ERR_FAIL_V(NULL); - } + ERR_FAIL_COND_V_MSG(has_method(instance_type, mdname), NULL, "Class " + String(instance_type) + " already has a method " + String(mdname) + "."); #endif ClassInfo *type = classes.getptr(instance_type); if (!type) { - ERR_PRINTS("Couldn't bind method '" + mdname + "' for instance: " + instance_type); memdelete(p_bind); - ERR_FAIL_V(NULL); + ERR_FAIL_V_MSG(NULL, "Couldn't bind method '" + mdname + "' for instance '" + instance_type + "'."); } if (type->method_map.has(mdname)) { memdelete(p_bind); // overloading not supported - ERR_EXPLAIN("Method already bound: " + instance_type + "::" + mdname); - ERR_FAIL_V(NULL); + ERR_FAIL_V_MSG(NULL, "Method already bound '" + instance_type + "::" + mdname + "'."); } #ifdef DEBUG_METHODS_ENABLED if (method_name.args.size() > p_bind->get_argument_count()) { memdelete(p_bind); - ERR_EXPLAIN("Method definition provides more arguments than the method actually has: " + instance_type + "::" + mdname); - ERR_FAIL_V(NULL); + ERR_FAIL_V_MSG(NULL, "Method definition provides more arguments than the method actually has '" + instance_type + "::" + mdname + "'."); } p_bind->set_argument_names(method_name.args); @@ -1265,7 +1265,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c } void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual) { - ERR_FAIL_COND(!classes.has(p_class)); + ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); OBJTYPE_WLOCK; @@ -1280,7 +1280,7 @@ void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_ void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance) { - ERR_FAIL_COND(!classes.has(p_class)); + ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); #ifdef DEBUG_METHODS_ENABLED @@ -1304,7 +1304,7 @@ void ClassDB::set_class_enabled(StringName p_class, bool p_enable) { OBJTYPE_WLOCK; - ERR_FAIL_COND(!classes.has(p_class)); + ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); classes[p_class].disabled = !p_enable; } @@ -1319,7 +1319,7 @@ bool ClassDB::is_class_enabled(StringName p_class) { } } - ERR_FAIL_COND_V(!ti, false); + ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'."); return !ti->disabled; } @@ -1328,7 +1328,7 @@ bool ClassDB::is_class_exposed(StringName p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_COND_V(!ti, false); + ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'."); return ti->exposed; } @@ -1372,37 +1372,60 @@ void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p } HashMap<StringName, HashMap<StringName, Variant> > ClassDB::default_values; +Set<StringName> ClassDB::default_values_cached; -Variant ClassDB::class_get_default_property_value(const StringName &p_class, const StringName &p_property) { +Variant ClassDB::class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid) { - if (!default_values.has(p_class)) { + if (!default_values_cached.has(p_class)) { - default_values[p_class] = HashMap<StringName, Variant>(); + if (!default_values.has(p_class)) { + default_values[p_class] = HashMap<StringName, Variant>(); + } - if (ClassDB::can_instance(p_class)) { + Object *c = NULL; + bool cleanup_c = false; + + if (Engine::get_singleton()->has_singleton(p_class)) { + c = Engine::get_singleton()->get_singleton_object(p_class); + cleanup_c = false; + } else if (ClassDB::can_instance(p_class)) { + c = ClassDB::instance(p_class); + cleanup_c = true; + } + + if (c) { - Object *c = ClassDB::instance(p_class); List<PropertyInfo> plist; c->get_property_list(&plist); for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { if (E->get().usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR)) { - Variant v = c->get(E->get().name); - default_values[p_class][E->get().name] = v; + if (!default_values[p_class].has(E->get().name)) { + Variant v = c->get(E->get().name); + default_values[p_class][E->get().name] = v; + } } } - memdelete(c); + + if (cleanup_c) { + memdelete(c); + } } + + default_values_cached.insert(p_class); } if (!default_values.has(p_class)) { + if (r_valid != NULL) *r_valid = false; return Variant(); } if (!default_values[p_class].has(p_property)) { + if (r_valid != NULL) *r_valid = false; return Variant(); } + if (r_valid != NULL) *r_valid = true; return default_values[p_class][p_property]; } @@ -1413,6 +1436,12 @@ void ClassDB::init() { lock = RWLock::create(); } +void ClassDB::cleanup_defaults() { + + default_values.clear(); + default_values_cached.clear(); +} + void ClassDB::cleanup() { //OBJTYPE_LOCK; hah not here @@ -1432,7 +1461,6 @@ void ClassDB::cleanup() { classes.clear(); resource_base_extensions.clear(); compat_classes.clear(); - default_values.clear(); memdelete(lock); } diff --git a/core/class_db.h b/core/class_db.h index f18a7113d7..5df425e662 100644 --- a/core/class_db.h +++ b/core/class_db.h @@ -35,10 +35,6 @@ #include "core/object.h" #include "core/print_string.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - /** To bind more then 6 parameters include this: * #include "core/method_bind_ext.gen.inc" */ @@ -162,6 +158,7 @@ public: static void _add_class2(const StringName &p_class, const StringName &p_inherits); static HashMap<StringName, HashMap<StringName, Variant> > default_values; + static Set<StringName> default_values_cached; public: // DO NOT USE THIS!!!!!! NEEDS TO BE PUBLIC BUT DO NOT USE NO MATTER WHAT!!! @@ -214,6 +211,7 @@ public: static void get_class_list(List<StringName> *p_classes); static void get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes); + static void get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes); static StringName get_parent_class_nocheck(const StringName &p_class); static StringName get_parent_class(const StringName &p_class); static bool class_exists(const StringName &p_class); @@ -286,6 +284,15 @@ public: return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 6); } + template <class N, class M> + static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4, const Variant &p_def5, const Variant &p_def6, const Variant &p_def7) { + + MethodBind *bind = create_method_bind(p_method); + const Variant *ptr[7] = { &p_def1, &p_def2, &p_def3, &p_def4, &p_def5, &p_def6, &p_def7 }; + + return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 7); + } + template <class M> static MethodBind *bind_vararg_method(uint32_t p_flags, StringName p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const Vector<Variant> &p_default_args = Vector<Variant>()) { @@ -308,8 +315,7 @@ public: if (type->method_map.has(p_name)) { memdelete(bind); // overloading not supported - ERR_EXPLAIN("Method already bound: " + instance_type + "::" + p_name); - ERR_FAIL_V(NULL); + ERR_FAIL_V_MSG(NULL, "Method already bound: " + instance_type + "::" + p_name + "."); } type->method_map[p_name] = bind; #ifdef DEBUG_METHODS_ENABLED @@ -328,14 +334,15 @@ public: static void add_property_group(StringName p_class, const String &p_name, const String &p_prefix = ""); static void add_property(StringName p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index = -1); + static void set_property_default_value(StringName p_class, const StringName &p_name, const Variant &p_default); static void get_property_list(StringName p_class, List<PropertyInfo> *p_list, bool p_no_inheritance = false, const Object *p_validator = NULL); static bool set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid = NULL); static bool get_property(Object *p_object, const StringName &p_property, Variant &r_value); static bool has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance = false); static int get_property_index(const StringName &p_class, const StringName &p_property, bool *r_is_valid = NULL); static Variant::Type get_property_type(const StringName &p_class, const StringName &p_property, bool *r_is_valid = NULL); - static StringName get_property_setter(StringName p_class, const StringName p_property); - static StringName get_property_getter(StringName p_class, const StringName p_property); + static StringName get_property_setter(StringName p_class, const StringName &p_property); + static StringName get_property_getter(StringName p_class, const StringName &p_property); static bool has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false); static void set_method_flags(StringName p_class, StringName p_method, int p_flags); @@ -354,7 +361,7 @@ public: static void get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance = false); static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance = false); - static Variant class_get_default_property_value(const StringName &p_class, const StringName &p_property); + static Variant class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid = NULL); static StringName get_category(const StringName &p_node); @@ -372,6 +379,7 @@ public: static void set_current_api(APIType p_api); static APIType get_current_api(); + static void cleanup_defaults(); static void cleanup(); }; diff --git a/core/color.cpp b/core/color.cpp index efd2941b47..a6ad50b745 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -214,6 +214,11 @@ void Color::set_hsv(float p_h, float p_s, float p_v, float p_alpha) { } } +bool Color::is_equal_approx(const Color &p_color) const { + + return Math::is_equal_approx(r, p_color.r) && Math::is_equal_approx(g, p_color.g) && Math::is_equal_approx(b, p_color.b) && Math::is_equal_approx(a, p_color.a); +} + void Color::invert() { r = 1.0 - r; @@ -335,36 +340,23 @@ Color Color::html(const String &p_color) { } else if (color.length() == 6) { alpha = false; } else { - ERR_EXPLAIN("Invalid Color Code: " + p_color); - ERR_FAIL_V(Color()); + ERR_FAIL_V_MSG(Color(), "Invalid color code: " + p_color + "."); } int a = 255; if (alpha) { a = _parse_col(color, 0); - if (a < 0) { - ERR_EXPLAIN("Invalid Color Code: " + p_color); - ERR_FAIL_V(Color()); - } + ERR_FAIL_COND_V_MSG(a < 0, Color(), "Invalid color code: " + p_color + "."); } int from = alpha ? 2 : 0; int r = _parse_col(color, from + 0); - if (r < 0) { - ERR_EXPLAIN("Invalid Color Code: " + p_color); - ERR_FAIL_V(Color()); - } + ERR_FAIL_COND_V_MSG(r < 0, Color(), "Invalid color code: " + p_color + "."); int g = _parse_col(color, from + 2); - if (g < 0) { - ERR_EXPLAIN("Invalid Color Code: " + p_color); - ERR_FAIL_V(Color()); - } + ERR_FAIL_COND_V_MSG(g < 0, Color(), "Invalid color code: " + p_color + "."); int b = _parse_col(color, from + 4); - if (b < 0) { - ERR_EXPLAIN("Invalid Color Code: " + p_color); - ERR_FAIL_V(Color()); - } + ERR_FAIL_COND_V_MSG(b < 0, Color(), "Invalid color code: " + p_color + "."); return Color(r / 255.0, g / 255.0, b / 255.0, a / 255.0); } @@ -388,9 +380,8 @@ bool Color::html_is_valid(const String &p_color) { return false; } - int a = 255; if (alpha) { - a = _parse_col(color, 0); + int a = _parse_col(color, 0); if (a < 0) { return false; } @@ -426,12 +417,8 @@ Color Color::named(const String &p_name) { name = name.to_lower(); const Map<String, Color>::Element *color = _named_colors.find(name); - if (color) { - return color->value(); - } else { - ERR_EXPLAIN("Invalid Color Name: " + p_name); - ERR_FAIL_V(Color()); - } + ERR_FAIL_NULL_V_MSG(color, Color(), "Invalid color name: " + p_name + "."); + return color->value(); } String _to_hex(float p_val) { @@ -524,8 +511,7 @@ Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const { // FIXME: Remove once Godot 3.1 has been released float Color::gray() const { - ERR_EXPLAIN("Color.gray() is deprecated and will be removed in a future version. Use Color.get_v() for a better grayscale approximation."); - WARN_DEPRECATED + WARN_DEPRECATED_MSG("Color.gray() is deprecated and will be removed in a future version. Use Color.get_v() for a better grayscale approximation."); return (r + g + b) / 3.0; } diff --git a/core/color.h b/core/color.h index b2148e1357..b34a82ef19 100644 --- a/core/color.h +++ b/core/color.h @@ -33,9 +33,7 @@ #include "core/math/math_funcs.h" #include "core/ustring.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ + struct Color { union { @@ -88,6 +86,8 @@ struct Color { void operator/=(const Color &p_color); void operator/=(const real_t &rvalue); + bool is_equal_approx(const Color &p_color) const; + void invert(); void contrast(); Color inverted() const; @@ -195,7 +195,7 @@ struct Color { static Color named(const String &p_name); String to_html(bool p_alpha = true) const; Color from_hsv(float p_h, float p_s, float p_v, float p_a) const; - static Color from_rgbe9995(uint32_t p_color); + static Color from_rgbe9995(uint32_t p_rgbe); _FORCE_INLINE_ bool operator<(const Color &p_color) const; //used in set keys operator String() const; diff --git a/core/color_names.inc b/core/color_names.inc index e126bfe0f8..b0ef507d92 100644 --- a/core/color_names.inc +++ b/core/color_names.inc @@ -143,6 +143,7 @@ static void _populate_named_colors() { _named_colors.insert("thistle", Color(0.85, 0.75, 0.85)); _named_colors.insert("tomato", Color(1.00, 0.39, 0.28)); _named_colors.insert("turquoise", Color(0.25, 0.88, 0.82)); + _named_colors.insert("transparent", Color(1.00, 1.00, 1.00, 0.00)); _named_colors.insert("violet", Color(0.93, 0.51, 0.93)); _named_colors.insert("wheat", Color(0.96, 0.87, 0.70)); _named_colors.insert("white", Color(1.00, 1.00, 1.00)); diff --git a/core/command_queue_mt.h b/core/command_queue_mt.h index 59eabd8786..98f5bc56d7 100644 --- a/core/command_queue_mt.h +++ b/core/command_queue_mt.h @@ -37,10 +37,6 @@ #include "core/simple_type.h" #include "core/typedefs.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - #define COMMA(N) _COMMA_##N #define _COMMA_0 #define _COMMA_1 , @@ -346,7 +342,7 @@ class CommandQueueMT { } return NULL; } - } else if (write_ptr >= dealloc_ptr) { + } else { // ahead of dealloc_ptr, check that there is room if ((COMMAND_MEM_SIZE - write_ptr) < alloc_size + sizeof(uint32_t)) { @@ -406,8 +402,10 @@ class CommandQueueMT { tryagain: // tried to read an empty queue - if (read_ptr == write_ptr) + if (read_ptr == write_ptr) { + if (p_lock) unlock(); return false; + } uint32_t size_ptr = read_ptr; uint32_t size = *(uint32_t *)&command_mem[read_ptr] >> 1; diff --git a/core/compressed_translation.cpp b/core/compressed_translation.cpp index f102721470..d927b74897 100644 --- a/core/compressed_translation.cpp +++ b/core/compressed_translation.cpp @@ -136,6 +136,8 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { bucket_table_size += 2 + b.size() * 4; } + ERR_FAIL_COND(bucket_table_size == 0); + hash_table.resize(size); bucket_table.resize(bucket_table_size); diff --git a/core/core_string_names.cpp b/core/core_string_names.cpp index 1b59508abf..eeaae96754 100644 --- a/core/core_string_names.cpp +++ b/core/core_string_names.cpp @@ -44,6 +44,7 @@ CoreStringNames::CoreStringNames() : _iter_next(StaticCString::create("_iter_next")), _iter_get(StaticCString::create("_iter_get")), get_rid(StaticCString::create("get_rid")), + _to_string(StaticCString::create("_to_string")), #ifdef TOOLS_ENABLED _sections_unfolded(StaticCString::create("_sections_unfolded")), #endif diff --git a/core/core_string_names.h b/core/core_string_names.h index 6fea40e1b2..85f8bb7f62 100644 --- a/core/core_string_names.h +++ b/core/core_string_names.h @@ -62,6 +62,7 @@ public: StringName _iter_next; StringName _iter_get; StringName get_rid; + StringName _to_string; #ifdef TOOLS_ENABLED StringName _sections_unfolded; #endif diff --git a/core/cowdata.h b/core/cowdata.h index 3e40ad0f4b..c92e20920c 100644 --- a/core/cowdata.h +++ b/core/cowdata.h @@ -33,6 +33,7 @@ #include <string.h> +#include "core/error_macros.h" #include "core/os/memory.h" #include "core/safe_refcount.h" diff --git a/core/crypto/SCsub b/core/crypto/SCsub new file mode 100644 index 0000000000..0a3f05d87a --- /dev/null +++ b/core/crypto/SCsub @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +Import('env') + +env_crypto = env.Clone() + +is_builtin = env["builtin_mbedtls"] +has_module = env["module_mbedtls_enabled"] + +if is_builtin or not has_module: + # Use our headers for builtin or if the module is not going to be compiled. + # We decided not to depend on system mbedtls just for these few files that can + # be easily extracted. + env_crypto.Prepend(CPPPATH=["#thirdparty/mbedtls/include"]) + +# MbedTLS core functions (for CryptoCore). +# If the mbedtls module is compiled we don't need to add the .c files with our +# custom config since they will be built by the module itself. +# Only if the module is not enabled, we must compile here the required sources +# to make a "light" build with only the necessary mbedtls files. +if not has_module: + env_thirdparty = env_crypto.Clone() + env_thirdparty.disable_warnings() + # Custom config file + env_thirdparty.Append(CPPDEFINES=[('MBEDTLS_CONFIG_FILE', '\\"thirdparty/mbedtls/include/godot_core_mbedtls_config.h\\"')]) + thirdparty_mbedtls_dir = "#thirdparty/mbedtls/library/" + thirdparty_mbedtls_sources = [ + "aes.c", + "base64.c", + "md5.c", + "sha1.c", + "sha256.c", + "godot_core_mbedtls_platform.c" + ] + thirdparty_mbedtls_sources = [thirdparty_mbedtls_dir + file for file in thirdparty_mbedtls_sources] + env_thirdparty.add_source_files(env.core_sources, thirdparty_mbedtls_sources) + +env_crypto.add_source_files(env.core_sources, "*.cpp") diff --git a/core/crypto/crypto.cpp b/core/crypto/crypto.cpp new file mode 100644 index 0000000000..83a25da901 --- /dev/null +++ b/core/crypto/crypto.cpp @@ -0,0 +1,170 @@ +/*************************************************************************/ +/* crypto.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "crypto.h" + +#include "core/engine.h" +#include "core/io/certs_compressed.gen.h" +#include "core/io/compression.h" + +/// Resources + +CryptoKey *(*CryptoKey::_create)() = NULL; +CryptoKey *CryptoKey::create() { + if (_create) + return _create(); + return NULL; +} + +void CryptoKey::_bind_methods() { + ClassDB::bind_method(D_METHOD("save", "path"), &CryptoKey::save); + ClassDB::bind_method(D_METHOD("load", "path"), &CryptoKey::load); +} + +X509Certificate *(*X509Certificate::_create)() = NULL; +X509Certificate *X509Certificate::create() { + if (_create) + return _create(); + return NULL; +} + +void X509Certificate::_bind_methods() { + ClassDB::bind_method(D_METHOD("save", "path"), &X509Certificate::save); + ClassDB::bind_method(D_METHOD("load", "path"), &X509Certificate::load); +} + +/// Crypto + +void (*Crypto::_load_default_certificates)(String p_path) = NULL; +Crypto *(*Crypto::_create)() = NULL; +Crypto *Crypto::create() { + if (_create) + return _create(); + return memnew(Crypto); +} + +void Crypto::load_default_certificates(String p_path) { + + if (_load_default_certificates) + _load_default_certificates(p_path); +} + +void Crypto::_bind_methods() { + ClassDB::bind_method(D_METHOD("generate_random_bytes", "size"), &Crypto::generate_random_bytes); + ClassDB::bind_method(D_METHOD("generate_rsa", "size"), &Crypto::generate_rsa); + ClassDB::bind_method(D_METHOD("generate_self_signed_certificate", "key", "issuer_name", "not_before", "not_after"), &Crypto::generate_self_signed_certificate, DEFVAL("CN=myserver,O=myorganisation,C=IT"), DEFVAL("20140101000000"), DEFVAL("20340101000000")); +} + +PoolByteArray Crypto::generate_random_bytes(int p_bytes) { + ERR_FAIL_V_MSG(PoolByteArray(), "generate_random_bytes is not available when mbedtls module is disabled."); +} + +Ref<CryptoKey> Crypto::generate_rsa(int p_bytes) { + ERR_FAIL_V_MSG(NULL, "generate_rsa is not available when mbedtls module is disabled."); +} + +Ref<X509Certificate> Crypto::generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after) { + ERR_FAIL_V_MSG(NULL, "generate_self_signed_certificate is not available when mbedtls module is disabled."); +} + +Crypto::Crypto() { +} + +/// Resource loader/saver + +RES ResourceFormatLoaderCrypto::load(const String &p_path, const String &p_original_path, Error *r_error) { + + String el = p_path.get_extension().to_lower(); + if (el == "crt") { + X509Certificate *cert = X509Certificate::create(); + if (cert) + cert->load(p_path); + return cert; + } else if (el == "key") { + CryptoKey *key = CryptoKey::create(); + if (key) + key->load(p_path); + return key; + } + return NULL; +} + +void ResourceFormatLoaderCrypto::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("crt"); + p_extensions->push_back("key"); +} + +bool ResourceFormatLoaderCrypto::handles_type(const String &p_type) const { + + return p_type == "X509Certificate" || p_type == "CryptoKey"; +} + +String ResourceFormatLoaderCrypto::get_resource_type(const String &p_path) const { + + String el = p_path.get_extension().to_lower(); + if (el == "crt") + return "X509Certificate"; + else if (el == "key") + return "CryptoKey"; + return ""; +} + +Error ResourceFormatSaverCrypto::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { + + Error err; + Ref<X509Certificate> cert = p_resource; + Ref<CryptoKey> key = p_resource; + if (cert.is_valid()) { + err = cert->save(p_path); + } else if (key.is_valid()) { + err = key->save(p_path); + } else { + ERR_FAIL_V(ERR_INVALID_PARAMETER); + } + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save Crypto resource to file '" + p_path + "'."); + return OK; +} + +void ResourceFormatSaverCrypto::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { + + const X509Certificate *cert = Object::cast_to<X509Certificate>(*p_resource); + const CryptoKey *key = Object::cast_to<CryptoKey>(*p_resource); + if (cert) { + p_extensions->push_back("crt"); + } + if (key) { + p_extensions->push_back("key"); + } +} +bool ResourceFormatSaverCrypto::recognize(const RES &p_resource) const { + + return Object::cast_to<X509Certificate>(*p_resource) || Object::cast_to<CryptoKey>(*p_resource); +} diff --git a/core/crypto/crypto.h b/core/crypto/crypto.h new file mode 100644 index 0000000000..2de81f5b57 --- /dev/null +++ b/core/crypto/crypto.h @@ -0,0 +1,105 @@ +/*************************************************************************/ +/* crypto.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 CRYPTO_H +#define CRYPTO_H + +#include "core/reference.h" +#include "core/resource.h" + +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" + +class CryptoKey : public Resource { + GDCLASS(CryptoKey, Resource); + +protected: + static void _bind_methods(); + static CryptoKey *(*_create)(); + +public: + static CryptoKey *create(); + virtual Error load(String p_path) = 0; + virtual Error save(String p_path) = 0; +}; + +class X509Certificate : public Resource { + GDCLASS(X509Certificate, Resource); + +protected: + static void _bind_methods(); + static X509Certificate *(*_create)(); + +public: + static X509Certificate *create(); + virtual Error load(String p_path) = 0; + virtual Error load_from_memory(const uint8_t *p_buffer, int p_len) = 0; + virtual Error save(String p_path) = 0; +}; + +class Crypto : public Reference { + GDCLASS(Crypto, Reference); + +protected: + static void _bind_methods(); + static Crypto *(*_create)(); + static void (*_load_default_certificates)(String p_path); + +public: + static Crypto *create(); + static void load_default_certificates(String p_path); + + virtual PoolByteArray generate_random_bytes(int p_bytes); + virtual Ref<CryptoKey> generate_rsa(int p_bytes); + virtual Ref<X509Certificate> generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after); + + Crypto(); +}; + +class ResourceFormatLoaderCrypto : public ResourceFormatLoader { + GDCLASS(ResourceFormatLoaderCrypto, ResourceFormatLoader); + +public: + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + +class ResourceFormatSaverCrypto : public ResourceFormatSaver { + GDCLASS(ResourceFormatSaverCrypto, ResourceFormatSaver); + +public: + virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); + virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; + virtual bool recognize(const RES &p_resource) const; +}; + +#endif // CRYPTO_H diff --git a/core/crypto/crypto_core.cpp b/core/crypto/crypto_core.cpp new file mode 100644 index 0000000000..51c2e3c9e5 --- /dev/null +++ b/core/crypto/crypto_core.cpp @@ -0,0 +1,183 @@ +/*************************************************************************/ +/* crypto_core.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "crypto_core.h" + +#include <mbedtls/aes.h> +#include <mbedtls/base64.h> +#include <mbedtls/md5.h> +#include <mbedtls/sha1.h> +#include <mbedtls/sha256.h> + +// MD5 +CryptoCore::MD5Context::MD5Context() { + ctx = memalloc(sizeof(mbedtls_md5_context)); + mbedtls_md5_init((mbedtls_md5_context *)ctx); +} + +CryptoCore::MD5Context::~MD5Context() { + mbedtls_md5_free((mbedtls_md5_context *)ctx); + memfree((mbedtls_md5_context *)ctx); +} + +Error CryptoCore::MD5Context::start() { + int ret = mbedtls_md5_starts_ret((mbedtls_md5_context *)ctx); + return ret ? FAILED : OK; +} + +Error CryptoCore::MD5Context::update(const uint8_t *p_src, size_t p_len) { + int ret = mbedtls_md5_update_ret((mbedtls_md5_context *)ctx, p_src, p_len); + return ret ? FAILED : OK; +} + +Error CryptoCore::MD5Context::finish(unsigned char r_hash[16]) { + int ret = mbedtls_md5_finish_ret((mbedtls_md5_context *)ctx, r_hash); + return ret ? FAILED : OK; +} + +// SHA1 +CryptoCore::SHA1Context::SHA1Context() { + ctx = memalloc(sizeof(mbedtls_sha1_context)); + mbedtls_sha1_init((mbedtls_sha1_context *)ctx); +} + +CryptoCore::SHA1Context::~SHA1Context() { + mbedtls_sha1_free((mbedtls_sha1_context *)ctx); + memfree((mbedtls_sha1_context *)ctx); +} + +Error CryptoCore::SHA1Context::start() { + int ret = mbedtls_sha1_starts_ret((mbedtls_sha1_context *)ctx); + return ret ? FAILED : OK; +} + +Error CryptoCore::SHA1Context::update(const uint8_t *p_src, size_t p_len) { + int ret = mbedtls_sha1_update_ret((mbedtls_sha1_context *)ctx, p_src, p_len); + return ret ? FAILED : OK; +} + +Error CryptoCore::SHA1Context::finish(unsigned char r_hash[20]) { + int ret = mbedtls_sha1_finish_ret((mbedtls_sha1_context *)ctx, r_hash); + return ret ? FAILED : OK; +} + +// SHA256 +CryptoCore::SHA256Context::SHA256Context() { + ctx = memalloc(sizeof(mbedtls_sha256_context)); + mbedtls_sha256_init((mbedtls_sha256_context *)ctx); +} + +CryptoCore::SHA256Context::~SHA256Context() { + mbedtls_sha256_free((mbedtls_sha256_context *)ctx); + memfree((mbedtls_sha256_context *)ctx); +} + +Error CryptoCore::SHA256Context::start() { + int ret = mbedtls_sha256_starts_ret((mbedtls_sha256_context *)ctx, 0); + return ret ? FAILED : OK; +} + +Error CryptoCore::SHA256Context::update(const uint8_t *p_src, size_t p_len) { + int ret = mbedtls_sha256_update_ret((mbedtls_sha256_context *)ctx, p_src, p_len); + return ret ? FAILED : OK; +} + +Error CryptoCore::SHA256Context::finish(unsigned char r_hash[32]) { + int ret = mbedtls_sha256_finish_ret((mbedtls_sha256_context *)ctx, r_hash); + return ret ? FAILED : OK; +} + +// AES256 +CryptoCore::AESContext::AESContext() { + ctx = memalloc(sizeof(mbedtls_aes_context)); + mbedtls_aes_init((mbedtls_aes_context *)ctx); +} + +CryptoCore::AESContext::~AESContext() { + mbedtls_aes_free((mbedtls_aes_context *)ctx); + memfree((mbedtls_aes_context *)ctx); +} + +Error CryptoCore::AESContext::set_encode_key(const uint8_t *p_key, size_t p_bits) { + int ret = mbedtls_aes_setkey_enc((mbedtls_aes_context *)ctx, p_key, p_bits); + return ret ? FAILED : OK; +} + +Error CryptoCore::AESContext::set_decode_key(const uint8_t *p_key, size_t p_bits) { + int ret = mbedtls_aes_setkey_dec((mbedtls_aes_context *)ctx, p_key, p_bits); + return ret ? FAILED : OK; +} + +Error CryptoCore::AESContext::encrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]) { + int ret = mbedtls_aes_crypt_ecb((mbedtls_aes_context *)ctx, MBEDTLS_AES_ENCRYPT, p_src, r_dst); + return ret ? FAILED : OK; +} + +Error CryptoCore::AESContext::decrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]) { + int ret = mbedtls_aes_crypt_ecb((mbedtls_aes_context *)ctx, MBEDTLS_AES_DECRYPT, p_src, r_dst); + return ret ? FAILED : OK; +} + +// CryptoCore +String CryptoCore::b64_encode_str(const uint8_t *p_src, int p_src_len) { + int b64len = p_src_len / 3 * 4 + 4 + 1; + PoolVector<uint8_t> b64buff; + b64buff.resize(b64len); + PoolVector<uint8_t>::Write w64 = b64buff.write(); + size_t strlen = 0; + int ret = b64_encode(&w64[0], b64len, &strlen, p_src, p_src_len); + w64[strlen] = 0; + return ret ? String() : (const char *)&w64[0]; +} + +Error CryptoCore::b64_encode(uint8_t *r_dst, int p_dst_len, size_t *r_len, const uint8_t *p_src, int p_src_len) { + int ret = mbedtls_base64_encode(r_dst, p_dst_len, r_len, p_src, p_src_len); + return ret ? FAILED : OK; +} + +Error CryptoCore::b64_decode(uint8_t *r_dst, int p_dst_len, size_t *r_len, const uint8_t *p_src, int p_src_len) { + int ret = mbedtls_base64_decode(r_dst, p_dst_len, r_len, p_src, p_src_len); + return ret ? FAILED : OK; +} + +Error CryptoCore::md5(const uint8_t *p_src, int p_src_len, unsigned char r_hash[16]) { + int ret = mbedtls_md5_ret(p_src, p_src_len, r_hash); + return ret ? FAILED : OK; +} + +Error CryptoCore::sha1(const uint8_t *p_src, int p_src_len, unsigned char r_hash[20]) { + int ret = mbedtls_sha1_ret(p_src, p_src_len, r_hash); + return ret ? FAILED : OK; +} + +Error CryptoCore::sha256(const uint8_t *p_src, int p_src_len, unsigned char r_hash[32]) { + int ret = mbedtls_sha256_ret(p_src, p_src_len, r_hash, 0); + return ret ? FAILED : OK; +} diff --git a/core/crypto/crypto_core.h b/core/crypto/crypto_core.h new file mode 100644 index 0000000000..c859d612d4 --- /dev/null +++ b/core/crypto/crypto_core.h @@ -0,0 +1,104 @@ +/*************************************************************************/ +/* crypto_core.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 CRYPTO_CORE_H +#define CRYPTO_CORE_H + +#include "core/reference.h" + +class CryptoCore { + +public: + class MD5Context { + + private: + void *ctx; // To include, or not to include... + + public: + MD5Context(); + ~MD5Context(); + + Error start(); + Error update(const uint8_t *p_src, size_t p_len); + Error finish(unsigned char r_hash[16]); + }; + + class SHA1Context { + + private: + void *ctx; // To include, or not to include... + + public: + SHA1Context(); + ~SHA1Context(); + + Error start(); + Error update(const uint8_t *p_src, size_t p_len); + Error finish(unsigned char r_hash[20]); + }; + + class SHA256Context { + + private: + void *ctx; // To include, or not to include... + + public: + SHA256Context(); + ~SHA256Context(); + + Error start(); + Error update(const uint8_t *p_src, size_t p_len); + Error finish(unsigned char r_hash[32]); + }; + + class AESContext { + + private: + void *ctx; // To include, or not to include... + + public: + AESContext(); + ~AESContext(); + + Error set_encode_key(const uint8_t *p_key, size_t p_bits); + Error set_decode_key(const uint8_t *p_key, size_t p_bits); + Error encrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]); + Error decrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]); + }; + + static String b64_encode_str(const uint8_t *p_src, int p_src_len); + static Error b64_encode(uint8_t *r_dst, int p_dst_len, size_t *r_len, const uint8_t *p_src, int p_src_len); + static Error b64_decode(uint8_t *r_dst, int p_dst_len, size_t *r_len, const uint8_t *p_src, int p_src_len); + + static Error md5(const uint8_t *p_src, int p_src_len, unsigned char r_hash[16]); + static Error sha1(const uint8_t *p_src, int p_src_len, unsigned char r_hash[20]); + static Error sha256(const uint8_t *p_src, int p_src_len, unsigned char r_hash[32]); +}; +#endif // CRYPTO_CORE_H diff --git a/core/crypto/hashing_context.cpp b/core/crypto/hashing_context.cpp new file mode 100644 index 0000000000..b5aa0ddc18 --- /dev/null +++ b/core/crypto/hashing_context.cpp @@ -0,0 +1,138 @@ +/*************************************************************************/ +/* hashing_context.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "hashing_context.h" + +#include "core/crypto/crypto_core.h" + +Error HashingContext::start(HashType p_type) { + ERR_FAIL_COND_V(ctx != NULL, ERR_ALREADY_IN_USE); + _create_ctx(p_type); + ERR_FAIL_COND_V(ctx == NULL, ERR_UNAVAILABLE); + switch (type) { + case HASH_MD5: + return ((CryptoCore::MD5Context *)ctx)->start(); + case HASH_SHA1: + return ((CryptoCore::SHA1Context *)ctx)->start(); + case HASH_SHA256: + return ((CryptoCore::SHA256Context *)ctx)->start(); + } + return ERR_UNAVAILABLE; +} + +Error HashingContext::update(PoolByteArray p_chunk) { + ERR_FAIL_COND_V(ctx == NULL, ERR_UNCONFIGURED); + size_t len = p_chunk.size(); + ERR_FAIL_COND_V(len == 0, FAILED); + PoolByteArray::Read r = p_chunk.read(); + switch (type) { + case HASH_MD5: + return ((CryptoCore::MD5Context *)ctx)->update(&r[0], len); + case HASH_SHA1: + return ((CryptoCore::SHA1Context *)ctx)->update(&r[0], len); + case HASH_SHA256: + return ((CryptoCore::SHA256Context *)ctx)->update(&r[0], len); + } + return ERR_UNAVAILABLE; +} + +PoolByteArray HashingContext::finish() { + ERR_FAIL_COND_V(ctx == NULL, PoolByteArray()); + PoolByteArray out; + Error err = FAILED; + switch (type) { + case HASH_MD5: + out.resize(16); + err = ((CryptoCore::MD5Context *)ctx)->finish(out.write().ptr()); + break; + case HASH_SHA1: + out.resize(20); + err = ((CryptoCore::SHA1Context *)ctx)->finish(out.write().ptr()); + break; + case HASH_SHA256: + out.resize(32); + err = ((CryptoCore::SHA256Context *)ctx)->finish(out.write().ptr()); + break; + } + _delete_ctx(); + ERR_FAIL_COND_V(err != OK, PoolByteArray()); + return out; +} + +void HashingContext::_create_ctx(HashType p_type) { + type = p_type; + switch (type) { + case HASH_MD5: + ctx = memnew(CryptoCore::MD5Context); + break; + case HASH_SHA1: + ctx = memnew(CryptoCore::SHA1Context); + break; + case HASH_SHA256: + ctx = memnew(CryptoCore::SHA256Context); + break; + default: + ctx = NULL; + } +} + +void HashingContext::_delete_ctx() { + + switch (type) { + case HASH_MD5: + memdelete((CryptoCore::MD5Context *)ctx); + break; + case HASH_SHA1: + memdelete((CryptoCore::SHA1Context *)ctx); + break; + case HASH_SHA256: + memdelete((CryptoCore::SHA256Context *)ctx); + break; + } + ctx = NULL; +} + +void HashingContext::_bind_methods() { + ClassDB::bind_method(D_METHOD("start", "type"), &HashingContext::start); + ClassDB::bind_method(D_METHOD("update", "chunk"), &HashingContext::update); + ClassDB::bind_method(D_METHOD("finish"), &HashingContext::finish); + BIND_ENUM_CONSTANT(HASH_MD5); + BIND_ENUM_CONSTANT(HASH_SHA1); + BIND_ENUM_CONSTANT(HASH_SHA256); +} + +HashingContext::HashingContext() { + ctx = NULL; +} + +HashingContext::~HashingContext() { + if (ctx != NULL) + _delete_ctx(); +} diff --git a/core/crypto/hashing_context.h b/core/crypto/hashing_context.h new file mode 100644 index 0000000000..aa69636f2c --- /dev/null +++ b/core/crypto/hashing_context.h @@ -0,0 +1,66 @@ +/*************************************************************************/ +/* hashing_context.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 HASHING_CONTEXT_H +#define HASHING_CONTEXT_H + +#include "core/reference.h" + +class HashingContext : public Reference { + GDCLASS(HashingContext, Reference); + +public: + enum HashType { + HASH_MD5, + HASH_SHA1, + HASH_SHA256 + }; + +private: + void *ctx; + HashType type; + +protected: + static void _bind_methods(); + void _create_ctx(HashType p_type); + void _delete_ctx(); + +public: + Error start(HashType p_type); + Error update(PoolByteArray p_chunk); + PoolByteArray finish(); + + HashingContext(); + ~HashingContext(); +}; + +VARIANT_ENUM_CAST(HashingContext::HashType); + +#endif // HASHING_CONTEXT_H diff --git a/core/dictionary.cpp b/core/dictionary.cpp index bea0997cc9..0d9945991e 100644 --- a/core/dictionary.cpp +++ b/core/dictionary.cpp @@ -192,13 +192,9 @@ uint32_t Dictionary::hash() const { uint32_t h = hash_djb2_one_32(Variant::DICTIONARY); - List<Variant> keys; - get_key_list(&keys); - - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - - h = hash_djb2_one_32(E->get().hash(), h); - h = hash_djb2_one_32(operator[](E->get()).hash(), h); + for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { + h = hash_djb2_one_32(E.key().hash(), h); + h = hash_djb2_one_32(E.value().hash(), h); } return h; @@ -207,10 +203,11 @@ uint32_t Dictionary::hash() const { Array Dictionary::keys() const { Array varr; - varr.resize(size()); if (_p->variant_map.empty()) return varr; + varr.resize(size()); + int i = 0; for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { varr[i] = E.key(); @@ -223,10 +220,11 @@ Array Dictionary::keys() const { Array Dictionary::values() const { Array varr; - varr.resize(size()); if (_p->variant_map.empty()) return varr; + varr.resize(size()); + int i = 0; for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { varr[i] = E.get(); @@ -255,11 +253,8 @@ Dictionary Dictionary::duplicate(bool p_deep) const { Dictionary n; - List<Variant> keys; - get_key_list(&keys); - - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - n[E->get()] = p_deep ? operator[](E->get()).duplicate(p_deep) : operator[](E->get()); + for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { + n[E.key()] = p_deep ? E.value().duplicate(true) : E.value(); } return n; @@ -270,6 +265,10 @@ void Dictionary::operator=(const Dictionary &p_dictionary) { _ref(p_dictionary); } +const void *Dictionary::id() const { + return _p->variant_map.id(); +} + Dictionary::Dictionary(const Dictionary &p_from) { _p = NULL; _ref(p_from); diff --git a/core/dictionary.h b/core/dictionary.h index eab7354cef..b68d3f5737 100644 --- a/core/dictionary.h +++ b/core/dictionary.h @@ -82,6 +82,8 @@ public: Dictionary duplicate(bool p_deep = false) const; + const void *id() const; + Dictionary(const Dictionary &p_from); Dictionary(); ~Dictionary(); diff --git a/core/engine.cpp b/core/engine.cpp index 9607dedb3c..b9dc057257 100644 --- a/core/engine.cpp +++ b/core/engine.cpp @@ -38,6 +38,7 @@ void Engine::set_iterations_per_second(int p_ips) { + ERR_FAIL_COND_MSG(p_ips <= 0, "Engine iterations per second must be greater than 0."); ips = p_ips; } int Engine::get_iterations_per_second() const { @@ -196,8 +197,7 @@ void Engine::add_singleton(const Singleton &p_singleton) { Object *Engine::get_singleton_object(const String &p_name) const { const Map<StringName, Object *>::Element *E = singleton_ptrs.find(p_name); - ERR_EXPLAIN("Failed to retrieve non-existent singleton '" + p_name + "'"); - ERR_FAIL_COND_V(!E, NULL); + ERR_FAIL_COND_V_MSG(!E, NULL, "Failed to retrieve non-existent singleton '" + p_name + "'."); return E->get(); }; @@ -224,6 +224,7 @@ Engine::Engine() { frames_drawn = 0; ips = 60; physics_jitter_fix = 0.5; + _physics_interpolation_fraction = 0.0f; _frame_delay = 0; _fps = 1; _target_fps = 0; diff --git a/core/engine.h b/core/engine.h index 15665fee29..192e8e67a0 100644 --- a/core/engine.h +++ b/core/engine.h @@ -63,6 +63,7 @@ private: float _time_scale; bool _pixel_snap; uint64_t _physics_frames; + float _physics_interpolation_fraction; uint64_t _idle_frames; bool _in_physics; @@ -95,6 +96,7 @@ public: bool is_in_physics_frame() const { return _in_physics; } uint64_t get_idle_frame_ticks() const { return _frame_ticks; } float get_idle_frame_step() const { return _frame_step; } + float get_physics_interpolation_fraction() const { return _physics_interpolation_fraction; } void set_time_scale(float p_scale); float get_time_scale() const; diff --git a/core/error_list.h b/core/error_list.h index 304861da4e..dc5a5e68dd 100644 --- a/core/error_list.h +++ b/core/error_list.h @@ -39,7 +39,7 @@ */ enum Error { - OK, + OK, // (0) FAILED, ///< Generic fail error ERR_UNAVAILABLE, ///< What is requested is unsupported/unavailable ERR_UNCONFIGURED, ///< The object being used hasn't been properly set up yet @@ -69,12 +69,12 @@ enum Error { ERR_CONNECTION_ERROR, ERR_CANT_ACQUIRE_RESOURCE, ERR_CANT_FORK, - ERR_INVALID_DATA, ///< Data passed is invalid (30) + ERR_INVALID_DATA, ///< Data passed is invalid (30) ERR_INVALID_PARAMETER, ///< Parameter passed is invalid ERR_ALREADY_EXISTS, ///< When adding, item already exists - ERR_DOES_NOT_EXIST, ///< When retrieving/erasing, it item does not exist + ERR_DOES_NOT_EXIST, ///< When retrieving/erasing, if item does not exist ERR_DATABASE_CANT_READ, ///< database is full - ERR_DATABASE_CANT_WRITE, ///< database is full (35) + ERR_DATABASE_CANT_WRITE, ///< database is full (35) ERR_COMPILATION_FAILED, ERR_METHOD_NOT_FOUND, ERR_LINK_FAILED, diff --git a/core/error_macros.cpp b/core/error_macros.cpp index 0e8e4a9bb4..55a215cfb6 100644 --- a/core/error_macros.cpp +++ b/core/error_macros.cpp @@ -31,22 +31,11 @@ #include "error_macros.h" #include "core/io/logger.h" +#include "core/ustring.h" #include "os/os.h" -bool _err_error_exists = false; - static ErrorHandlerList *error_handler_list = NULL; -void _err_set_last_error(const char *p_err) { - - OS::get_singleton()->set_last_error(p_err); -} - -void _err_clear_last_error() { - - OS::get_singleton()->clear_last_error(); -} - void add_error_handler(ErrorHandlerList *p_handler) { _global_lock(); @@ -80,28 +69,47 @@ void remove_error_handler(ErrorHandlerList *p_handler) { } void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type) { + _err_print_error(p_function, p_file, p_line, p_error, "", p_type); +} + +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type) { + _err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), "", p_type); +} - OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, _err_error_exists ? OS::get_singleton()->get_last_error() : "", (Logger::ErrorType)p_type); +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) { + + OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, p_message, (Logger::ErrorType)p_type); _global_lock(); ErrorHandlerList *l = error_handler_list; while (l) { - l->errfunc(l->userdata, p_function, p_file, p_line, p_error, _err_error_exists ? OS::get_singleton()->get_last_error() : "", p_type); + l->errfunc(l->userdata, p_function, p_file, p_line, p_error, p_message, p_type); l = l->next; } _global_unlock(); +} - if (_err_error_exists) { - OS::get_singleton()->clear_last_error(); - _err_error_exists = false; - } +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, ErrorHandlerType p_type) { + _err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message, p_type); } -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, bool fatal) { +void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, ErrorHandlerType p_type) { + _err_print_error(p_function, p_file, p_line, p_error, p_message.utf8().get_data(), p_type); +} + +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, ErrorHandlerType p_type) { + _err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message.utf8().get_data(), p_type); +} + +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) { String fstr(fatal ? "FATAL: " : ""); String err(fstr + "Index " + p_index_str + "=" + itos(p_index) + " out of size (" + p_size_str + "=" + itos(p_size) + ")"); - _err_print_error(p_function, p_file, p_line, err.utf8().get_data()); + _err_print_error(p_function, p_file, p_line, err.utf8().get_data(), p_message); +} + +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) { + _err_print_index_error(p_function, p_file, p_line, p_index, p_size, p_index_str, p_size_str, p_message.utf8().get_data(), fatal); } diff --git a/core/error_macros.h b/core/error_macros.h index ca5ccd24cf..00e830f73a 100644 --- a/core/error_macros.h +++ b/core/error_macros.h @@ -41,7 +41,7 @@ */ /** - * Pointer to the error macro priting function. Reassign to any function to have errors printed + * Pointer to the error macro printing function. Reassign to any function to have errors printed */ /** Function used by the error macros */ @@ -55,9 +55,8 @@ enum ErrorHandlerType { ERR_HANDLER_SHADER, }; +class String; typedef void (*ErrorHandlerFunc)(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type); -void _err_set_last_error(const char *p_err); -void _err_clear_last_error(); struct ErrorHandlerList { @@ -77,7 +76,13 @@ void add_error_handler(ErrorHandlerList *p_handler); void remove_error_handler(ErrorHandlerList *p_handler); 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_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, bool fatal = false); +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); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, 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 String &p_message, 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, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +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 @@ -86,30 +91,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li #define _FNL __FILE__ ":" -/** An index has failed if m_index<0 or m_index >=m_size, the function exists */ - -extern bool _err_error_exists; - -#ifdef DEBUG_ENABLED -/** Print a warning string. - */ -#define ERR_EXPLAINC(m_reason) \ - { \ - _err_set_last_error(m_reason); \ - _err_error_exists = true; \ - } -#define ERR_EXPLAIN(m_string) \ - { \ - _err_set_last_error(String(m_string).utf8().get_data()); \ - _err_error_exists = true; \ - } - -#else - -#define ERR_EXPLAIN(m_text) -#define ERR_EXPLAINC(m_text) - -#endif +/** 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 @@ -129,6 +111,13 @@ extern bool _err_error_exists; #define GENERATE_TRAP __builtin_trap(); #endif +// Used to strip debug messages in release mode +#ifdef DEBUG_ENABLED +#define DEBUG_STR(m_msg) m_msg +#else +#define DEBUG_STR(m_msg) "" +#endif + // (*): See https://stackoverflow.com/questions/257418/do-while-0-what-is-it-good-for #define ERR_FAIL_INDEX(m_index, m_size) \ @@ -136,11 +125,18 @@ extern bool _err_error_exists; 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 \ - _err_error_exists = false; \ + } \ } while (0); // (*) -/** An index has failed if m_index<0 or m_index >=m_size, the function exists. +#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); // (*) + +/** An index has failed if m_index<0 or m_index >=m_size, the function exits. * This function returns an error value, if returning Error, please select the most * appropriate error condition from error_macros.h */ @@ -150,11 +146,18 @@ extern bool _err_error_exists; 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 \ - _err_error_exists = false; \ + } \ + } while (0); // (*) + +#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); // (*) -/** An index has failed if m_index >=m_size, the function exists. +/** An index has failed if m_index >=m_size, the function exits. * This function returns an error value, if returning Error, please select the most * appropriate error condition from error_macros.h */ @@ -164,19 +167,34 @@ extern bool _err_error_exists; 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 \ - _err_error_exists = false; \ + } \ + } while (0); // (*) + +#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); // (*) /** Use this one if there is no sensible fallback, that is, the error is unrecoverable. * We'll return a null reference and try to keep running. */ -#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 \ - } \ +#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); // (*) + +#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); // (*) /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). @@ -188,8 +206,15 @@ extern bool _err_error_exists; if (unlikely(!m_param)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter ' " _STR(m_param) " ' is null."); \ return; \ - } else \ - _err_error_exists = false; \ + } \ + } + +#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; \ + } \ } #define ERR_FAIL_NULL_V(m_param, m_retval) \ @@ -197,8 +222,15 @@ extern bool _err_error_exists; if (unlikely(!m_param)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter ' " _STR(m_param) " ' is null."); \ return m_retval; \ - } else \ - _err_error_exists = false; \ + } \ + } + +#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; \ + } \ } /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). @@ -210,8 +242,15 @@ extern bool _err_error_exists; if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true."); \ return; \ - } else \ - _err_error_exists = false; \ + } \ + } + +#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; \ + } \ } /** Use this one if there is no sensible fallback, that is, the error is unrecoverable. @@ -225,6 +264,14 @@ extern bool _err_error_exists; } \ } +#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 \ + } \ + } + /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). * the function will exit. * This function returns an error value, if returning Error, please select the most @@ -236,8 +283,15 @@ extern bool _err_error_exists; 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 \ - _err_error_exists = false; \ + } \ + } + +#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; \ + } \ } /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). @@ -249,8 +303,15 @@ extern bool _err_error_exists; if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Continuing..:"); \ continue; \ - } else \ - _err_error_exists = false; \ + } \ + } + +#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; \ + } \ } /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). @@ -262,8 +323,15 @@ extern bool _err_error_exists; if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Breaking..:"); \ break; \ - } else \ - _err_error_exists = false; \ + } \ + } + +#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; \ + } \ } /** Print an error string and return @@ -272,20 +340,30 @@ extern bool _err_error_exists; #define ERR_FAIL() \ { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed."); \ - _err_error_exists = false; \ return; \ } +#define ERR_FAIL_MSG(m_msg) \ + { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed.", DEBUG_STR(m_msg)); \ + return; \ + } + /** Print an error string and return with value */ #define ERR_FAIL_V(m_value) \ { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed, returning: " __STR(m_value)); \ - _err_error_exists = false; \ return m_value; \ } +#define ERR_FAIL_V_MSG(m_value, m_msg) \ + { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/Function Failed, returning: " __STR(m_value), DEBUG_STR(m_msg)); \ + return m_value; \ + } + /** Use this one if there is no sensible fallback, that is, the error is unrecoverable. */ @@ -295,19 +373,23 @@ extern bool _err_error_exists; GENERATE_TRAP \ } +#define CRASH_NOW_MSG(m_msg) \ + { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/Function Failed.", DEBUG_STR(m_msg)); \ + GENERATE_TRAP \ + } + /** Print an error string. */ #define ERR_PRINT(m_string) \ { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string); \ - _err_error_exists = false; \ } -#define ERR_PRINTS(m_string) \ - { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, String(m_string).utf8().get_data()); \ - _err_error_exists = false; \ +#define ERR_PRINTS(m_string) \ + { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string); \ } #define ERR_PRINT_ONCE(m_string) \ @@ -315,7 +397,6 @@ extern bool _err_error_exists; static bool first_print = true; \ if (first_print) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string); \ - _err_error_exists = false; \ first_print = false; \ } \ } @@ -326,13 +407,11 @@ extern bool _err_error_exists; #define WARN_PRINT(m_string) \ { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string, ERR_HANDLER_WARNING); \ - _err_error_exists = false; \ } -#define WARN_PRINTS(m_string) \ - { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, String(m_string).utf8().get_data(), ERR_HANDLER_WARNING); \ - _err_error_exists = false; \ +#define WARN_PRINTS(m_string) \ + { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string, ERR_HANDLER_WARNING); \ } #define WARN_PRINT_ONCE(m_string) \ @@ -340,7 +419,6 @@ extern bool _err_error_exists; static bool first_print = true; \ if (first_print) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string, ERR_HANDLER_WARNING); \ - _err_error_exists = false; \ first_print = false; \ } \ } @@ -350,9 +428,17 @@ extern bool _err_error_exists; 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); \ - _err_error_exists = false; \ warning_shown = true; \ } \ } +#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; \ + } \ + } + #endif diff --git a/core/func_ref.cpp b/core/func_ref.cpp index 4a965473d9..66ef27f6b9 100644 --- a/core/func_ref.cpp +++ b/core/func_ref.cpp @@ -46,16 +46,39 @@ Variant FuncRef::call_func(const Variant **p_args, int p_argcount, Variant::Call return obj->call(function, p_args, p_argcount, r_error); } +Variant FuncRef::call_funcv(const Array &p_args) { + + ERR_FAIL_COND_V(id == 0, Variant()); + + Object *obj = ObjectDB::get_instance(id); + + ERR_FAIL_COND_V(!obj, Variant()); + + return obj->callv(function, p_args); +} + void FuncRef::set_instance(Object *p_obj) { ERR_FAIL_NULL(p_obj); id = p_obj->get_instance_id(); } + void FuncRef::set_function(const StringName &p_func) { function = p_func; } +bool FuncRef::is_valid() const { + if (id == 0) + return false; + + Object *obj = ObjectDB::get_instance(id); + if (!obj) + return false; + + return obj->has_method(function); +} + void FuncRef::_bind_methods() { { @@ -65,8 +88,11 @@ void FuncRef::_bind_methods() { ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call_func", &FuncRef::call_func, mi, defargs); } + ClassDB::bind_method(D_METHOD("call_funcv", "arg_array"), &FuncRef::call_funcv); + ClassDB::bind_method(D_METHOD("set_instance", "instance"), &FuncRef::set_instance); ClassDB::bind_method(D_METHOD("set_function", "name"), &FuncRef::set_function); + ClassDB::bind_method(D_METHOD("is_valid"), &FuncRef::is_valid); } FuncRef::FuncRef() : diff --git a/core/func_ref.h b/core/func_ref.h index 339279fdba..af0bf63203 100644 --- a/core/func_ref.h +++ b/core/func_ref.h @@ -44,8 +44,10 @@ protected: public: Variant call_func(const Variant **p_args, int p_argcount, Variant::CallError &r_error); + Variant call_funcv(const Array &p_args); void set_instance(Object *p_obj); void set_function(const StringName &p_func); + bool is_valid() const; FuncRef(); }; diff --git a/core/global_constants.cpp b/core/global_constants.cpp index fb90403226..5bfdc8ab8f 100644 --- a/core/global_constants.cpp +++ b/core/global_constants.cpp @@ -425,6 +425,16 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(JOY_DS_X); BIND_GLOBAL_ENUM_CONSTANT(JOY_DS_Y); + BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_GRIP); + BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_PAD); + BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_TRIGGER); + + BIND_GLOBAL_ENUM_CONSTANT(JOY_OCULUS_AX); + BIND_GLOBAL_ENUM_CONSTANT(JOY_OCULUS_BY); + BIND_GLOBAL_ENUM_CONSTANT(JOY_OCULUS_MENU); + + BIND_GLOBAL_ENUM_CONSTANT(JOY_OPENVR_MENU); + BIND_GLOBAL_ENUM_CONSTANT(JOY_SELECT); BIND_GLOBAL_ENUM_CONSTANT(JOY_START); BIND_GLOBAL_ENUM_CONSTANT(JOY_DPAD_UP); @@ -459,6 +469,12 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_L2); BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_R2); + BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_ANALOG_TRIGGER); + BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_ANALOG_GRIP); + + BIND_GLOBAL_ENUM_CONSTANT(JOY_OPENVR_TOUCHPADX); + BIND_GLOBAL_ENUM_CONSTANT(JOY_OPENVR_TOUCHPADY); + // midi BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_OFF); BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_ON); @@ -470,47 +486,55 @@ void register_global_constants() { // error list - BIND_GLOBAL_ENUM_CONSTANT(OK); - BIND_GLOBAL_ENUM_CONSTANT(FAILED); ///< Generic fail error - BIND_GLOBAL_ENUM_CONSTANT(ERR_UNAVAILABLE); ///< What is requested is unsupported/unavailable - BIND_GLOBAL_ENUM_CONSTANT(ERR_UNCONFIGURED); ///< The object being used hasn't been properly set up yet - BIND_GLOBAL_ENUM_CONSTANT(ERR_UNAUTHORIZED); ///< Missing credentials for requested resource - BIND_GLOBAL_ENUM_CONSTANT(ERR_PARAMETER_RANGE_ERROR); ///< Parameter given out of range - BIND_GLOBAL_ENUM_CONSTANT(ERR_OUT_OF_MEMORY); ///< Out of memory + BIND_GLOBAL_ENUM_CONSTANT(OK); // (0) + BIND_GLOBAL_ENUM_CONSTANT(FAILED); + BIND_GLOBAL_ENUM_CONSTANT(ERR_UNAVAILABLE); + BIND_GLOBAL_ENUM_CONSTANT(ERR_UNCONFIGURED); + BIND_GLOBAL_ENUM_CONSTANT(ERR_UNAUTHORIZED); + BIND_GLOBAL_ENUM_CONSTANT(ERR_PARAMETER_RANGE_ERROR); // (5) + BIND_GLOBAL_ENUM_CONSTANT(ERR_OUT_OF_MEMORY); BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_NOT_FOUND); BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_BAD_DRIVE); BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_BAD_PATH); - BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_NO_PERMISSION); + BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_NO_PERMISSION); // (10) BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_ALREADY_IN_USE); BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_CANT_OPEN); BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_CANT_WRITE); BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_CANT_READ); - BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_UNRECOGNIZED); + BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_UNRECOGNIZED); // (15) BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_CORRUPT); BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_MISSING_DEPENDENCIES); BIND_GLOBAL_ENUM_CONSTANT(ERR_FILE_EOF); - BIND_GLOBAL_ENUM_CONSTANT(ERR_CANT_OPEN); ///< Can't open a resource/socket/file - BIND_GLOBAL_ENUM_CONSTANT(ERR_CANT_CREATE); - BIND_GLOBAL_ENUM_CONSTANT(ERR_PARSE_ERROR); + BIND_GLOBAL_ENUM_CONSTANT(ERR_CANT_OPEN); + BIND_GLOBAL_ENUM_CONSTANT(ERR_CANT_CREATE); // (20) BIND_GLOBAL_ENUM_CONSTANT(ERR_QUERY_FAILED); BIND_GLOBAL_ENUM_CONSTANT(ERR_ALREADY_IN_USE); - BIND_GLOBAL_ENUM_CONSTANT(ERR_LOCKED); ///< resource is locked + BIND_GLOBAL_ENUM_CONSTANT(ERR_LOCKED); BIND_GLOBAL_ENUM_CONSTANT(ERR_TIMEOUT); + BIND_GLOBAL_ENUM_CONSTANT(ERR_CANT_CONNECT); // (25) + BIND_GLOBAL_ENUM_CONSTANT(ERR_CANT_RESOLVE); + BIND_GLOBAL_ENUM_CONSTANT(ERR_CONNECTION_ERROR); BIND_GLOBAL_ENUM_CONSTANT(ERR_CANT_ACQUIRE_RESOURCE); - BIND_GLOBAL_ENUM_CONSTANT(ERR_INVALID_DATA); ///< Data passed is invalid - BIND_GLOBAL_ENUM_CONSTANT(ERR_INVALID_PARAMETER); ///< Parameter passed is invalid - BIND_GLOBAL_ENUM_CONSTANT(ERR_ALREADY_EXISTS); ///< When adding ), item already exists - BIND_GLOBAL_ENUM_CONSTANT(ERR_DOES_NOT_EXIST); ///< When retrieving/erasing ), it item does not exist - BIND_GLOBAL_ENUM_CONSTANT(ERR_DATABASE_CANT_READ); ///< database is full - BIND_GLOBAL_ENUM_CONSTANT(ERR_DATABASE_CANT_WRITE); ///< database is full + BIND_GLOBAL_ENUM_CONSTANT(ERR_CANT_FORK); + BIND_GLOBAL_ENUM_CONSTANT(ERR_INVALID_DATA); // (30) + BIND_GLOBAL_ENUM_CONSTANT(ERR_INVALID_PARAMETER); + BIND_GLOBAL_ENUM_CONSTANT(ERR_ALREADY_EXISTS); + BIND_GLOBAL_ENUM_CONSTANT(ERR_DOES_NOT_EXIST); + BIND_GLOBAL_ENUM_CONSTANT(ERR_DATABASE_CANT_READ); + BIND_GLOBAL_ENUM_CONSTANT(ERR_DATABASE_CANT_WRITE); // (35) BIND_GLOBAL_ENUM_CONSTANT(ERR_COMPILATION_FAILED); BIND_GLOBAL_ENUM_CONSTANT(ERR_METHOD_NOT_FOUND); BIND_GLOBAL_ENUM_CONSTANT(ERR_LINK_FAILED); BIND_GLOBAL_ENUM_CONSTANT(ERR_SCRIPT_FAILED); - BIND_GLOBAL_ENUM_CONSTANT(ERR_CYCLIC_LINK); + BIND_GLOBAL_ENUM_CONSTANT(ERR_CYCLIC_LINK); // (40) + BIND_GLOBAL_ENUM_CONSTANT(ERR_INVALID_DECLARATION); + BIND_GLOBAL_ENUM_CONSTANT(ERR_DUPLICATE_SYMBOL); + BIND_GLOBAL_ENUM_CONSTANT(ERR_PARSE_ERROR); BIND_GLOBAL_ENUM_CONSTANT(ERR_BUSY); - BIND_GLOBAL_ENUM_CONSTANT(ERR_HELP); ///< user requested help!! - BIND_GLOBAL_ENUM_CONSTANT(ERR_BUG); ///< a bug in the software certainly happened ), due to a double check failing or unexpected behavior. + BIND_GLOBAL_ENUM_CONSTANT(ERR_SKIP); // (45) + BIND_GLOBAL_ENUM_CONSTANT(ERR_HELP); + BIND_GLOBAL_ENUM_CONSTANT(ERR_BUG); + BIND_GLOBAL_ENUM_CONSTANT(ERR_PRINTER_ON_FIRE); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_NONE); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_RANGE); diff --git a/core/hash_map.h b/core/hash_map.h index 44459a3080..edc67e7806 100644 --- a/core/hash_map.h +++ b/core/hash_map.h @@ -112,7 +112,7 @@ private: void erase_hash_table() { - ERR_FAIL_COND(elements); + ERR_FAIL_COND_MSG(elements, "Cannot erase hash table if there are still elements inside."); memdelete_arr(hash_table); hash_table = 0; @@ -151,31 +151,28 @@ private: return; Element **new_hash_table = memnew_arr(Element *, ((uint64_t)1 << new_hash_table_power)); - if (!new_hash_table) { - - ERR_PRINT("Out of Memory"); - return; - } + ERR_FAIL_COND_MSG(!new_hash_table, "Out of memory."); for (int i = 0; i < (1 << new_hash_table_power); i++) { new_hash_table[i] = 0; } - for (int i = 0; i < (1 << hash_table_power); i++) { + if (hash_table) { + for (int i = 0; i < (1 << hash_table_power); i++) { - while (hash_table[i]) { + while (hash_table[i]) { - Element *se = hash_table[i]; - hash_table[i] = se->next; - int new_pos = se->hash & ((1 << new_hash_table_power) - 1); - se->next = new_hash_table[new_pos]; - new_hash_table[new_pos] = se; + Element *se = hash_table[i]; + hash_table[i] = se->next; + int new_pos = se->hash & ((1 << new_hash_table_power) - 1); + se->next = new_hash_table[new_pos]; + new_hash_table[new_pos] = se; + } } - } - if (hash_table) memdelete_arr(hash_table); + } hash_table = new_hash_table; hash_table_power = new_hash_table_power; } @@ -207,12 +204,13 @@ private: /* if element doesn't exist, create it */ Element *e = memnew(Element); - ERR_FAIL_COND_V(!e, NULL); /* out of memory */ + ERR_FAIL_COND_V_MSG(!e, NULL, "Out of memory."); uint32_t hash = Hasher::hash(p_key); uint32_t index = hash & ((1 << hash_table_power) - 1); e->next = hash_table[index]; e->hash = hash; e->pair.key = p_key; + e->pair.data = TData(); hash_table[index] = e; elements++; @@ -494,8 +492,7 @@ public: } else { /* get the next key */ const Element *e = get_element(*p_key); - ERR_FAIL_COND_V(!e, NULL); /* invalid key supplied */ - + ERR_FAIL_COND_V_MSG(!e, NULL, "Invalid key supplied."); if (e->next) { /* if there is a "next" in the list, return that */ return &e->next->pair.key; diff --git a/core/image.cpp b/core/image.cpp index f547d7e973..74706535b3 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -83,6 +83,7 @@ const char *Image::format_names[Image::FORMAT_MAX] = { }; SavePNGFunc Image::save_png_func = NULL; +SaveEXRFunc Image::save_exr_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) { @@ -240,27 +241,27 @@ int Image::get_format_block_size(Format p_format) { case FORMAT_RGTC_RG: { //bc5 case case FORMAT_DXT1: return 4; - } break; + } case FORMAT_PVRTC2: case FORMAT_PVRTC2A: { return 4; - } break; + } case FORMAT_PVRTC4A: case FORMAT_PVRTC4: { return 4; - } break; + } case FORMAT_ETC: { return 4; - } break; + } case FORMAT_BPTC_RGBA: case FORMAT_BPTC_RGBF: case FORMAT_BPTC_RGBFU: { return 4; - } break; + } case FORMAT_ETC2_R11: //etc2 case FORMAT_ETC2_R11S: //signed: NOT srgb. case FORMAT_ETC2_RG11: @@ -270,7 +271,7 @@ int Image::get_format_block_size(Format p_format) { case FORMAT_ETC2_RGB8A1: { return 4; - } break; + } default: { } } @@ -422,8 +423,7 @@ void Image::convert(Format p_new_format) { if (format > FORMAT_RGBE9995 || p_new_format > FORMAT_RGBE9995) { - ERR_EXPLAIN("Cannot convert to <-> from compressed formats. Use compress() and decompress() instead."); - ERR_FAIL(); + ERR_FAIL_MSG("Cannot convert to <-> from compressed formats. Use compress() and decompress() instead."); } else if (format > FORMAT_RGBA8 || p_new_format > FORMAT_RGBA8) { @@ -495,8 +495,8 @@ void Image::convert(Format p_new_format) { case FORMAT_RGBA8 | (FORMAT_RGB8 << 8): _convert<3, true, 3, false, false, false>(width, height, rptr, wptr); break; } - r = PoolVector<uint8_t>::Read(); - w = PoolVector<uint8_t>::Write(); + r.release(); + w.release(); bool gen_mipmaps = mipmaps; @@ -725,9 +725,131 @@ static void _scale_nearest(const uint8_t *__restrict p_src, uint8_t *__restrict } } +#define LANCZOS_TYPE 3 + +static float _lanczos(float p_x) { + return Math::abs(p_x) >= LANCZOS_TYPE ? 0 : Math::sincn(p_x) * Math::sincn(p_x / LANCZOS_TYPE); +} + +template <int CC, class T> +static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { + + int32_t src_width = p_src_width; + int32_t src_height = p_src_height; + int32_t dst_height = p_dst_height; + int32_t dst_width = p_dst_width; + + uint32_t buffer_size = src_height * dst_width * CC; + float *buffer = memnew_arr(float, buffer_size); // Store the first pass in a buffer + + { // FIRST PASS (horizontal) + + float x_scale = float(src_width) / float(dst_width); + + float scale_factor = MAX(x_scale, 1); // A larger kernel is required only when downscaling + int32_t half_kernel = LANCZOS_TYPE * scale_factor; + + float *kernel = memnew_arr(float, half_kernel * 2); + + for (int32_t buffer_x = 0; buffer_x < dst_width; buffer_x++) { + + // The corresponding point on the source image + float src_x = (buffer_x + 0.5f) * x_scale; // Offset by 0.5 so it uses the pixel's center + int32_t start_x = MAX(0, int32_t(src_x) - half_kernel + 1); + int32_t end_x = MIN(src_width - 1, int32_t(src_x) + half_kernel); + + // Create the kernel used by all the pixels of the column + for (int32_t target_x = start_x; target_x <= end_x; target_x++) + kernel[target_x - start_x] = _lanczos((target_x + 0.5f - src_x) / scale_factor); + + for (int32_t buffer_y = 0; buffer_y < src_height; buffer_y++) { + + float pixel[CC] = { 0 }; + float weight = 0; + + for (int32_t target_x = start_x; target_x <= end_x; target_x++) { + + float lanczos_val = kernel[target_x - start_x]; + weight += lanczos_val; + + const T *__restrict src_data = ((const T *)p_src) + (buffer_y * src_width + target_x) * CC; + + for (uint32_t i = 0; i < CC; i++) { + if (sizeof(T) == 2) //half float + pixel[i] += Math::half_to_float(src_data[i]) * lanczos_val; + else + pixel[i] += src_data[i] * lanczos_val; + } + } + + float *dst_data = ((float *)buffer) + (buffer_y * dst_width + buffer_x) * CC; + + for (uint32_t i = 0; i < CC; i++) + dst_data[i] = pixel[i] / weight; // Normalize the sum of all the samples + } + } + + memdelete_arr(kernel); + } // End of first pass + + { // SECOND PASS (vertical + result) + + float y_scale = float(src_height) / float(dst_height); + + float scale_factor = MAX(y_scale, 1); + int32_t half_kernel = LANCZOS_TYPE * scale_factor; + + float *kernel = memnew_arr(float, half_kernel * 2); + + for (int32_t dst_y = 0; dst_y < dst_height; dst_y++) { + + float buffer_y = (dst_y + 0.5f) * y_scale; + int32_t start_y = MAX(0, int32_t(buffer_y) - half_kernel + 1); + int32_t end_y = MIN(src_height - 1, int32_t(buffer_y) + half_kernel); + + for (int32_t target_y = start_y; target_y <= end_y; target_y++) + kernel[target_y - start_y] = _lanczos((target_y + 0.5f - buffer_y) / scale_factor); + + for (int32_t dst_x = 0; dst_x < dst_width; dst_x++) { + + float pixel[CC] = { 0 }; + float weight = 0; + + for (int32_t target_y = start_y; target_y <= end_y; target_y++) { + + float lanczos_val = kernel[target_y - start_y]; + weight += lanczos_val; + + float *buffer_data = ((float *)buffer) + (target_y * dst_width + dst_x) * CC; + + for (uint32_t i = 0; i < CC; i++) + pixel[i] += buffer_data[i] * lanczos_val; + } + + T *dst_data = ((T *)p_dst) + (dst_y * dst_width + dst_x) * CC; + + for (uint32_t i = 0; i < CC; i++) { + pixel[i] /= weight; + + if (sizeof(T) == 1) //byte + dst_data[i] = CLAMP(Math::fast_ftoi(pixel[i]), 0, 255); + else if (sizeof(T) == 2) //half float + dst_data[i] = Math::make_half_float(pixel[i]); + else // float + dst_data[i] = pixel[i]; + } + } + } + + memdelete_arr(kernel); + } // End of second pass + + memdelete_arr(buffer); +} + static void _overlay(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, float p_alpha, uint32_t p_width, uint32_t p_height, uint32_t p_pixel_size) { - uint16_t alpha = CLAMP((uint16_t)(p_alpha * 256.0f), 0, 256); + uint16_t alpha = MIN((uint16_t)(p_alpha * 256.0f), 256); for (uint32_t i = 0; i < p_width * p_height * p_pixel_size; i++) { @@ -741,10 +863,7 @@ bool Image::is_size_po2() const { void Image::resize_to_po2(bool p_square) { - if (!_can_modify(format)) { - ERR_EXPLAIN("Cannot resize in indexed, compressed or custom image formats."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot resize in compressed or custom image formats."); int w = next_power_of_2(width); int h = next_power_of_2(height); @@ -760,22 +879,16 @@ void Image::resize_to_po2(bool p_square) { void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { - if (data.size() == 0) { - ERR_EXPLAIN("Cannot resize image before creating it, use create() or create_from_data() first."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(data.size() == 0, "Cannot resize image before creating it, use create() or create_from_data() first."); - if (!_can_modify(format)) { - ERR_EXPLAIN("Cannot resize in indexed, compressed or custom image formats."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot resize in compressed or custom image formats."); bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */; - ERR_FAIL_COND(p_width <= 0); - ERR_FAIL_COND(p_height <= 0); - ERR_FAIL_COND(p_width > MAX_WIDTH); - ERR_FAIL_COND(p_height > MAX_HEIGHT); + ERR_FAIL_COND_MSG(p_width <= 0, "Image width must be greater than 0."); + 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) + "."); if (p_width == width && p_height == height) return; @@ -939,10 +1052,35 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { } } } break; + case INTERPOLATE_LANCZOS: { + + if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) { + switch (get_format_pixel_size(format)) { + case 1: _scale_lanczos<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 2: _scale_lanczos<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 3: _scale_lanczos<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: _scale_lanczos<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } + } else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) { + switch (get_format_pixel_size(format)) { + case 4: _scale_lanczos<1, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 8: _scale_lanczos<2, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 12: _scale_lanczos<3, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 16: _scale_lanczos<4, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } + } else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) { + switch (get_format_pixel_size(format)) { + case 2: _scale_lanczos<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: _scale_lanczos<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 6: _scale_lanczos<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 8: _scale_lanczos<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } + } + } break; } - r = PoolVector<uint8_t>::Read(); - w = PoolVector<uint8_t>::Write(); + r.release(); + w.release(); if (interpolate_mipmaps) { dst._copy_internals_from(dst2); @@ -956,16 +1094,14 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { void Image::crop_from_point(int p_x, int p_y, int p_width, int p_height) { - if (!_can_modify(format)) { - ERR_EXPLAIN("Cannot crop in indexed, compressed or custom image formats."); - ERR_FAIL(); - } - ERR_FAIL_COND(p_x < 0); - ERR_FAIL_COND(p_y < 0); - ERR_FAIL_COND(p_width <= 0); - ERR_FAIL_COND(p_height <= 0); - ERR_FAIL_COND(p_x + p_width > MAX_WIDTH); - ERR_FAIL_COND(p_y + p_height > MAX_HEIGHT); + ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot crop in compressed or custom image formats."); + + ERR_FAIL_COND_MSG(p_x < 0, "Start x position cannot be smaller than 0."); + ERR_FAIL_COND_MSG(p_y < 0, "Start y position cannot be smaller than 0."); + ERR_FAIL_COND_MSG(p_width <= 0, "Width of image must be greater than 0."); + ERR_FAIL_COND_MSG(p_height <= 0, "Height of image must be greater than 0."); + ERR_FAIL_COND_MSG(p_x + p_width > MAX_WIDTH, "End x position cannot be greater than " + itos(MAX_WIDTH) + "."); + ERR_FAIL_COND_MSG(p_y + p_height > MAX_HEIGHT, "End y position cannot be greater than " + itos(MAX_HEIGHT) + "."); /* to save memory, cropping should be done in-place, however, since this function will most likely either not be used much, or in critical areas, for now it won't, because @@ -1013,10 +1149,7 @@ void Image::crop(int p_width, int p_height) { void Image::flip_y() { - if (!_can_modify(format)) { - ERR_EXPLAIN("Cannot flip_y in indexed, compressed or custom image formats."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot flip_y in compressed or custom image formats."); bool used_mipmaps = has_mipmaps(); if (used_mipmaps) { @@ -1049,10 +1182,7 @@ void Image::flip_y() { void Image::flip_x() { - if (!_can_modify(format)) { - ERR_EXPLAIN("Cannot flip_x in indexed, compressed or custom image formats."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot flip_x in compressed or custom image formats."); bool used_mipmaps = has_mipmaps(); if (used_mipmaps) { @@ -1154,8 +1284,8 @@ static void _generate_po2_mipmap(const Component *p_src, Component *p_dst, uint3 Component *dst_ptr = &p_dst[i * dst_w * CC]; uint32_t count = dst_w; - while (count--) { - + while (count) { + count--; for (int j = 0; j < CC; j++) { average_func(dst_ptr[j], rup_ptr[j], rup_ptr[j + right_step], rdown_ptr[j], rdown_ptr[j + right_step]); } @@ -1192,6 +1322,8 @@ void Image::expand_x2_hq2x() { PoolVector<uint8_t>::Read r = data.read(); PoolVector<uint8_t>::Write w = dest.write(); + ERR_FAIL_COND(!r.ptr()); + hq2x_resize((const uint32_t *)r.ptr(), width, height, (uint32_t *)w.ptr()); } @@ -1222,6 +1354,7 @@ void Image::shrink_x2() { int new_size = data.size() - ofs; new_img.resize(new_size); + ERR_FAIL_COND(new_img.size() == 0); { PoolVector<uint8_t>::Write w = new_img.write(); @@ -1241,6 +1374,8 @@ void Image::shrink_x2() { ERR_FAIL_COND(!_can_modify(format)); int ps = get_format_pixel_size(format); new_img.resize((width / 2) * (height / 2) * ps); + ERR_FAIL_COND(new_img.size() == 0); + ERR_FAIL_COND(data.size() == 0); { PoolVector<uint8_t>::Write w = new_img.write(); @@ -1266,7 +1401,8 @@ void Image::shrink_x2() { case FORMAT_RGBAH: _generate_po2_mipmap<uint16_t, 4, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(r.ptr()), reinterpret_cast<uint16_t *>(w.ptr()), width, height); break; case FORMAT_RGBE9995: _generate_po2_mipmap<uint32_t, 1, false, Image::average_4_rgbe9995, Image::renormalize_rgbe9995>(reinterpret_cast<const uint32_t *>(r.ptr()), reinterpret_cast<uint32_t *>(w.ptr()), width, height); break; - default: {} + default: { + } } } @@ -1308,12 +1444,9 @@ void Image::normalize() { Error Image::generate_mipmaps(bool p_renormalize) { - if (!_can_modify(format)) { - ERR_EXPLAIN("Cannot generate mipmaps in indexed, compressed or custom image formats."); - ERR_FAIL_V(ERR_UNAVAILABLE); - } + ERR_FAIL_COND_V_MSG(!_can_modify(format), ERR_UNAVAILABLE, "Cannot generate mipmaps in compressed or custom image formats."); - ERR_FAIL_COND_V(width == 0 || height == 0, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(width == 0 || height == 0, ERR_UNCONFIGURED, "Cannot generate mipmaps with width or height equal to 0."); int mmcount; @@ -1398,7 +1531,8 @@ Error Image::generate_mipmaps(bool p_renormalize) { _generate_po2_mipmap<uint32_t, 1, false, Image::average_4_rgbe9995, Image::renormalize_rgbe9995>(reinterpret_cast<const uint32_t *>(&wp[prev_ofs]), reinterpret_cast<uint32_t *>(&wp[ofs]), prev_w, prev_h); break; - default: {} + default: { + } } prev_ofs = ofs; @@ -1463,10 +1597,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma int mm; int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); - if (size != p_data.size()) { - ERR_EXPLAIN("Expected data size of " + itos(size) + " bytes in Image::create(), got instead " + itos(p_data.size()) + " bytes."); - ERR_FAIL_COND(p_data.size() != size); - } + ERR_FAIL_COND_MSG(p_data.size() != size, "Expected data size of " + itos(size) + " bytes in Image::create(), got instead " + itos(p_data.size()) + " bytes."); height = p_height; width = p_width; @@ -1612,7 +1743,8 @@ void Image::create(const char **p_xpm) { if (y == (size_height - 1)) status = DONE; } break; - default: {} + default: { + } } line++; @@ -1685,7 +1817,8 @@ bool Image::is_invisible() const { case FORMAT_DXT5: { detected = true; } break; - default: {} + default: { + } } return !detected; @@ -1729,7 +1862,8 @@ Image::AlphaMode Image::detect_alpha() const { case FORMAT_DXT5: { detected = true; } break; - default: {} + default: { + } } if (detected) @@ -1757,6 +1891,14 @@ Error Image::save_png(const String &p_path) const { return save_png_func(p_path, Ref<Image>((Image *)this)); } +Error Image::save_exr(const String &p_path, bool p_grayscale) const { + + if (save_exr_func == NULL) + return ERR_UNAVAILABLE; + + return save_exr_func(p_path, Ref<Image>((Image *)this), p_grayscale); +} + int Image::get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps) { int mm; @@ -1871,7 +2013,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) + if (format != FORMAT_LA8 && format != FORMAT_RGBA8 && format != FORMAT_RGBAF && format != FORMAT_RGBAH && format != FORMAT_RGBA4444 && format != FORMAT_RGBA5551) return Rect2(Point2(), Size2(width, height)); int len = data.size(); @@ -1879,17 +2021,13 @@ Rect2 Image::get_used_rect() const { if (len == 0) return Rect2(); - //int data_size = len; - PoolVector<uint8_t>::Read r = data.read(); - const unsigned char *rptr = r.ptr(); - - int ps = format == FORMAT_LA8 ? 2 : 4; + const_cast<Image *>(this)->lock(); int minx = 0xFFFFFF, miny = 0xFFFFFFF; int maxx = -1, maxy = -1; for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { - bool opaque = rptr[(j * width + i) * ps + (ps - 1)] > 2; + bool opaque = get_pixel(i, j).a > 0.99; if (!opaque) continue; if (i > maxx) @@ -1903,6 +2041,8 @@ Rect2 Image::get_used_rect() const { } } + const_cast<Image *>(this)->unlock(); + if (maxx == -1) return Rect2(); else @@ -1918,7 +2058,7 @@ Ref<Image> Image::get_rect(const Rect2 &p_area) const { void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest) { - ERR_FAIL_COND(p_src.is_null()); + ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); int dsize = data.size(); int srcdsize = p_src->data.size(); ERR_FAIL_COND(dsize == 0); @@ -1968,16 +2108,16 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest) { - ERR_FAIL_COND(p_src.is_null()); - ERR_FAIL_COND(p_mask.is_null()); + ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); + ERR_FAIL_COND_MSG(p_mask.is_null(), "It's not a reference to a valid Image object."); int dsize = data.size(); int srcdsize = p_src->data.size(); int maskdsize = p_mask->data.size(); ERR_FAIL_COND(dsize == 0); ERR_FAIL_COND(srcdsize == 0); ERR_FAIL_COND(maskdsize == 0); - ERR_FAIL_COND(p_src->width != p_mask->width); - ERR_FAIL_COND(p_src->height != p_mask->height); + ERR_FAIL_COND_MSG(p_src->width != p_mask->width, "Source image width is different from mask width."); + ERR_FAIL_COND_MSG(p_src->height != p_mask->height, "Source image height is different from mask height."); ERR_FAIL_COND(format != p_src->format); Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); @@ -2031,7 +2171,7 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest) { - ERR_FAIL_COND(p_src.is_null()); + ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); int dsize = data.size(); int srcdsize = p_src->data.size(); ERR_FAIL_COND(dsize == 0); @@ -2081,16 +2221,16 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const P void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest) { - ERR_FAIL_COND(p_src.is_null()); - ERR_FAIL_COND(p_mask.is_null()); + ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); + ERR_FAIL_COND_MSG(p_mask.is_null(), "It's not a reference to a valid Image object."); int dsize = data.size(); int srcdsize = p_src->data.size(); int maskdsize = p_mask->data.size(); ERR_FAIL_COND(dsize == 0); ERR_FAIL_COND(srcdsize == 0); ERR_FAIL_COND(maskdsize == 0); - ERR_FAIL_COND(p_src->width != p_mask->width); - ERR_FAIL_COND(p_src->height != p_mask->height); + ERR_FAIL_COND_MSG(p_src->width != p_mask->width, "Source image width is different from mask width."); + ERR_FAIL_COND_MSG(p_src->height != p_mask->height, "Source image height is different from mask height."); ERR_FAIL_COND(format != p_src->format); Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); @@ -2236,7 +2376,7 @@ void Image::lock() { void Image::unlock() { - write_lock = PoolVector<uint8_t>::Write(); + write_lock.release(); } Color Image::get_pixelv(const Point2 &p_src) const { @@ -2247,10 +2387,7 @@ Color Image::get_pixel(int p_x, int p_y) const { uint8_t *ptr = write_lock.ptr(); #ifdef DEBUG_ENABLED - if (!ptr) { - ERR_EXPLAIN("Image must be locked with 'lock()' before using get_pixel()"); - ERR_FAIL_COND_V(!ptr, Color()); - } + 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()); @@ -2263,38 +2400,36 @@ Color Image::get_pixel(int p_x, int p_y) const { case FORMAT_L8: { float l = ptr[ofs] / 255.0; return Color(l, l, l, 1); - } break; + } case FORMAT_LA8: { float l = ptr[ofs * 2 + 0] / 255.0; float a = ptr[ofs * 2 + 1] / 255.0; return Color(l, l, l, a); - } break; + } case FORMAT_R8: { float r = ptr[ofs] / 255.0; return Color(r, 0, 0, 1); - } break; + } case FORMAT_RG8: { float r = ptr[ofs * 2 + 0] / 255.0; float g = ptr[ofs * 2 + 1] / 255.0; return Color(r, g, 0, 1); - } break; + } case FORMAT_RGB8: { float r = ptr[ofs * 3 + 0] / 255.0; float g = ptr[ofs * 3 + 1] / 255.0; float b = ptr[ofs * 3 + 2] / 255.0; return Color(r, g, b, 1); - - } break; + } case FORMAT_RGBA8: { float r = ptr[ofs * 4 + 0] / 255.0; float g = ptr[ofs * 4 + 1] / 255.0; float b = ptr[ofs * 4 + 2] / 255.0; float a = ptr[ofs * 4 + 3] / 255.0; return Color(r, g, b, a); - - } break; + } case FORMAT_RGBA4444: { uint16_t u = ((uint16_t *)ptr)[ofs]; float r = (u & 0xF) / 15.0; @@ -2302,8 +2437,7 @@ Color Image::get_pixel(int p_x, int p_y) const { float b = ((u >> 8) & 0xF) / 15.0; float a = ((u >> 12) & 0xF) / 15.0; return Color(r, g, b, a); - - } break; + } case FORMAT_RGBA5551: { uint16_t u = ((uint16_t *)ptr)[ofs]; @@ -2312,25 +2446,25 @@ Color Image::get_pixel(int p_x, int p_y) const { float b = ((u >> 10) & 0x1F) / 15.0; float a = ((u >> 15) & 0x1) / 1.0; return Color(r, g, b, a); - } break; + } case FORMAT_RF: { float r = ((float *)ptr)[ofs]; return Color(r, 0, 0, 1); - } break; + } case FORMAT_RGF: { float r = ((float *)ptr)[ofs * 2 + 0]; float g = ((float *)ptr)[ofs * 2 + 1]; return Color(r, g, 0, 1); - } break; + } case FORMAT_RGBF: { float r = ((float *)ptr)[ofs * 3 + 0]; float g = ((float *)ptr)[ofs * 3 + 1]; float b = ((float *)ptr)[ofs * 3 + 2]; return Color(r, g, b, 1); - } break; + } case FORMAT_RGBAF: { float r = ((float *)ptr)[ofs * 4 + 0]; @@ -2338,25 +2472,25 @@ Color Image::get_pixel(int p_x, int p_y) const { float b = ((float *)ptr)[ofs * 4 + 2]; float a = ((float *)ptr)[ofs * 4 + 3]; return Color(r, g, b, a); - } break; + } case FORMAT_RH: { uint16_t r = ((uint16_t *)ptr)[ofs]; return Color(Math::half_to_float(r), 0, 0, 1); - } break; + } case FORMAT_RGH: { uint16_t r = ((uint16_t *)ptr)[ofs * 2 + 0]; uint16_t g = ((uint16_t *)ptr)[ofs * 2 + 1]; return Color(Math::half_to_float(r), Math::half_to_float(g), 0, 1); - } break; + } case FORMAT_RGBH: { uint16_t r = ((uint16_t *)ptr)[ofs * 3 + 0]; uint16_t g = ((uint16_t *)ptr)[ofs * 3 + 1]; uint16_t b = ((uint16_t *)ptr)[ofs * 3 + 2]; return Color(Math::half_to_float(r), Math::half_to_float(g), Math::half_to_float(b), 1); - } break; + } case FORMAT_RGBAH: { uint16_t r = ((uint16_t *)ptr)[ofs * 4 + 0]; @@ -2364,32 +2498,25 @@ Color Image::get_pixel(int p_x, int p_y) const { uint16_t b = ((uint16_t *)ptr)[ofs * 4 + 2]; uint16_t a = ((uint16_t *)ptr)[ofs * 4 + 3]; return Color(Math::half_to_float(r), Math::half_to_float(g), Math::half_to_float(b), Math::half_to_float(a)); - } break; + } case FORMAT_RGBE9995: { return Color::from_rgbe9995(((uint32_t *)ptr)[ofs]); - - } break; + } default: { - ERR_EXPLAIN("Can't get_pixel() on compressed image, sorry."); - ERR_FAIL_V(Color()); + ERR_FAIL_V_MSG(Color(), "Can't get_pixel() on compressed image, sorry."); } } - - return Color(); } void Image::set_pixelv(const Point2 &p_dst, const Color &p_color) { - return set_pixel(p_dst.x, p_dst.y, 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 - if (!ptr) { - ERR_EXPLAIN("Image must be locked with 'lock()' before using set_pixel()"); - ERR_FAIL_COND(!ptr); - } + 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); @@ -2501,8 +2628,7 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) { } break; default: { - ERR_EXPLAIN("Can't set_pixel() on compressed image, sorry."); - ERR_FAIL(); + ERR_FAIL_MSG("Can't set_pixel() on compressed image, sorry."); } } } @@ -2594,6 +2720,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("load", "path"), &Image::load); ClassDB::bind_method(D_METHOD("save_png", "path"), &Image::save_png); + ClassDB::bind_method(D_METHOD("save_exr", "path", "grayscale"), &Image::save_exr, DEFVAL(false)); ClassDB::bind_method(D_METHOD("detect_alpha"), &Image::detect_alpha); ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible); @@ -2682,6 +2809,7 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(INTERPOLATE_BILINEAR); BIND_ENUM_CONSTANT(INTERPOLATE_CUBIC); BIND_ENUM_CONSTANT(INTERPOLATE_TRILINEAR); + BIND_ENUM_CONSTANT(INTERPOLATE_LANCZOS); BIND_ENUM_CONSTANT(ALPHA_NONE); BIND_ENUM_CONSTANT(ALPHA_BIT); @@ -2770,6 +2898,8 @@ void Image::bumpmap_to_normalmap(float bump_scale) { PoolVector<uint8_t>::Read rp = data.read(); PoolVector<uint8_t>::Write wp = result_image.write(); + ERR_FAIL_COND(!rp.ptr()); + unsigned char *write_ptr = wp.ptr(); float *read_ptr = (float *)rp.ptr(); @@ -2805,7 +2935,7 @@ void Image::srgb_to_linear() { if (data.size() == 0) return; - static const uint8_t srgb2lin[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 24, 25, 26, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 33, 34, 35, 36, 36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 47, 48, 49, 50, 51, 52, 53, 54, 55, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 70, 71, 72, 73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 85, 87, 88, 89, 90, 92, 93, 94, 95, 97, 98, 99, 101, 102, 103, 105, 106, 107, 109, 110, 112, 113, 114, 116, 117, 119, 120, 122, 123, 125, 126, 128, 129, 131, 132, 134, 135, 137, 139, 140, 142, 144, 145, 147, 148, 150, 152, 153, 155, 157, 159, 160, 162, 164, 166, 167, 169, 171, 173, 175, 176, 178, 180, 182, 184, 186, 188, 190, 192, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 218, 220, 222, 224, 226, 228, 230, 232, 235, 237, 239, 241, 243, 245, 248, 250, 252 }; + static const uint8_t srgb2lin[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 24, 25, 26, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 33, 34, 35, 36, 36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 47, 48, 49, 50, 51, 52, 53, 54, 55, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 70, 71, 72, 73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 85, 87, 88, 89, 90, 92, 93, 94, 95, 97, 98, 99, 101, 102, 103, 105, 106, 107, 109, 110, 112, 113, 114, 116, 117, 119, 120, 122, 123, 125, 126, 128, 129, 131, 132, 134, 135, 137, 139, 140, 142, 144, 145, 147, 148, 150, 152, 153, 155, 157, 159, 160, 162, 164, 166, 167, 169, 171, 173, 175, 176, 178, 180, 182, 184, 186, 188, 190, 192, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 218, 220, 222, 224, 226, 228, 230, 232, 235, 237, 239, 241, 243, 245, 248, 250, 252, 255 }; ERR_FAIL_COND(format != FORMAT_RGB8 && format != FORMAT_RGBA8); diff --git a/core/image.h b/core/image.h index 69a42f169a..94ee8a2c33 100644 --- a/core/image.h +++ b/core/image.h @@ -49,11 +49,14 @@ class Image; typedef Error (*SavePNGFunc)(const String &p_path, 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); + class Image : public Resource { GDCLASS(Image, Resource); public: static SavePNGFunc save_png_func; + static SaveEXRFunc save_exr_func; enum { MAX_WIDTH = 16384, // force a limit somehow @@ -109,6 +112,7 @@ public: INTERPOLATE_BILINEAR, INTERPOLATE_CUBIC, INTERPOLATE_TRILINEAR, + INTERPOLATE_LANCZOS, /* INTERPOLATE_TRICUBIC, */ /* INTERPOLATE GAUSS */ }; @@ -216,9 +220,7 @@ public: /** * Resize the image, using the preferred interpolation method. - * Indexed-Color images always use INTERPOLATE_NEAREST. */ - void resize_to_po2(bool p_square = false); void resize(int p_width, int p_height, Interpolation p_interpolation = INTERPOLATE_BILINEAR); void shrink_x2(); @@ -257,6 +259,7 @@ public: Error load(const String &p_path); Error save_png(const String &p_path) const; + Error save_exr(const String &p_path, bool p_grayscale) const; /** * create an empty image @@ -349,11 +352,11 @@ public: Color get_pixelv(const Point2 &p_src) const; Color get_pixel(int p_x, int p_y) const; - void set_pixelv(const Point2 &p_dest, const Color &p_color); + void set_pixelv(const Point2 &p_dst, const Color &p_color); void set_pixel(int p_x, int p_y, const Color &p_color); void copy_internals_from(const Ref<Image> &p_image) { - ERR_FAIL_COND(p_image.is_null()); + ERR_FAIL_COND_MSG(p_image.is_null(), "It's not a reference to a valid Image object."); format = p_image->format; width = p_image->width; height = p_image->height; diff --git a/core/input_map.cpp b/core/input_map.cpp index 15f68f9c2a..05c75febf2 100644 --- a/core/input_map.cpp +++ b/core/input_map.cpp @@ -56,7 +56,7 @@ void InputMap::_bind_methods() { void InputMap::add_action(const StringName &p_action, float p_deadzone) { - ERR_FAIL_COND(input_map.has(p_action)); + ERR_FAIL_COND_MSG(input_map.has(p_action), "InputMap already has action '" + String(p_action) + "'."); input_map[p_action] = Action(); static int last_id = 1; input_map[p_action].id = last_id; @@ -66,7 +66,7 @@ void InputMap::add_action(const StringName &p_action, float p_deadzone) { void InputMap::erase_action(const StringName &p_action) { - ERR_FAIL_COND(!input_map.has(p_action)); + ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); input_map.erase(p_action); } @@ -126,15 +126,15 @@ bool InputMap::has_action(const StringName &p_action) const { void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) { - ERR_FAIL_COND(!input_map.has(p_action)); + ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); input_map[p_action].deadzone = p_deadzone; } void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event) { - ERR_FAIL_COND(p_event.is_null()); - ERR_FAIL_COND(!input_map.has(p_action)); + ERR_FAIL_COND_MSG(p_event.is_null(), "It's not a reference to a valid InputEvent object."); + ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); if (_find_event(input_map[p_action], p_event)) return; //already gots @@ -143,13 +143,13 @@ void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent bool InputMap::action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event) { - ERR_FAIL_COND_V(!input_map.has(p_action), false); + ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); return (_find_event(input_map[p_action], p_event) != NULL); } void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) { - ERR_FAIL_COND(!input_map.has(p_action)); + ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); List<Ref<InputEvent> >::Element *E = _find_event(input_map[p_action], p_event); if (E) @@ -158,7 +158,7 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEve void InputMap::action_erase_events(const StringName &p_action) { - ERR_FAIL_COND(!input_map.has(p_action)); + ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); input_map[p_action].inputs.clear(); } @@ -192,17 +192,14 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed, float *p_strength) const { Map<StringName, Action>::Element *E = input_map.find(p_action); - if (!E) { - ERR_EXPLAIN("Request for nonexistent InputMap action: " + String(p_action)); - ERR_FAIL_COND_V(!E, false); - } + ERR_FAIL_COND_V_MSG(!E, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); Ref<InputEventAction> input_event_action = p_event; if (input_event_action.is_valid()) { if (p_pressed != NULL) *p_pressed = input_event_action->is_pressed(); if (p_strength != NULL) - *p_strength = (*p_pressed) ? 1.0f : 0.0f; + *p_strength = (p_pressed != NULL && *p_pressed) ? input_event_action->get_strength() : 0.0f; return input_event_action->get_action() == p_action; } @@ -336,6 +333,6 @@ void InputMap::load_default() { InputMap::InputMap() { - ERR_FAIL_COND(singleton); + ERR_FAIL_COND_MSG(singleton, "Singleton in InputMap already exist."); singleton = this; } diff --git a/core/io/compression.cpp b/core/io/compression.cpp index a113f3b61b..b51e50150e 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -175,7 +175,9 @@ int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p } break; case MODE_ZSTD: { ZSTD_DCtx *dctx = ZSTD_createDCtx(); - if (zstd_long_distance_matching) ZSTD_DCtx_setMaxWindowSize(dctx, (size_t)1 << zstd_window_log_size); + if (zstd_long_distance_matching) { + ZSTD_DCtx_setParameter(dctx, ZSTD_d_windowLogMax, zstd_window_log_size); + } int ret = ZSTD_decompressDCtx(dctx, p_dst, p_dst_max_size, p_src, p_src_size); ZSTD_freeDCtx(dctx); return ret; @@ -189,4 +191,4 @@ int Compression::zlib_level = Z_DEFAULT_COMPRESSION; int Compression::gzip_level = Z_DEFAULT_COMPRESSION; int Compression::zstd_level = 3; bool Compression::zstd_long_distance_matching = false; -int Compression::zstd_window_log_size = 27; +int Compression::zstd_window_log_size = 27; // ZSTD_WINDOWLOG_LIMIT_DEFAULT diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index 414742deeb..5684c82d1c 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -30,7 +30,7 @@ #include "config_file.h" -#include "core/os/file_access.h" +#include "core/io/file_access_encrypted.h" #include "core/os/keyboard.h" #include "core/variant_parser.h" @@ -86,10 +86,7 @@ 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)) { - if (p_default.get_type() == Variant::NIL) { - ERR_EXPLAIN("Couldn't find the given section/key and no default was given"); - ERR_FAIL_V(p_default); - } + 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."); return p_default; } return values[p_section][p_key]; @@ -114,7 +111,7 @@ void ConfigFile::get_sections(List<String> *r_sections) const { } void ConfigFile::get_section_keys(const String &p_section, List<String> *r_keys) const { - ERR_FAIL_COND(!values.has(p_section)); + ERR_FAIL_COND_MSG(!values.has(p_section), "Cannont get keys from nonexistent section '" + p_section + "'."); for (OrderedHashMap<String, Variant>::ConstElement E = values[p_section].front(); E; E = E.next()) { r_keys->push_back(E.key()); @@ -126,6 +123,13 @@ void ConfigFile::erase_section(const String &p_section) { values.erase(p_section); } +void ConfigFile::erase_section_key(const String &p_section, const String &p_key) { + + ERR_FAIL_COND_MSG(!values.has(p_section), "Cannot erase key from nonexistent section '" + p_section + "'."); + + values[p_section].erase(p_key); +} + Error ConfigFile::save(const String &p_path) { Error err; @@ -137,6 +141,48 @@ Error ConfigFile::save(const String &p_path) { return err; } + return _internal_save(file); +} + +Error ConfigFile::save_encrypted(const String &p_path, const Vector<uint8_t> &p_key) { + + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE, &err); + + if (err) + return err; + + FileAccessEncrypted *fae = memnew(FileAccessEncrypted); + err = fae->open_and_parse(f, p_key, FileAccessEncrypted::MODE_WRITE_AES256); + if (err) { + memdelete(fae); + memdelete(f); + return err; + } + return _internal_save(fae); +} + +Error ConfigFile::save_encrypted_pass(const String &p_path, const String &p_pass) { + + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE, &err); + + if (err) + return err; + + FileAccessEncrypted *fae = memnew(FileAccessEncrypted); + err = fae->open_and_parse_password(f, p_pass, FileAccessEncrypted::MODE_WRITE_AES256); + if (err) { + memdelete(fae); + memdelete(f); + return err; + } + + return _internal_save(fae); +} + +Error ConfigFile::_internal_save(FileAccess *file) { + for (OrderedHashMap<String, OrderedHashMap<String, Variant> >::Element E = values.front(); E; E = E.next()) { if (E != values.front()) @@ -162,7 +208,49 @@ Error ConfigFile::load(const String &p_path) { FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); if (!f) - return ERR_CANT_OPEN; + return err; + + return _internal_load(p_path, f); +} + +Error ConfigFile::load_encrypted(const String &p_path, const Vector<uint8_t> &p_key) { + + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + + if (err) + return err; + + FileAccessEncrypted *fae = memnew(FileAccessEncrypted); + err = fae->open_and_parse(f, p_key, FileAccessEncrypted::MODE_READ); + if (err) { + memdelete(fae); + memdelete(f); + return err; + } + return _internal_load(p_path, fae); +} + +Error ConfigFile::load_encrypted_pass(const String &p_path, const String &p_pass) { + + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + + if (err) + return err; + + FileAccessEncrypted *fae = memnew(FileAccessEncrypted); + err = fae->open_and_parse_password(f, p_pass, FileAccessEncrypted::MODE_READ); + if (err) { + memdelete(fae); + memdelete(f); + return err; + } + + return _internal_load(p_path, fae); +} + +Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) { VariantParser::StreamFile stream; stream.f = f; @@ -182,12 +270,12 @@ Error ConfigFile::load(const String &p_path) { next_tag.fields.clear(); next_tag.name = String(); - err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, NULL, true); + Error err = VariantParser::parse_tag_assign_eof(&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); + ERR_PRINTS("ConfgFile::load - " + p_path + ":" + itos(lines) + " error: " + error_text + "."); memdelete(f); return err; } @@ -198,10 +286,6 @@ Error ConfigFile::load(const String &p_path) { section = next_tag.name; } } - - memdelete(f); - - return OK; } void ConfigFile::_bind_methods() { @@ -216,9 +300,16 @@ void ConfigFile::_bind_methods() { ClassDB::bind_method(D_METHOD("get_section_keys", "section"), &ConfigFile::_get_section_keys); ClassDB::bind_method(D_METHOD("erase_section", "section"), &ConfigFile::erase_section); + 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("save", "path"), &ConfigFile::save); + + ClassDB::bind_method(D_METHOD("load_encrypted", "path", "key"), &ConfigFile::load_encrypted); + ClassDB::bind_method(D_METHOD("load_encrypted_pass", "path", "pass"), &ConfigFile::load_encrypted_pass); + + ClassDB::bind_method(D_METHOD("save_encrypted", "path", "key"), &ConfigFile::save_encrypted); + ClassDB::bind_method(D_METHOD("save_encrypted_pass", "path", "pass"), &ConfigFile::save_encrypted_pass); } ConfigFile::ConfigFile() { diff --git a/core/io/config_file.h b/core/io/config_file.h index 36e5c0ca7d..d927779f9c 100644 --- a/core/io/config_file.h +++ b/core/io/config_file.h @@ -32,6 +32,7 @@ #define CONFIG_FILE_H #include "core/ordered_hash_map.h" +#include "core/os/file_access.h" #include "core/reference.h" class ConfigFile : public Reference { @@ -42,6 +43,8 @@ class ConfigFile : public Reference { PoolStringArray _get_sections() const; PoolStringArray _get_section_keys(const String &p_section) const; + Error _internal_load(const String &p_path, FileAccess *f); + Error _internal_save(FileAccess *file); protected: static void _bind_methods(); @@ -57,10 +60,17 @@ public: void get_section_keys(const String &p_section, List<String> *r_keys) const; void erase_section(const String &p_section); + void erase_section_key(const String &p_section, const String &p_key); Error save(const String &p_path); Error load(const String &p_path); + 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); + + Error save_encrypted(const String &p_path, const Vector<uint8_t> &p_key); + Error save_encrypted_pass(const String &p_path, const String &p_pass); + ConfigFile(); }; diff --git a/core/io/file_access_buffered.cpp b/core/io/file_access_buffered.cpp index 83ff532aa4..f72ad61da6 100644 --- a/core/io/file_access_buffered.cpp +++ b/core/io/file_access_buffered.cpp @@ -35,79 +35,75 @@ Error FileAccessBuffered::set_error(Error p_error) const { return (last_error = p_error); -}; +} void FileAccessBuffered::set_cache_size(int p_size) { cache_size = p_size; -}; +} int FileAccessBuffered::get_cache_size() { return cache_size; -}; +} int FileAccessBuffered::cache_data_left() const { if (file.offset >= file.size) { return 0; - }; + } if (cache.offset == -1 || file.offset < cache.offset || file.offset >= cache.offset + cache.buffer.size()) { return read_data_block(file.offset, cache_size); + } - } else { - - return cache.buffer.size() - (file.offset - cache.offset); - }; - - return 0; -}; + return cache.buffer.size() - (file.offset - cache.offset); +} void FileAccessBuffered::seek(size_t p_position) { file.offset = p_position; -}; +} void FileAccessBuffered::seek_end(int64_t p_position) { file.offset = file.size + p_position; -}; +} size_t FileAccessBuffered::get_position() const { return file.offset; -}; +} size_t FileAccessBuffered::get_len() const { return file.size; -}; +} bool FileAccessBuffered::eof_reached() const { return file.offset > file.size; -}; +} uint8_t FileAccessBuffered::get_8() const { - ERR_FAIL_COND_V(!file.open, 0); + ERR_FAIL_COND_V_MSG(!file.open, 0, "Can't get data, when file is not opened."); uint8_t byte = 0; if (cache_data_left() >= 1) { byte = cache.buffer[file.offset - cache.offset]; - }; + } ++file.offset; return byte; -}; +} int FileAccessBuffered::get_buffer(uint8_t *p_dest, int p_length) const { - ERR_FAIL_COND_V(!file.open, -1); + ERR_FAIL_COND_V_MSG(!file.open, -1, "Can't get buffer, when file is not opened."); if (p_length > cache_size) { @@ -124,16 +120,16 @@ int FileAccessBuffered::get_buffer(uint8_t *p_dest, int p_length) const { p_length -= size; file.offset += size; total_read += size; - }; + } int err = read_data_block(file.offset, p_length, p_dest); if (err >= 0) { total_read += err; file.offset += err; - }; + } return total_read; - }; + } int to_read = p_length; int total_read = 0; @@ -141,14 +137,12 @@ int FileAccessBuffered::get_buffer(uint8_t *p_dest, int p_length) const { int left = cache_data_left(); if (left == 0) { - if (to_read > 0) { - file.offset += to_read; - }; + file.offset += to_read; return total_read; - }; + } if (left < 0) { return left; - }; + } int r = MIN(left, to_read); //PoolVector<uint8_t>::Read read = cache.buffer.read(); @@ -158,25 +152,25 @@ int FileAccessBuffered::get_buffer(uint8_t *p_dest, int p_length) const { file.offset += r; total_read += r; to_read -= r; - }; + } return p_length; -}; +} bool FileAccessBuffered::is_open() const { return file.open; -}; +} Error FileAccessBuffered::get_error() const { return last_error; -}; +} FileAccessBuffered::FileAccessBuffered() { cache_size = DEFAULT_CACHE_SIZE; -}; +} FileAccessBuffered::~FileAccessBuffered() { } diff --git a/core/io/file_access_buffered_fa.h b/core/io/file_access_buffered_fa.h index be960fbc25..c8cee04208 100644 --- a/core/io/file_access_buffered_fa.h +++ b/core/io/file_access_buffered_fa.h @@ -40,7 +40,7 @@ class FileAccessBufferedFA : public FileAccessBuffered { int read_data_block(int p_offset, int p_size, uint8_t *p_dest = 0) const { - ERR_FAIL_COND_V(!f.is_open(), -1); + ERR_FAIL_COND_V_MSG(!f.is_open(), -1, "Can't read data block when file is not opened."); ((T *)&f)->seek(p_offset); @@ -143,6 +143,14 @@ public: return f._get_modified_time(p_file); } + virtual uint32_t _get_unix_permissions(const String &p_file) { + return f._get_unix_permissions(p_file); + } + + virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) { + return f._set_unix_permissions(p_file, p_permissions); + } + FileAccessBufferedFA(){ }; diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index b268d5c710..a52c6f79c9 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -195,7 +195,7 @@ bool FileAccessCompressed::is_open() const { void FileAccessCompressed::seek(size_t p_position) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); if (writing) { ERR_FAIL_COND(p_position > write_max); @@ -208,7 +208,8 @@ void FileAccessCompressed::seek(size_t p_position) { if (p_position == read_total) { at_end = true; } else { - + at_end = false; + read_eof = false; int block_idx = p_position / block_size; if (block_idx != read_block) { @@ -226,7 +227,7 @@ void FileAccessCompressed::seek(size_t p_position) { void FileAccessCompressed::seek_end(int64_t p_position) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); if (writing) { seek(write_max + p_position); @@ -237,7 +238,7 @@ void FileAccessCompressed::seek_end(int64_t p_position) { } size_t FileAccessCompressed::get_position() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); if (writing) { return write_pos; @@ -248,7 +249,7 @@ size_t FileAccessCompressed::get_position() const { } size_t FileAccessCompressed::get_len() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); if (writing) { return write_max; @@ -259,7 +260,7 @@ size_t FileAccessCompressed::get_len() const { bool FileAccessCompressed::eof_reached() const { - ERR_FAIL_COND_V(!f, false); + ERR_FAIL_COND_V_MSG(!f, false, "File must be opened before use."); if (writing) { return false; } else { @@ -269,8 +270,8 @@ bool FileAccessCompressed::eof_reached() const { uint8_t FileAccessCompressed::get_8() const { - ERR_FAIL_COND_V(writing, 0); - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); if (at_end) { read_eof = true; @@ -300,8 +301,8 @@ uint8_t FileAccessCompressed::get_8() const { } int FileAccessCompressed::get_buffer(uint8_t *p_dst, int p_length) const { - ERR_FAIL_COND_V(writing, 0); - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); if (at_end) { read_eof = true; @@ -341,16 +342,16 @@ Error FileAccessCompressed::get_error() const { } void FileAccessCompressed::flush() { - ERR_FAIL_COND(!f); - ERR_FAIL_COND(!writing); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); // compressed files keep data in memory till close() } void FileAccessCompressed::store_8(uint8_t p_dest) { - ERR_FAIL_COND(!f); - ERR_FAIL_COND(!writing); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); WRITE_FIT(1); write_ptr[write_pos++] = p_dest; @@ -373,6 +374,19 @@ uint64_t FileAccessCompressed::_get_modified_time(const String &p_file) { return 0; } +uint32_t FileAccessCompressed::_get_unix_permissions(const String &p_file) { + if (f) + return f->_get_unix_permissions(p_file); + return 0; +} + +Error FileAccessCompressed::_set_unix_permissions(const String &p_file, uint32_t p_permissions) { + if (f) { + return f->_set_unix_permissions(p_file, p_permissions); + } + return FAILED; +} + FileAccessCompressed::FileAccessCompressed() : cmode(Compression::MODE_ZSTD), writing(false), diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index f408b1bc29..773fed6a3a 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -91,6 +91,8 @@ public: virtual bool file_exists(const String &p_name); ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file); + virtual uint32_t _get_unix_permissions(const String &p_file); + virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions); FileAccessCompressed(); virtual ~FileAccessCompressed(); diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index 6ad68dd74d..c2e4e0f575 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -30,20 +30,18 @@ #include "file_access_encrypted.h" +#include "core/crypto/crypto_core.h" #include "core/os/copymem.h" #include "core/print_string.h" #include "core/variant.h" -#include "thirdparty/misc/aes256.h" -#include "thirdparty/misc/md5.h" - #include <stdio.h> #define COMP_MAGIC 0x43454447 Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8_t> &p_key, Mode p_mode) { - ERR_FAIL_COND_V(file != NULL, ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V_MSG(file != NULL, ERR_ALREADY_IN_USE, "Can't open file while another file from path '" + file->get_path_absolute() + "' is open."); ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER); pos = 0; @@ -83,24 +81,20 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8 uint32_t blen = p_base->get_buffer(data.ptrw(), ds); ERR_FAIL_COND_V(blen != ds, ERR_FILE_CORRUPT); - aes256_context ctx; - aes256_init(&ctx, key.ptrw()); + CryptoCore::AESContext ctx; + ctx.set_decode_key(key.ptrw(), 256); for (size_t i = 0; i < ds; i += 16) { - aes256_decrypt_ecb(&ctx, &data.write[i]); + ctx.decrypt_ecb(&data.write[i], &data.write[i]); } - aes256_done(&ctx); - data.resize(length); - MD5_CTX md5; - MD5Init(&md5); - MD5Update(&md5, (uint8_t *)data.ptr(), data.size()); - MD5Final(&md5); + unsigned char hash[16]; + ERR_FAIL_COND_V(CryptoCore::md5(data.ptr(), data.size(), hash) != OK, ERR_BUG); - ERR_FAIL_COND_V(String::md5(md5.digest) != String::md5(md5d), ERR_FILE_CORRUPT); + ERR_FAIL_COND_V_MSG(String::md5(hash) != String::md5(md5d), ERR_FILE_CORRUPT, "The MD5 sum of the decrypted file does not match the expected value. It could be that the file is corrupt, or that the provided decryption key is invalid."); file = p_base; } @@ -139,10 +133,8 @@ void FileAccessEncrypted::close() { len += 16 - (len % 16); } - MD5_CTX md5; - MD5Init(&md5); - MD5Update(&md5, (uint8_t *)data.ptr(), data.size()); - MD5Final(&md5); + unsigned char hash[16]; + ERR_FAIL_COND(CryptoCore::md5(data.ptr(), data.size(), hash) != OK); // Bug? compressed.resize(len); zeromem(compressed.ptrw(), len); @@ -150,20 +142,18 @@ void FileAccessEncrypted::close() { compressed.write[i] = data[i]; } - aes256_context ctx; - aes256_init(&ctx, key.ptrw()); + CryptoCore::AESContext ctx; + ctx.set_encode_key(key.ptrw(), 256); for (size_t i = 0; i < len; i += 16) { - aes256_encrypt_ecb(&ctx, &compressed.write[i]); + ctx.encrypt_ecb(&compressed.write[i], &compressed.write[i]); } - aes256_done(&ctx); - file->store_32(COMP_MAGIC); file->store_32(mode); - file->store_buffer(md5.digest, 16); + file->store_buffer(hash, 16); file->store_64(data.size()); file->store_buffer(compressed.ptr(), compressed.size()); @@ -186,6 +176,22 @@ bool FileAccessEncrypted::is_open() const { return file != NULL; } +String FileAccessEncrypted::get_path() const { + + if (file) + return file->get_path(); + else + return ""; +} + +String FileAccessEncrypted::get_path_absolute() const { + + if (file) + return file->get_path_absolute(); + else + return ""; +} + void FileAccessEncrypted::seek(size_t p_position) { if (p_position > (size_t)data.size()) @@ -215,7 +221,7 @@ bool FileAccessEncrypted::eof_reached() const { uint8_t FileAccessEncrypted::get_8() const { - ERR_FAIL_COND_V(writing, 0); + ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); if (pos >= data.size()) { eofed = true; return 0; @@ -227,7 +233,7 @@ uint8_t FileAccessEncrypted::get_8() const { } int FileAccessEncrypted::get_buffer(uint8_t *p_dst, int p_length) const { - ERR_FAIL_COND_V(writing, 0); + ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); int to_copy = MIN(p_length, data.size() - pos); for (int i = 0; i < to_copy; i++) { @@ -249,7 +255,7 @@ Error FileAccessEncrypted::get_error() const { void FileAccessEncrypted::store_buffer(const uint8_t *p_src, int p_length) { - ERR_FAIL_COND(!writing); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); if (pos < data.size()) { @@ -269,14 +275,14 @@ void FileAccessEncrypted::store_buffer(const uint8_t *p_src, int p_length) { } void FileAccessEncrypted::flush() { - ERR_FAIL_COND(!writing); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); // encrypted files keep data in memory till close() } void FileAccessEncrypted::store_8(uint8_t p_dest) { - ERR_FAIL_COND(!writing); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); if (pos < data.size()) { data.write[pos] = p_dest; @@ -301,6 +307,16 @@ uint64_t FileAccessEncrypted::_get_modified_time(const String &p_file) { return 0; } +uint32_t FileAccessEncrypted::_get_unix_permissions(const String &p_file) { + + return 0; +} + +Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, uint32_t p_permissions) { + ERR_PRINT("Setting UNIX permissions on encrypted files is not implemented yet."); + return ERR_UNAVAILABLE; +} + FileAccessEncrypted::FileAccessEncrypted() { file = NULL; diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index e77d62a9f4..c3be0f7de8 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -60,6 +60,9 @@ public: virtual void close(); ///< close a file virtual bool is_open() const; ///< true when file is open + virtual String get_path() const; /// returns the path for the current open file + virtual String get_path_absolute() const; /// returns the absolute path for the current open file + virtual void seek(size_t p_position); ///< seek to a given position virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file virtual size_t get_position() const; ///< get position in the file @@ -79,6 +82,8 @@ public: virtual bool file_exists(const String &p_name); ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file); + virtual uint32_t _get_unix_permissions(const String &p_file); + virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions); FileAccessEncrypted(); ~FileAccessEncrypted(); diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index fbcf5b8021..c0acd36751 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -90,7 +90,7 @@ Error FileAccessMemory::_open(const String &p_path, int p_mode_flags) { //name = DirAccess::normalize_path(name); Map<String, Vector<uint8_t> >::Element *E = files->find(name); - ERR_FAIL_COND_V(!E, ERR_FILE_NOT_FOUND); + ERR_FAIL_COND_V_MSG(!E, ERR_FILE_NOT_FOUND, "Can't find file '" + p_path + "'."); data = E->get().ptrw(); length = E->get().size(); diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index 73952133c1..4db7811aaa 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -70,6 +70,8 @@ public: virtual bool file_exists(const String &p_name); ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) { return 0; } + virtual uint32_t _get_unix_permissions(const String &p_file) { return 0; } + virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) { return FAILED; } FileAccessMemory(); }; diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index 501a21a50d..e653a924ba 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -118,7 +118,10 @@ void FileAccessNetworkClient::_thread_func() { FileAccessNetwork *fa = NULL; if (response != FileAccessNetwork::RESPONSE_DATA) { - ERR_FAIL_COND(!accesses.has(id)); + if (!accesses.has(id)) { + unlock_mutex(); + ERR_FAIL_COND(!accesses.has(id)); + } } if (accesses.has(id)) @@ -192,7 +195,7 @@ Error FileAccessNetworkClient::connect(const String &p_host, int p_port, const S DEBUG_PRINT("IP: " + String(ip) + " port " + itos(p_port)); Error err = client->connect_to_host(ip, p_port); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot connect to host with IP: " + String(ip) + " and port: " + itos(p_port)); while (client->get_status() == StreamPeerTCP::STATUS_CONNECTING) { //DEBUG_PRINT("trying to connect...."); OS::get_singleton()->delay_usec(1000); @@ -336,7 +339,7 @@ bool FileAccessNetwork::is_open() const { void FileAccessNetwork::seek(size_t p_position) { - ERR_FAIL_COND(!opened); + ERR_FAIL_COND_MSG(!opened, "File must be opened before use."); eof_flag = p_position > total_size; if (p_position >= total_size) { @@ -352,18 +355,18 @@ void FileAccessNetwork::seek_end(int64_t p_position) { } size_t FileAccessNetwork::get_position() const { - ERR_FAIL_COND_V(!opened, 0); + ERR_FAIL_COND_V_MSG(!opened, 0, "File must be opened before use."); return pos; } size_t FileAccessNetwork::get_len() const { - ERR_FAIL_COND_V(!opened, 0); + ERR_FAIL_COND_V_MSG(!opened, 0, "File must be opened before use."); return total_size; } bool FileAccessNetwork::eof_reached() const { - ERR_FAIL_COND_V(!opened, false); + ERR_FAIL_COND_V_MSG(!opened, false, "File must be opened before use."); return eof_flag; } @@ -432,7 +435,6 @@ int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const { _queue_page(page + j); } - buff = pages.write[page].buffer.ptrw(); //queue pages buffer_mutex->unlock(); } @@ -497,6 +499,16 @@ uint64_t FileAccessNetwork::_get_modified_time(const String &p_file) { return exists_modtime; } +uint32_t FileAccessNetwork::_get_unix_permissions(const String &p_file) { + ERR_PRINT("Getting UNIX permissions from network drives is not implemented yet"); + return 0; +} + +Error FileAccessNetwork::_set_unix_permissions(const String &p_file, uint32_t p_permissions) { + ERR_PRINT("Setting UNIX permissions on network drives is not implemented yet"); + return ERR_UNAVAILABLE; +} + void FileAccessNetwork::configure() { GLOBAL_DEF("network/remote_fs/page_size", 65536); diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h index 5bbf7588c7..073b75a37b 100644 --- a/core/io/file_access_network.h +++ b/core/io/file_access_network.h @@ -159,6 +159,8 @@ public: virtual bool file_exists(const String &p_path); ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file); + virtual uint32_t _get_unix_permissions(const String &p_file); + virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions); static void configure(); diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index d38d09c6bb..34d3eb5344 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -36,11 +36,11 @@ #define PACK_VERSION 1 -Error PackedData::add_pack(const String &p_path) { +Error PackedData::add_pack(const String &p_path, bool p_replace_files) { for (int i = 0; i < sources.size(); i++) { - if (sources[i]->try_open_pack(p_path)) { + if (sources[i]->try_open_pack(p_path, p_replace_files)) { return OK; }; @@ -49,7 +49,7 @@ Error PackedData::add_pack(const String &p_path) { return ERR_FILE_UNRECOGNIZED; }; -void PackedData::add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src) { +void PackedData::add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files) { PathMD5 pmd5(path.md5_buffer()); //printf("adding path %ls, %lli, %lli\n", path.c_str(), pmd5.a, pmd5.b); @@ -64,7 +64,8 @@ void PackedData::add_path(const String &pkg_path, const String &path, uint64_t o pf.md5[i] = p_md5[i]; pf.src = p_src; - files[pmd5] = pf; + if (!exists || p_replace_files) + files[pmd5] = pf; if (!exists) { //search for dir @@ -90,7 +91,7 @@ void PackedData::add_path(const String &pkg_path, const String &path, uint64_t o } } String filename = path.get_file(); - // Don't add as a file if the path points to a directoryy + // Don't add as a file if the path points to a directory if (!filename.empty()) { cd->files.insert(filename); } @@ -133,7 +134,7 @@ PackedData::~PackedData() { ////////////////////////////////////////////////////////////////// -bool PackedSourcePCK::try_open_pack(const String &p_path) { +bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files) { FileAccess *f = FileAccess::open(p_path, FileAccess::READ); if (!f) @@ -144,12 +145,13 @@ bool PackedSourcePCK::try_open_pack(const String &p_path) { uint32_t magic = f->get_32(); if (magic != 0x43504447) { - //maybe at he end.... self contained exe + //maybe at the end.... self contained exe f->seek_end(); f->seek(f->get_position() - 4); magic = f->get_32(); if (magic != 0x43504447) { + f->close(); memdelete(f); return false; } @@ -161,6 +163,7 @@ bool PackedSourcePCK::try_open_pack(const String &p_path) { magic = f->get_32(); if (magic != 0x43504447) { + f->close(); memdelete(f); return false; } @@ -171,10 +174,16 @@ bool PackedSourcePCK::try_open_pack(const String &p_path) { uint32_t ver_minor = f->get_32(); f->get_32(); // ver_rev - ERR_EXPLAIN("Pack version unsupported: " + itos(version)); - ERR_FAIL_COND_V(version != PACK_VERSION, false); - ERR_EXPLAIN("Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor)); - ERR_FAIL_COND_V(ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), false); + if (version != PACK_VERSION) { + f->close(); + memdelete(f); + ERR_FAIL_V_MSG(false, "Pack version unsupported: " + itos(version) + "."); + } + if (ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR)) { + f->close(); + memdelete(f); + ERR_FAIL_V_MSG(false, "Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor) + "."); + } for (int i = 0; i < 16; i++) { //reserved @@ -198,9 +207,11 @@ bool PackedSourcePCK::try_open_pack(const String &p_path) { uint64_t size = f->get_64(); uint8_t md5[16]; f->get_buffer(md5, 16); - PackedData::get_singleton()->add_path(p_path, path, ofs, size, md5, this); + PackedData::get_singleton()->add_path(p_path, path, ofs, size, md5, this, p_replace_files); }; + f->close(); + memdelete(f); return true; }; @@ -322,10 +333,9 @@ bool FileAccessPack::file_exists(const String &p_name) { FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) : pf(p_file), f(FileAccess::open(pf.pack, FileAccess::READ)) { - if (!f) { - ERR_EXPLAIN("Can't open pack-referenced file: " + String(pf.pack)); - ERR_FAIL_COND(!f); - } + + ERR_FAIL_COND_MSG(!f, "Can't open pack-referenced file '" + String(pf.pack) + "'."); + f->seek(pf.offset); pos = 0; eof = false; @@ -463,11 +473,15 @@ String DirAccessPack::get_current_dir() { bool DirAccessPack::file_exists(String p_file) { + p_file = fix_path(p_file); + return current->files.has(p_file); } bool DirAccessPack::dir_exists(String p_dir) { + p_dir = fix_path(p_dir); + return current->subdirs.has(p_dir); } diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index a90672ce26..8c34069f3a 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -102,13 +102,13 @@ private: public: void add_pack_source(PackSource *p_source); - void add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src); // for PackSource + void add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files); // for PackSource void set_disabled(bool p_disabled) { disabled = p_disabled; } _FORCE_INLINE_ bool is_disabled() const { return disabled; } static PackedData *get_singleton() { return singleton; } - Error add_pack(const String &p_path); + Error add_pack(const String &p_path, bool p_replace_files); _FORCE_INLINE_ FileAccess *try_open_path(const String &p_path); _FORCE_INLINE_ bool has_path(const String &p_path); @@ -120,7 +120,7 @@ public: class PackSource { public: - virtual bool try_open_pack(const String &p_path) = 0; + virtual bool try_open_pack(const String &p_path, bool p_replace_files) = 0; virtual FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file) = 0; virtual ~PackSource() {} }; @@ -128,7 +128,7 @@ public: class PackedSourcePCK : public PackSource { public: - virtual bool try_open_pack(const String &p_path); + virtual bool try_open_pack(const String &p_path, bool p_replace_files); virtual FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file); }; @@ -142,6 +142,8 @@ class FileAccessPack : public FileAccess { FileAccess *f; virtual Error _open(const String &p_path, int p_mode_flags); virtual uint64_t _get_modified_time(const String &p_file) { return 0; } + virtual uint32_t _get_unix_permissions(const String &p_file) { return 0; } + virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) { return FAILED; } public: virtual void close(); diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index be28c9a877..3187f3bab6 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -117,7 +117,7 @@ static void godot_free(voidpf opaque, voidpf address) { void ZipArchive::close_handle(unzFile p_file) const { - ERR_FAIL_COND(!p_file); + ERR_FAIL_COND_MSG(!p_file, "Cannot close a file if none is open."); FileAccess *f = (FileAccess *)unzGetOpaque(p_file); unzCloseCurrentFile(p_file); unzClose(p_file); @@ -126,11 +126,11 @@ void ZipArchive::close_handle(unzFile p_file) const { unzFile ZipArchive::get_file_handle(String p_file) const { - ERR_FAIL_COND_V(!file_exists(p_file), NULL); + ERR_FAIL_COND_V_MSG(!file_exists(p_file), NULL, "File '" + p_file + " doesn't exist."); File file = files[p_file]; FileAccess *f = FileAccess::open(packages[file.package].filename, FileAccess::READ); - ERR_FAIL_COND_V(!f, NULL); + ERR_FAIL_COND_V_MSG(!f, NULL, "Cannot open file '" + packages[file.package].filename + "'."); zlib_filefunc_def io; zeromem(&io, sizeof(io)); @@ -160,7 +160,7 @@ unzFile ZipArchive::get_file_handle(String p_file) const { return pkg; } -bool ZipArchive::try_open_pack(const String &p_path) { +bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files) { //printf("opening zip pack %ls, %i, %i\n", p_name.c_str(), p_name.extension().nocasecmp_to("zip"), p_name.extension().nocasecmp_to("pcz")); if (p_path.get_extension().nocasecmp_to("zip") != 0 && p_path.get_extension().nocasecmp_to("pcz") != 0) @@ -194,7 +194,7 @@ bool ZipArchive::try_open_pack(const String &p_path) { packages.push_back(pkg); int pkg_num = packages.size() - 1; - for (unsigned int i = 0; i < gi.number_entry; i++) { + for (uint64_t i = 0; i < gi.number_entry; i++) { char filename_inzip[256]; @@ -210,7 +210,7 @@ bool ZipArchive::try_open_pack(const String &p_path) { files[fname] = f; uint8_t md5[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - PackedData::get_singleton()->add_path(p_path, fname, 1, 0, md5, this); + PackedData::get_singleton()->add_path(p_path, fname, 1, 0, md5, this, p_replace_files); //printf("packed data add path %ls, %ls\n", p_name.c_str(), fname.c_str()); if ((i + 1) < gi.number_entry) { diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index fc8f85c07b..cdd50f9eb3 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -74,7 +74,7 @@ public: bool file_exists(String p_name) const; - virtual bool try_open_pack(const String &p_path); + virtual bool try_open_pack(const String &p_path, bool p_replace_files); FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file); static ZipArchive *get_singleton(); @@ -112,6 +112,8 @@ public: virtual bool file_exists(const String &p_name); ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) { return 0; } // todo + virtual uint32_t _get_unix_permissions(const String &p_file) { return 0; } + virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) { return FAILED; } FileAccessZip(const String &p_path, const PackedData::PackedFile &p_file); ~FileAccessZip(); diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index e5c6d2a4f2..bfa272e859 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -98,6 +98,8 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, void HTTPClient::set_connection(const Ref<StreamPeer> &p_connection) { + ERR_FAIL_COND_MSG(p_connection.is_null(), "Connection is not a reference to a valid StreamPeer object."); + close(); connection = p_connection; status = STATUS_CONNECTED; @@ -171,6 +173,7 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector } status = STATUS_REQUESTING; + head_request = p_method == METHOD_HEAD; return OK; } @@ -226,6 +229,7 @@ Error HTTPClient::request(Method p_method, const String &p_url, const Vector<Str } status = STATUS_REQUESTING; + head_request = p_method == METHOD_HEAD; return OK; } @@ -267,6 +271,7 @@ void HTTPClient::close() { connection.unref(); status = STATUS_DISCONNECTED; + head_request = false; if (resolving != IP::RESOLVER_INVALID_ID) { IP::get_singleton()->erase_resolve_item(resolving); @@ -346,6 +351,12 @@ Error HTTPClient::poll() { } else { // We are already handshaking, which means we can use your already active SSL connection ssl = static_cast<Ref<StreamPeerSSL> >(connection); + if (ssl.is_null()) { + close(); + status = STATUS_SSL_HANDSHAKE_ERROR; + return ERR_CANT_CONNECT; + } + ssl->poll(); // Try to finish the handshake } @@ -429,7 +440,7 @@ Error HTTPClient::poll() { response_num = RESPONSE_OK; // Per the HTTP 1.1 spec, keep-alive is the default. - // Not following that specification breaks standard implemetations. + // Not following that specification breaks standard implementations. // Broken web servers should be fixed. bool keep_alive = true; @@ -462,6 +473,12 @@ Error HTTPClient::poll() { } } + // This is a HEAD request, we wont receive anything. + if (head_request) { + body_size = 0; + body_left = 0; + } + if (body_size != -1 || chunked) { status = STATUS_BODY; @@ -476,8 +493,6 @@ Error HTTPClient::poll() { return OK; } } - // Wait for response - return OK; } break; case STATUS_DISCONNECTED: { return ERR_UNCONFIGURED; @@ -709,11 +724,16 @@ void HTTPClient::set_read_chunk_size(int p_size) { read_chunk_size = p_size; } +int HTTPClient::get_read_chunk_size() const { + return read_chunk_size; +} + HTTPClient::HTTPClient() { tcp_connection.instance(); resolving = IP::RESOLVER_INVALID_ID; status = STATUS_DISCONNECTED; + head_request = false; conn_port = -1; body_size = -1; chunked = false; @@ -769,7 +789,7 @@ Dictionary HTTPClient::_get_response_headers_as_dictionary() { get_response_headers(&rh); Dictionary ret; for (const List<String>::Element *E = rh.front(); E; E = E->next()) { - String s = E->get(); + const String &s = E->get(); int sp = s.find(":"); if (sp == -1) continue; @@ -812,6 +832,7 @@ void HTTPClient::_bind_methods() { ClassDB::bind_method(D_METHOD("get_response_body_length"), &HTTPClient::get_response_body_length); ClassDB::bind_method(D_METHOD("read_response_body_chunk"), &HTTPClient::read_response_body_chunk); ClassDB::bind_method(D_METHOD("set_read_chunk_size", "bytes"), &HTTPClient::set_read_chunk_size); + ClassDB::bind_method(D_METHOD("get_read_chunk_size"), &HTTPClient::get_read_chunk_size); ClassDB::bind_method(D_METHOD("set_blocking_mode", "enabled"), &HTTPClient::set_blocking_mode); ClassDB::bind_method(D_METHOD("is_blocking_mode_enabled"), &HTTPClient::is_blocking_mode_enabled); @@ -823,6 +844,7 @@ void HTTPClient::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_mode_enabled"), "set_blocking_mode", "is_blocking_mode_enabled"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "connection", PROPERTY_HINT_RESOURCE_TYPE, "StreamPeer", 0), "set_connection", "get_connection"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "read_chunk_size", PROPERTY_HINT_RANGE, "256,16777216"), "set_read_chunk_size", "get_read_chunk_size"); BIND_ENUM_CONSTANT(METHOD_GET); BIND_ENUM_CONSTANT(METHOD_HEAD); diff --git a/core/io/http_client.h b/core/io/http_client.h index 85ee1959a2..27c6711bcf 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -166,6 +166,7 @@ private: bool ssl_verify_host; bool blocking; bool handshaking; + bool head_request; Vector<uint8_t> response_str; @@ -220,6 +221,7 @@ public: bool is_blocking_mode_enabled() const; void set_read_chunk_size(int p_size); + int get_read_chunk_size() const; Error poll(); diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index a5a0738140..095c2abb54 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -46,14 +46,14 @@ bool ImageFormatLoader::recognize(const String &p_extension) const { } Error ImageLoader::load_image(String p_file, Ref<Image> p_image, FileAccess *p_custom, bool p_force_linear, float p_scale) { - ERR_FAIL_COND_V(p_image.is_null(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(p_image.is_null(), ERR_INVALID_PARAMETER, "It's not a reference to a valid Image object."); FileAccess *f = p_custom; if (!f) { Error err; f = FileAccess::open(p_file, FileAccess::READ, &err); if (!f) { - ERR_PRINTS("Error opening file: " + p_file); + ERR_PRINTS("Error opening file '" + p_file + "'."); return err; } } @@ -65,6 +65,9 @@ Error ImageLoader::load_image(String p_file, Ref<Image> p_image, FileAccess *p_c if (!loader[i]->recognize(extension)) 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); + } if (err != ERR_FILE_UNRECOGNIZED) { diff --git a/core/io/image_loader.h b/core/io/image_loader.h index 95c562b7a9..af6b0551a3 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -37,24 +37,8 @@ #include "core/os/file_access.h" #include "core/ustring.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - -/** - * @class ImageScanLineLoader - * @author Juan Linietsky <reduzio@gmail.com> - * - - */ class ImageLoader; -/** - * @class ImageLoader - * Base Class and singleton for loading images from disk - * Can load images in one go, or by scanline - */ - class ImageFormatLoader { friend class ImageLoader; friend class ResourceFormatLoaderImage; @@ -88,7 +72,6 @@ public: }; class ResourceFormatLoaderImage : public ResourceFormatLoader { - GDCLASS(ResourceFormatLoaderImage, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 420e48f839..f1b6570799 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -234,6 +234,41 @@ Array IP::_get_local_addresses() const { return addresses; } +Array IP::_get_local_interfaces() const { + + Array results; + Map<String, Interface_Info> interfaces; + get_local_interfaces(&interfaces); + for (Map<String, Interface_Info>::Element *E = interfaces.front(); E; E = E->next()) { + Interface_Info &c = E->get(); + Dictionary rc; + rc["name"] = c.name; + rc["friendly"] = c.name_friendly; + rc["index"] = c.index; + + Array ips; + for (const List<IP_Address>::Element *F = c.ip_addresses.front(); F; F = F->next()) { + ips.push_front(F->get()); + } + rc["addresses"] = ips; + + results.push_front(rc); + } + + return results; +} + +void IP::get_local_addresses(List<IP_Address> *r_addresses) const { + + Map<String, Interface_Info> interfaces; + get_local_interfaces(&interfaces); + for (Map<String, Interface_Info>::Element *E = interfaces.front(); E; E = E->next()) { + for (const List<IP_Address>::Element *F = E->get().ip_addresses.front(); F; F = F->next()) { + r_addresses->push_front(F->get()); + } + } +} + void IP::_bind_methods() { ClassDB::bind_method(D_METHOD("resolve_hostname", "host", "ip_type"), &IP::resolve_hostname, DEFVAL(IP::TYPE_ANY)); @@ -242,6 +277,7 @@ void IP::_bind_methods() { ClassDB::bind_method(D_METHOD("get_resolve_item_address", "id"), &IP::get_resolve_item_address); ClassDB::bind_method(D_METHOD("erase_resolve_item", "id"), &IP::erase_resolve_item); ClassDB::bind_method(D_METHOD("get_local_addresses"), &IP::_get_local_addresses); + ClassDB::bind_method(D_METHOD("get_local_interfaces"), &IP::_get_local_interfaces); ClassDB::bind_method(D_METHOD("clear_cache", "hostname"), &IP::clear_cache, DEFVAL("")); BIND_ENUM_CONSTANT(RESOLVER_STATUS_NONE); @@ -269,7 +305,7 @@ IP *(*IP::_create)() = NULL; IP *IP::create() { - ERR_FAIL_COND_V(singleton, NULL); + ERR_FAIL_COND_V_MSG(singleton, NULL, "IP singleton already exist."); ERR_FAIL_COND_V(!_create, NULL); return _create(); } diff --git a/core/io/ip.h b/core/io/ip.h index ead71ebb54..59b18ef986 100644 --- a/core/io/ip.h +++ b/core/io/ip.h @@ -73,16 +73,25 @@ protected: virtual IP_Address _resolve_hostname(const String &p_hostname, Type p_type = TYPE_ANY) = 0; Array _get_local_addresses() const; + Array _get_local_interfaces() const; static IP *(*_create)(); public: + struct Interface_Info { + String name; + String name_friendly; + String index; + List<IP_Address> ip_addresses; + }; + IP_Address resolve_hostname(const String &p_hostname, Type p_type = TYPE_ANY); // async resolver hostname ResolverID resolve_hostname_queue_item(const String &p_hostname, Type p_type = TYPE_ANY); ResolverStatus get_resolve_item_status(ResolverID p_id) const; IP_Address get_resolve_item_address(ResolverID p_id) const; - virtual void get_local_addresses(List<IP_Address> *r_addresses) const = 0; + virtual void get_local_addresses(List<IP_Address> *r_addresses) const; + virtual void get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const = 0; void erase_resolve_item(ResolverID p_id); void clear_cache(const String &p_hostname = ""); diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp index 763a5fbb9a..0980027f42 100644 --- a/core/io/ip_address.cpp +++ b/core/io/ip_address.cpp @@ -40,6 +40,9 @@ IP_Address::operator Variant() const { IP_Address::operator String() const { + if (wildcard) + return "*"; + if (!valid) return ""; @@ -78,8 +81,7 @@ static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) { } else if (c == ':') { break; } else { - ERR_EXPLAIN("Invalid character in ipv6 address: " + p_string); - ERR_FAIL(); + ERR_FAIL_MSG("Invalid character in IPv6 address: " + p_string + "."); }; ret = ret << 4; ret += n; @@ -123,9 +125,7 @@ void IP_Address::_parse_ipv6(const String &p_string) { ++parts_count; }; } else { - - ERR_EXPLAIN("Invalid character in IPv6 address: " + p_string); - ERR_FAIL(); + ERR_FAIL_MSG("Invalid character in IPv6 address: " + p_string + "."); }; }; @@ -163,10 +163,7 @@ void IP_Address::_parse_ipv4(const String &p_string, int p_start, uint8_t *p_ret }; int slices = ip.get_slice_count("."); - if (slices != 4) { - ERR_EXPLAIN("Invalid IP Address String: " + ip); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(slices != 4, "Invalid IP address string: " + ip + "."); for (int i = 0; i < 4; i++) { p_ret[i] = ip.get_slicec('.', i).to_int(); } @@ -184,7 +181,7 @@ bool IP_Address::is_ipv4() const { } const uint8_t *IP_Address::get_ipv4() const { - ERR_FAIL_COND_V(!is_ipv4(), &(field8[12])); // Not the correct IPv4 (it's an IPv6), but we don't want to return a null pointer risking an engine crash. + ERR_FAIL_COND_V_MSG(!is_ipv4(), &(field8[12]), "IPv4 requested, but current IP is IPv6."); // Not the correct IPv4 (it's an IPv6), but we don't want to return a null pointer risking an engine crash. return &(field8[12]); } @@ -226,7 +223,7 @@ IP_Address::IP_Address(const String &p_string) { valid = true; } else { - ERR_PRINT("Invalid IP address"); + ERR_PRINT("Invalid IP address."); } } diff --git a/core/io/json.cpp b/core/io/json.cpp index c211ca2ed4..4e729cb355 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -347,8 +347,6 @@ Error JSON::_parse_value(Variant &value, Token &token, const CharType *p_str, in r_err_str = "Expected value, got " + String(tk_name[token.type]) + "."; return ERR_PARSE_ERROR; } - - return ERR_PARSE_ERROR; } Error JSON::_parse_array(Array &array, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str) { diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 363460311c..2ae542bca7 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -37,13 +37,11 @@ #include <limits.h> #include <stdio.h> -#define _S(a) ((int32_t)a) -#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(_S(b) < 0 || _S(a) < 0 || _S(a) > INT_MAX - _S(b), err) -#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(_S(a) < 0 || _S(b) <= 0 || _S(a) > INT_MAX / _S(b), err) - void EncodedObjectAsID::_bind_methods() { ClassDB::bind_method(D_METHOD("set_object_id", "id"), &EncodedObjectAsID::set_object_id); ClassDB::bind_method(D_METHOD("get_object_id"), &EncodedObjectAsID::get_object_id); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "object_id"), "set_object_id", "get_object_id"); } void EncodedObjectAsID::set_object_id(ObjectID p_id) { @@ -59,6 +57,10 @@ EncodedObjectAsID::EncodedObjectAsID() : id(0) { } +#define _S(a) ((int32_t)a) +#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(_S(b) < 0 || _S(a) < 0 || _S(a) > INT_MAX - _S(b), err) +#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(_S(a) < 0 || _S(b) <= 0 || _S(a) > INT_MAX / _S(b), err) + #define ENCODE_MASK 0xFF #define ENCODE_FLAG_64 1 << 16 #define ENCODE_FLAG_OBJECT_AS_ID 1 << 16 @@ -103,10 +105,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int const uint8_t *buf = p_buffer; int len = p_len; - if (len < 4) { - - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - } + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); uint32_t type = decode_uint32(buf); @@ -378,11 +377,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } } break; - /*case Variant::RESOURCE: { - - ERR_EXPLAIN("Can't marshallize resources"); - ERR_FAIL_V(ERR_INVALID_DATA); //no, i'm sorry, no go - } break;*/ case Variant::_RID: { r_variant = RID(); @@ -484,7 +478,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int int used; Error err = decode_variant(key, buf, len, &used, p_allow_objects); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to decode Variant."); buf += used; len -= used; @@ -493,7 +487,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } err = decode_variant(value, buf, len, &used, p_allow_objects); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to decode Variant."); buf += used; len -= used; @@ -528,7 +522,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int int used = 0; Variant v; Error err = decode_variant(v, buf, len, &used, p_allow_objects); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to decode Variant."); buf += used; len -= used; varr.push_back(v); @@ -559,8 +553,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int w[i] = buf[i]; } - - w = PoolVector<uint8_t>::Write(); } r_variant = data; @@ -591,8 +583,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int w[i] = decode_uint32(&buf[i * 4]); } - - w = PoolVector<int>::Write(); } r_variant = Variant(data); if (r_len) { @@ -619,8 +609,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int w[i] = decode_float(&buf[i * 4]); } - - w = PoolVector<float>::Write(); } r_variant = data; @@ -684,8 +672,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (r_len) (*r_len) += adv; - len -= adv; - buf += adv; } r_variant = varray; @@ -722,8 +708,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (r_len) (*r_len) += adv; - len -= adv; - buf += adv; } r_variant = varray; @@ -761,14 +745,14 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (r_len) (*r_len) += adv; - len -= adv; - buf += adv; } r_variant = carray; } break; - default: { ERR_FAIL_V(ERR_BUG); } + default: { + ERR_FAIL_V(ERR_BUG); + } } return OK; @@ -823,7 +807,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo flags |= ENCODE_FLAG_OBJECT_AS_ID; } } break; - default: {} // nothing to do at this stage + default: { + } // nothing to do at this stage } if (buf) { @@ -1076,11 +1061,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4 * 4; } break; - /*case Variant::RESOURCE: { - - ERR_EXPLAIN("Can't marshallize resources"); - ERR_FAIL_V(ERR_INVALID_DATA); //no, i'm sorry, no go - } break;*/ case Variant::_RID: { } break; @@ -1092,7 +1072,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo if (!obj) { if (buf) { encode_uint32(0, buf); - buf += 4; } r_len += 4; @@ -1228,11 +1207,15 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo buf += 4; PoolVector<uint8_t>::Read r = data.read(); copymem(buf, &r[0], datalen * datasize); + buf += datalen * datasize; } r_len += 4 + datalen * datasize; - while (r_len % 4) + while (r_len % 4) { r_len++; + if (buf) + *(buf++) = 0; + } } break; case Variant::POOL_INT_ARRAY: { @@ -1385,7 +1368,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4 * 4 * len; } break; - default: { ERR_FAIL_V(ERR_BUG); } + default: { + ERR_FAIL_V(ERR_BUG); + } } return OK; diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 86382939f1..1426dbbd4d 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -33,6 +33,10 @@ #include "core/io/marshalls.h" #include "scene/main/node.h" +#ifdef DEBUG_ENABLED +#include "core/os/os.h" +#endif + _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) { switch (mode) { @@ -146,8 +150,7 @@ void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_pee network_peer = p_peer; - ERR_EXPLAIN("Supplied NetworkedNetworkPeer must be connecting or connected."); - ERR_FAIL_COND(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED); + ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED, "Supplied NetworkedNetworkPeer must be connecting or connected."); if (network_peer.is_valid()) { network_peer->connect("peer_connected", this, "_add_peer"); @@ -164,10 +167,16 @@ Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const { void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_EXPLAIN("Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it"); - ERR_FAIL_COND(root_node == NULL); - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_packet_len < 1); + ERR_FAIL_COND_MSG(root_node == NULL, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it."); + ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small."); + +#ifdef DEBUG_ENABLED + if (profiling) { + bandwidth_incoming_data.write[bandwidth_incoming_pointer].timestamp = OS::get_singleton()->get_ticks_msec(); + bandwidth_incoming_data.write[bandwidth_incoming_pointer].packet_size = p_packet_len; + bandwidth_incoming_pointer = (bandwidth_incoming_pointer + 1) % bandwidth_incoming_data.size(); + } +#endif uint8_t packet_type = p_packet[0]; @@ -186,13 +195,11 @@ 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_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_packet_len < 6); + 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); - ERR_EXPLAIN("Invalid packet received. Requested node was not found."); - ERR_FAIL_COND(node == NULL); + ERR_FAIL_COND_MSG(node == NULL, "Invalid packet received. Requested node was not found."); // Detect cstring end. int len_end = 5; @@ -202,8 +209,7 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ } } - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(len_end >= p_packet_len); + ERR_FAIL_COND_MSG(len_end >= p_packet_len, "Invalid packet received. Size too small."); StringName name = String::utf8((const char *)&p_packet[5]); @@ -235,8 +241,7 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int int ofs = target & 0x7FFFFFFF; - ERR_EXPLAIN("Invalid packet received. Size smaller than declared."); - ERR_FAIL_COND_V(ofs >= p_packet_len, NULL); + ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, NULL, "Invalid packet received. Size smaller than declared."); String paths; paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs); @@ -246,33 +251,30 @@ 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_PRINTS("Failed to get path from RPC: " + String(np) + "."); } else { // Use cached path. int id = target; Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from); - ERR_EXPLAIN("Invalid packet received. Requests invalid peer cache."); - ERR_FAIL_COND_V(!E, NULL); + ERR_FAIL_COND_V_MSG(!E, NULL, "Invalid packet received. Requests invalid peer cache."); Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id); - ERR_EXPLAIN("Invalid packet received. Unabled to find requested cached node."); - ERR_FAIL_COND_V(!F, NULL); + ERR_FAIL_COND_V_MSG(!F, NULL, "Invalid packet received. Unabled to find requested cached node."); PathGetCache::NodeInfo *ni = &F->get(); // Do proper caching later. node = root_node->get_node(ni->path); if (!node) - ERR_PRINTS("Failed to get cached path from RPC: " + String(ni->path)); + ERR_PRINTS("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) { - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_offset >= p_packet_len); + 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; @@ -283,8 +285,8 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_name); } - ERR_EXPLAIN("RPC '" + String(p_name) + "' is not allowed from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + "."); - ERR_FAIL_COND(!_can_call_mode(p_node, rpc_mode, p_from)); + 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()) + "."); int argc = p_packet[p_offset]; Vector<Variant> args; @@ -294,15 +296,21 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ p_offset++; +#ifdef DEBUG_ENABLED + if (profiling) { + ObjectID id = p_node->get_instance_id(); + _init_node_profile(id); + profiler_frame_data[id].incoming_rpc += 1; + } +#endif + for (int i = 0; i < argc; i++) { - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_offset >= p_packet_len); + 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()); - ERR_EXPLAIN("Invalid packet received. Unable to decode RPC argument."); - ERR_FAIL_COND(err != OK); + ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument."); argp.write[i] = &args[i]; p_offset += vlen; @@ -320,8 +328,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ 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) { - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_offset >= p_packet_len); + 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; @@ -332,28 +339,34 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p rset_mode = p_node->get_script_instance()->get_rset_mode(p_name); } - ERR_EXPLAIN("RSET '" + String(p_name) + "' is not allowed from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + "."); - ERR_FAIL_COND(!_can_call_mode(p_node, rset_mode, p_from)); + 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()) + "."); + +#ifdef DEBUG_ENABLED + if (profiling) { + ObjectID id = p_node->get_instance_id(); + _init_node_profile(id); + profiler_frame_data[id].incoming_rset += 1; + } +#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()); - ERR_EXPLAIN("Invalid packet received. Unable to decode RSET value."); - ERR_FAIL_COND(err != OK); + ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RSET value."); bool valid; p_node->set(p_name, value, &valid); if (!valid) { - String error = "Error setting remote property '" + String(p_name) + "', not found in object of type " + p_node->get_class(); + String error = "Error setting remote property '" + String(p_name) + "', not found in object of type " + p_node->get_class() + "."; ERR_PRINTS(error); } } void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_packet_len < 5); + ERR_FAIL_COND_MSG(p_packet_len < 5, "Invalid packet received. Size too small."); int id = decode_uint32(&p_packet[1]); String paths; @@ -388,8 +401,7 @@ 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_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_packet_len < 2); + ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small."); String paths; paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1); @@ -397,12 +409,10 @@ void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, NodePath path = paths; PathSentCache *psc = path_send_cache.getptr(path); - ERR_EXPLAIN("Invalid packet received. Tries to confirm a path which was not found in cache."); - ERR_FAIL_COND(!psc); + ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache."); Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from); - ERR_EXPLAIN("Invalid packet received. Source peer was not found in cache for the given path."); - ERR_FAIL_COND(!E); + ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path."); E->get() = true; } @@ -458,39 +468,22 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int 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) { - if (network_peer.is_null()) { - ERR_EXPLAIN("Attempt to remote call/set when networking is not active in SceneTree."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree."); - if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING) { - ERR_EXPLAIN("Attempt to remote call/set when networking is not connected yet in SceneTree."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree."); - if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) { - ERR_EXPLAIN("Attempt to remote call/set when networking is disconnected."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to remote call/set when networking is disconnected."); - if (p_argcount > 255) { - ERR_EXPLAIN("Too many arguments >255."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments >255."); if (p_to != 0 && !connected_peers.has(ABS(p_to))) { - if (p_to == network_peer->get_unique_id()) { - ERR_EXPLAIN("Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id())); - } else { - ERR_EXPLAIN("Attempt to remote call unexisting ID: " + itos(p_to)); - } + ERR_FAIL_COND_MSG(p_to == network_peer->get_unique_id(), "Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id()) + "."); - ERR_FAIL(); + ERR_FAIL_MSG("Attempt to remote call unexisting ID: " + itos(p_to) + "."); } NodePath from_path = (root_node->get_path()).rel_path_to(p_from->get_path()); - ERR_EXPLAIN("Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!"); - ERR_FAIL_COND(from_path.is_empty()); + ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!"); // See if the path is cached. PathSentCache *psc = path_send_cache.getptr(from_path); @@ -528,8 +521,7 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p if (p_set) { // Set argument. Error err = encode_variant(*p_arg[0], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed()); - ERR_EXPLAIN("Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!"); - ERR_FAIL_COND(err != OK); + 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()); ofs += len; @@ -541,14 +533,21 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p 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()); - ERR_EXPLAIN("Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); - ERR_FAIL_COND(err != OK); + 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()); ofs += len; } } +#ifdef DEBUG_ENABLED + if (profiling) { + bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].timestamp = OS::get_singleton()->get_ticks_msec(); + bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].packet_size = ofs; + bandwidth_outgoing_pointer = (bandwidth_outgoing_pointer + 1) % bandwidth_outgoing_data.size(); + } +#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); @@ -603,7 +602,16 @@ void MultiplayerAPI::_add_peer(int p_id) { void MultiplayerAPI::_del_peer(int p_id) { connected_peers.erase(p_id); - path_get_cache.erase(p_id); // I no longer need your cache, sorry. + // Cleanup get cache. + path_get_cache.erase(p_id); + // Cleanup sent cache. + // Some refactoring is needed to make this faster and do paths GC. + List<NodePath> keys; + path_send_cache.get_key_list(&keys); + for (List<NodePath>::Element *E = keys.front(); E; E = E->next()) { + PathSentCache *psc = path_send_cache.getptr(E->get()); + psc->confirmed_peers.erase(p_id); + } emit_signal("network_peer_disconnected", p_id); } @@ -624,15 +632,12 @@ void MultiplayerAPI::_server_disconnected() { void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { - ERR_EXPLAIN("Trying to call an RPC while no network peer is active."); - ERR_FAIL_COND(!network_peer.is_valid()); - ERR_EXPLAIN("Trying to call an RPC on a node which is not inside SceneTree."); - ERR_FAIL_COND(!p_node->is_inside_tree()); - ERR_EXPLAIN("Trying to call an RPC via a network peer which is not connected."); - ERR_FAIL_COND(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED); + ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to call an RPC while no network peer is active."); + ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree."); + ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected."); int node_id = network_peer->get_unique_id(); - bool skip_rpc = false; + bool skip_rpc = node_id == p_peer_id; bool call_local_native = false; bool call_local_script = false; bool is_master = p_node->is_network_master(); @@ -655,51 +660,63 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const } if (!skip_rpc) { + +#ifdef DEBUG_ENABLED + if (profiling) { + ObjectID id = p_node->get_instance_id(); + _init_node_profile(id); + profiler_frame_data[id].outgoing_rpc += 1; + } +#endif + _send_rpc(p_node, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount); } if (call_local_native) { + int temp_id = rpc_sender_id; + rpc_sender_id = get_network_unique_id(); Variant::CallError ce; p_node->call(p_method, p_arg, p_argcount, ce); + rpc_sender_id = temp_id; 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; + error = "rpc() aborted in local call: - " + error + "."; ERR_PRINTS(error); return; } } if (call_local_script) { + int temp_id = rpc_sender_id; + rpc_sender_id = get_network_unique_id(); Variant::CallError ce; ce.error = Variant::CallError::CALL_OK; p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce); + rpc_sender_id = temp_id; 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; + error = "rpc() aborted in script local call: - " + error + "."; ERR_PRINTS(error); return; } } + + ERR_FAIL_COND_MSG(skip_rpc && !(call_local_native || call_local_script), "RPC '" + p_method + "' on yourself is not allowed by selected mode."); } void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) { - ERR_EXPLAIN("Trying to RSET while no network peer is active."); - ERR_FAIL_COND(!network_peer.is_valid()); - ERR_EXPLAIN("Trying to RSET on a node which is not inside SceneTree."); - ERR_FAIL_COND(!p_node->is_inside_tree()); - ERR_EXPLAIN("Trying to send an RSET via a network peer which is not connected."); - ERR_FAIL_COND(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED); + ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to RSET while no network peer is active."); + ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to RSET on a node which is not inside SceneTree."); + ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to send an RSET via a network peer which is not connected."); int node_id = network_peer->get_unique_id(); bool is_master = p_node->is_network_master(); - bool skip_rset = false; + bool skip_rset = node_id == p_peer_id; + bool set_local = false; 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. - - bool set_local = false; - const Map<StringName, RPCMode>::Element *E = p_node->get_node_rset_mode(p_property); if (E) { @@ -708,10 +725,14 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const if (set_local) { bool valid; + int temp_id = rpc_sender_id; + + rpc_sender_id = get_network_unique_id(); p_node->set(p_property, p_value, &valid); + rpc_sender_id = temp_id; if (!valid) { - String error = "rset() aborted in local set, property not found: - " + String(p_property); + String error = "rset() aborted in local set, property not found: - " + String(p_property) + "."; ERR_PRINTS(error); return; } @@ -722,11 +743,14 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const set_local = _should_call_local(rpc_mode, is_master, skip_rset); if (set_local) { + int temp_id = rpc_sender_id; + rpc_sender_id = get_network_unique_id(); bool valid = p_node->get_script_instance()->set(p_property, p_value); + rpc_sender_id = temp_id; if (!valid) { - String error = "rset() aborted in local script set, property not found: - " + String(p_property); + String error = "rset() aborted in local script set, property not found: - " + String(p_property) + "."; ERR_PRINTS(error); return; } @@ -734,8 +758,18 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const } } - if (skip_rset) + if (skip_rset) { + ERR_FAIL_COND_MSG(!set_local, "RSET for '" + p_property + "' on yourself is not allowed by selected mode."); return; + } + +#ifdef DEBUG_ENABLED + if (profiling) { + ObjectID id = p_node->get_instance_id(); + _init_node_profile(id); + profiler_frame_data[id].outgoing_rset += 1; + } +#endif const Variant *vptr = &p_value; @@ -744,12 +778,9 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) { - ERR_EXPLAIN("Trying to send an empty raw packet."); - ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA); - ERR_EXPLAIN("Trying to send a raw packet while no network peer is active."); - ERR_FAIL_COND_V(!network_peer.is_valid(), ERR_UNCONFIGURED); - ERR_EXPLAIN("Trying to send a raw packet via a network peer which is not connected."); - ERR_FAIL_COND_V(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet."); + ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no network peer is active."); + ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected."); MAKE_ROOM(p_data.size() + 1); PoolVector<uint8_t>::Read r = p_data.read(); @@ -764,8 +795,7 @@ Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, Networked void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_packet_len < 2); + ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small."); PoolVector<uint8_t> out; int len = p_packet_len - 1; @@ -779,37 +809,32 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac int MultiplayerAPI::get_network_unique_id() const { - ERR_EXPLAIN("No network peer is assigned. Unable to get unique network ID."); - ERR_FAIL_COND_V(!network_peer.is_valid(), 0); + ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), 0, "No network peer is assigned. Unable to get unique network ID."); return network_peer->get_unique_id(); } bool MultiplayerAPI::is_network_server() const { // XXX Maybe fail silently? Maybe should actually return true to make development of both local and online multiplayer easier? - ERR_EXPLAIN("No network peer is assigned. I can't be a server."); - ERR_FAIL_COND_V(!network_peer.is_valid(), false); + ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. I can't be a server."); return network_peer->is_server(); } void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) { - ERR_EXPLAIN("No network peer is assigned. Unable to set 'refuse_new_connections'."); - ERR_FAIL_COND(!network_peer.is_valid()); + ERR_FAIL_COND_MSG(!network_peer.is_valid(), "No network peer is assigned. Unable to set 'refuse_new_connections'."); network_peer->set_refuse_new_connections(p_refuse); } bool MultiplayerAPI::is_refusing_new_network_connections() const { - ERR_EXPLAIN("No network peer is assigned. Unable to get 'refuse_new_connections'."); - ERR_FAIL_COND_V(!network_peer.is_valid(), false); + ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. Unable to get 'refuse_new_connections'."); return network_peer->is_refusing_new_connections(); } Vector<int> MultiplayerAPI::get_network_connected_peers() const { - ERR_EXPLAIN("No network peer is assigned. Assume no peers are connected."); - ERR_FAIL_COND_V(!network_peer.is_valid(), Vector<int>()); + ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), Vector<int>(), "No network peer is assigned. Assume no peers are connected."); Vector<int> ret; for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { @@ -829,6 +854,95 @@ bool MultiplayerAPI::is_object_decoding_allowed() const { return allow_object_decoding; } +void MultiplayerAPI::profiling_start() { +#ifdef DEBUG_ENABLED + profiling = true; + profiler_frame_data.clear(); + + bandwidth_incoming_pointer = 0; + bandwidth_incoming_data.resize(16384); // ~128kB + for (int i = 0; i < bandwidth_incoming_data.size(); ++i) { + bandwidth_incoming_data.write[i].packet_size = -1; + } + + bandwidth_outgoing_pointer = 0; + bandwidth_outgoing_data.resize(16384); // ~128kB + for (int i = 0; i < bandwidth_outgoing_data.size(); ++i) { + bandwidth_outgoing_data.write[i].packet_size = -1; + } +#endif +} + +void MultiplayerAPI::profiling_end() { +#ifdef DEBUG_ENABLED + profiling = false; + bandwidth_incoming_data.clear(); + bandwidth_outgoing_data.clear(); +#endif +} + +int MultiplayerAPI::get_profiling_frame(ProfilingInfo *r_info) { + int i = 0; +#ifdef DEBUG_ENABLED + for (Map<ObjectID, ProfilingInfo>::Element *E = profiler_frame_data.front(); E; E = E->next()) { + r_info[i] = E->get(); + ++i; + } + profiler_frame_data.clear(); +#endif + return i; +} + +int MultiplayerAPI::get_incoming_bandwidth_usage() { +#ifdef DEBUG_ENABLED + return _get_bandwidth_usage(bandwidth_incoming_data, bandwidth_incoming_pointer); +#else + return 0; +#endif +} + +int MultiplayerAPI::get_outgoing_bandwidth_usage() { +#ifdef DEBUG_ENABLED + return _get_bandwidth_usage(bandwidth_outgoing_data, bandwidth_outgoing_pointer); +#else + return 0; +#endif +} + +#ifdef DEBUG_ENABLED +int MultiplayerAPI::_get_bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) { + int total_bandwidth = 0; + + uint32_t timestamp = OS::get_singleton()->get_ticks_msec(); + uint32_t final_timestamp = timestamp - 1000; + + int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size(); + + while (i != p_pointer && p_buffer[i].packet_size > 0) { + if (p_buffer[i].timestamp < final_timestamp) { + return total_bandwidth; + } + total_bandwidth += p_buffer[i].packet_size; + i = (i + p_buffer.size() - 1) % p_buffer.size(); + } + + ERR_FAIL_COND_V_MSG(i == p_pointer, total_bandwidth, "Reached the end of the bandwidth profiler buffer, values might be inaccurate."); + return total_bandwidth; +} + +void MultiplayerAPI::_init_node_profile(ObjectID p_node) { + if (profiler_frame_data.has(p_node)) + return; + profiler_frame_data.insert(p_node, ProfilingInfo()); + profiler_frame_data[p_node].node = p_node; + profiler_frame_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path(); + profiler_frame_data[p_node].incoming_rpc = 0; + profiler_frame_data[p_node].incoming_rset = 0; + profiler_frame_data[p_node].outgoing_rpc = 0; + profiler_frame_data[p_node].outgoing_rset = 0; +} +#endif + void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE)); @@ -855,6 +969,7 @@ void MultiplayerAPI::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer"); + ADD_PROPERTY_DEFAULT("refuse_new_network_connections", false); ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id"))); @@ -878,6 +993,9 @@ MultiplayerAPI::MultiplayerAPI() : allow_object_decoding(false) { rpc_sender_id = 0; root_node = NULL; +#ifdef DEBUG_ENABLED + profiling = false; +#endif clear(); } diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h index 779dd043bd..b824456e0f 100644 --- a/core/io/multiplayer_api.h +++ b/core/io/multiplayer_api.h @@ -38,6 +38,16 @@ class MultiplayerAPI : public Reference { GDCLASS(MultiplayerAPI, Reference); +public: + struct ProfilingInfo { + ObjectID node; + String node_path; + int incoming_rpc; + int incoming_rset; + int outgoing_rpc; + int outgoing_rset; + }; + private: //path sent caches struct PathSentCache { @@ -55,6 +65,23 @@ private: Map<int, NodeInfo> nodes; }; +#ifdef DEBUG_ENABLED + struct BandwidthFrame { + uint32_t timestamp; + int packet_size; + }; + + int bandwidth_incoming_pointer; + Vector<BandwidthFrame> bandwidth_incoming_data; + int bandwidth_outgoing_pointer; + Vector<BandwidthFrame> bandwidth_outgoing_data; + Map<ObjectID, ProfilingInfo> profiler_frame_data; + bool profiling; + + void _init_node_profile(ObjectID p_node); + int _get_bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer); +#endif + Ref<NetworkedMultiplayerPeer> network_peer; int rpc_sender_id; Set<int> connected_peers; @@ -77,7 +104,7 @@ protected: 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_from); + bool _send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target); public: enum NetworkCommands { @@ -130,6 +157,13 @@ public: void set_allow_object_decoding(bool p_enable); bool is_object_decoding_allowed() const; + void profiling_start(); + void profiling_end(); + + int get_profiling_frame(ProfilingInfo *r_info); + int get_incoming_bandwidth_usage(); + int get_outgoing_bandwidth_usage(); + MultiplayerAPI(); ~MultiplayerAPI(); }; diff --git a/core/io/net_socket.h b/core/io/net_socket.h index 94e7ef6f75..3bc1369487 100644 --- a/core/io/net_socket.h +++ b/core/io/net_socket.h @@ -74,6 +74,8 @@ public: virtual void set_ipv6_only_enabled(bool p_enabled) = 0; virtual void set_tcp_no_delay_enabled(bool p_enabled) = 0; virtual void set_reuse_address_enabled(bool p_enabled) = 0; + virtual Error join_multicast_group(const IP_Address &p_multi_address, String p_if_name) = 0; + virtual Error leave_multicast_group(const IP_Address &p_multi_address, String p_if_name) = 0; }; #endif // NET_SOCKET_H diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index c77c81f9e2..23dfc58385 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -101,19 +101,20 @@ Error PacketPeer::put_var(const Variant &p_packet, bool p_full_objects) { return OK; uint8_t *buf = (uint8_t *)alloca(len); - ERR_FAIL_COND_V(!buf, ERR_OUT_OF_MEMORY); + ERR_FAIL_COND_V_MSG(!buf, ERR_OUT_OF_MEMORY, "Out of memory."); err = encode_variant(p_packet, buf, len, p_full_objects || allow_object_decoding); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to encode Variant."); return put_packet(buf, len); } Variant PacketPeer::_bnd_get_var(bool p_allow_objects) { Variant var; - get_var(var, p_allow_objects); + Error err = get_var(var, p_allow_objects); + ERR_FAIL_COND_V(err != OK, Variant()); return var; -}; +} Error PacketPeer::_put_packet(const PoolVector<uint8_t> &p_buffer) { return put_packet_buffer(p_buffer); @@ -149,7 +150,7 @@ void PacketPeer::_bind_methods() { void PacketPeerStream::_set_stream_peer(REF p_peer) { - ERR_FAIL_COND(p_peer.is_null()); + ERR_FAIL_COND_MSG(p_peer.is_null(), "It's not a reference to a valid Resource object."); set_stream_peer(p_peer); } @@ -278,9 +279,9 @@ Ref<StreamPeer> PacketPeerStream::get_stream_peer() const { void PacketPeerStream::set_input_buffer_max_size(int p_max_size) { + ERR_FAIL_COND_MSG(p_max_size < 0, "Max size of input buffer size cannot be smaller than 0."); //warning may lose packets - ERR_EXPLAIN("Buffer in use, resizing would cause loss of data"); - ERR_FAIL_COND(ring_buffer.data_left()); + ERR_FAIL_COND_MSG(ring_buffer.data_left(), "Buffer in use, resizing would cause loss of data."); ring_buffer.resize(nearest_shift(p_max_size + 4)); input_buffer.resize(next_power_of_2(p_max_size + 4)); } diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index 5912b8df94..7e9471c053 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -37,6 +37,27 @@ void PacketPeerUDP::set_blocking_mode(bool p_enable) { blocking = p_enable; } +Error PacketPeerUDP::join_multicast_group(IP_Address p_multi_address, String p_if_name) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(!p_multi_address.is_valid(), ERR_INVALID_PARAMETER); + + if (!_sock->is_open()) { + IP::Type ip_type = p_multi_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + Error err = _sock->open(NetSocket::TYPE_UDP, ip_type); + ERR_FAIL_COND_V(err != OK, err); + _sock->set_blocking_enabled(false); + } + return _sock->join_multicast_group(p_multi_address, p_if_name); +} + +Error PacketPeerUDP::leave_multicast_group(IP_Address p_multi_address, String p_if_name) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(!_sock->is_open(), ERR_UNCONFIGURED); + return _sock->leave_multicast_group(p_multi_address, p_if_name); +} + String PacketPeerUDP::_get_packet_ip() const { return get_packet_address(); @@ -237,6 +258,8 @@ void PacketPeerUDP::_bind_methods() { ClassDB::bind_method(D_METHOD("get_packet_ip"), &PacketPeerUDP::_get_packet_ip); ClassDB::bind_method(D_METHOD("get_packet_port"), &PacketPeerUDP::get_packet_port); ClassDB::bind_method(D_METHOD("set_dest_address", "host", "port"), &PacketPeerUDP::_set_dest_address); + ClassDB::bind_method(D_METHOD("join_multicast_group", "multicast_address", "interface_name"), &PacketPeerUDP::join_multicast_group); + ClassDB::bind_method(D_METHOD("leave_multicast_group", "multicast_address", "interface_name"), &PacketPeerUDP::leave_multicast_group); } PacketPeerUDP::PacketPeerUDP() : diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h index 0593137604..068bd5cd5a 100644 --- a/core/io/packet_peer_udp.h +++ b/core/io/packet_peer_udp.h @@ -77,6 +77,8 @@ public: Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); int get_available_packet_count() const; int get_max_packet_size() const; + Error join_multicast_group(IP_Address p_multi_address, String p_if_name); + Error leave_multicast_group(IP_Address p_multi_address, String p_if_name); PacketPeerUDP(); ~PacketPeerUDP(); diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 8920bbfb81..011ebb1812 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -55,18 +55,16 @@ static void _pad(FileAccess *p_file, int p_bytes) { void PCKPacker::_bind_methods() { - ClassDB::bind_method(D_METHOD("pck_start", "pck_name", "alignment"), &PCKPacker::pck_start); + ClassDB::bind_method(D_METHOD("pck_start", "pck_name", "alignment"), &PCKPacker::pck_start, DEFVAL(0)); ClassDB::bind_method(D_METHOD("add_file", "pck_path", "source_path"), &PCKPacker::add_file); - ClassDB::bind_method(D_METHOD("flush", "verbose"), &PCKPacker::flush); + ClassDB::bind_method(D_METHOD("flush", "verbose"), &PCKPacker::flush, DEFVAL(false)); }; Error PCKPacker::pck_start(const String &p_file, int p_alignment) { file = FileAccess::open(p_file, FileAccess::WRITE); - if (file == NULL) { - return ERR_CANT_CREATE; - }; + ERR_FAIL_COND_V_MSG(!file, ERR_CANT_CREATE, "Can't open file to write: " + String(p_file) + "."); alignment = p_alignment; @@ -109,10 +107,7 @@ Error PCKPacker::add_file(const String &p_file, const String &p_src) { Error PCKPacker::flush(bool p_verbose) { - if (!file) { - ERR_FAIL_COND_V(!file, ERR_INVALID_PARAMETER); - return ERR_INVALID_PARAMETER; - }; + ERR_FAIL_COND_V_MSG(!file, ERR_INVALID_PARAMETER, "File must be opened before use."); // write the index diff --git a/core/io/pck_packer.h b/core/io/pck_packer.h index 4df495b11f..f5661c55a1 100644 --- a/core/io/pck_packer.h +++ b/core/io/pck_packer.h @@ -54,7 +54,7 @@ class PCKPacker : public Reference { Vector<File> files; public: - Error pck_start(const String &p_file, int p_alignment); + Error pck_start(const String &p_file, int p_alignment = 0); Error add_file(const String &p_file, const String &p_src); Error flush(bool p_verbose = false); diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 42070cd132..e91dd579b5 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -378,10 +378,10 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { for (uint32_t i = 0; i < len; i++) { Variant key; Error err = parse_variant(key); - ERR_FAIL_COND_V(err, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Error when trying to parse Variant."); Variant value; err = parse_variant(value); - ERR_FAIL_COND_V(err, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Error when trying to parse Variant."); d[key] = value; } r_v = d; @@ -395,7 +395,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { for (uint32_t i = 0; i < len; i++) { Variant val; Error err = parse_variant(val); - ERR_FAIL_COND_V(err, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Error when trying to parse Variant."); a[i] = val; } r_v = a; @@ -410,7 +410,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { PoolVector<uint8_t>::Write w = array.write(); f->get_buffer(w.ptr(), len); _advance_padding(len); - w = PoolVector<uint8_t>::Write(); + w.release(); r_v = array; } break; @@ -432,7 +432,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { } #endif - w = PoolVector<int>::Write(); + w.release(); r_v = array; } break; case VARIANT_REAL_ARRAY: { @@ -454,7 +454,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { #endif - w = PoolVector<real_t>::Write(); + w.release(); r_v = array; } break; case VARIANT_STRING_ARRAY: { @@ -465,7 +465,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { PoolVector<String>::Write w = array.write(); for (uint32_t i = 0; i < len; i++) w[i] = get_unicode_string(); - w = PoolVector<String>::Write(); + w.release(); r_v = array; } break; @@ -490,10 +490,9 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { #endif } else { - ERR_EXPLAIN("Vector2 size is NOT 8!"); - ERR_FAIL_V(ERR_UNAVAILABLE); + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Vector2 size is NOT 8!"); } - w = PoolVector<Vector2>::Write(); + w.release(); r_v = array; } break; @@ -518,10 +517,9 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { #endif } else { - ERR_EXPLAIN("Vector3 size is NOT 12!"); - ERR_FAIL_V(ERR_UNAVAILABLE); + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Vector3 size is NOT 12!"); } - w = PoolVector<Vector3>::Write(); + w.release(); r_v = array; } break; @@ -546,10 +544,9 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { #endif } else { - ERR_EXPLAIN("Color size is NOT 16!"); - ERR_FAIL_V(ERR_UNAVAILABLE); + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Color size is NOT 16!"); } - w = PoolVector<Color>::Write(); + w.release(); r_v = array; } break; #ifndef DISABLE_DEPRECATED @@ -571,7 +568,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { const uint32_t current_version = 0; if (format_version > current_version) { - ERR_PRINT("Format version for encoded binary image is too new"); + ERR_PRINT("Format version for encoded binary image is too new."); return ERR_PARSE_ERROR; } @@ -584,7 +581,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { PoolVector<uint8_t>::Write w = imgdata.write(); f->get_buffer(w.ptr(), datalen); _advance_padding(datalen); - w = PoolVector<uint8_t>::Write(); + w.release(); Ref<Image> image; image.instance(); @@ -597,7 +594,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { data.resize(f->get_32()); PoolVector<uint8_t>::Write w = data.write(); f->get_buffer(w.ptr(), data.size()); - w = PoolVector<uint8_t>::Write(); + w.release(); Ref<Image> image; @@ -655,8 +652,7 @@ Error ResourceInteractiveLoaderBinary::poll() { } else { error = ERR_FILE_MISSING_DEPENDENCIES; - ERR_EXPLAIN("Can't load dependency: " + path); - ERR_FAIL_V(error); + ERR_FAIL_V_MSG(error, "Can't load dependency: " + path + "."); } } else { @@ -711,16 +707,15 @@ Error ResourceInteractiveLoaderBinary::poll() { Object *obj = ClassDB::instance(t); if (!obj) { error = ERR_FILE_CORRUPT; - ERR_EXPLAIN(local_path + ":Resource of unrecognized type in file: " + t); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource of unrecognized type in file: " + t + "."); } - ERR_FAIL_COND_V(!obj, ERR_FILE_CORRUPT); Resource *r = Object::cast_to<Resource>(obj); if (!r) { + String obj_class = obj->get_class(); error = ERR_FILE_CORRUPT; memdelete(obj); //bye - ERR_EXPLAIN(local_path + ":Resource type in resource field not a resource, type is: " + obj->get_class()); - ERR_FAIL_COND_V(!r, ERR_FILE_CORRUPT); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource type in resource field not a resource, type is: " + obj_class + "."); } RES res = RES(r); @@ -850,8 +845,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { //not normal error = ERR_FILE_UNRECOGNIZED; - ERR_EXPLAIN("Unrecognized binary resource file: " + local_path); - ERR_FAIL(); + ERR_FAIL_MSG("Unrecognized binary resource file: " + local_path + "."); } bool big_endian = f->get_32(); @@ -877,8 +871,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) { f->close(); - ERR_EXPLAIN("File Format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path); - ERR_FAIL(); + ERR_FAIL_MSG("File format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path + "."); } type = get_unicode_string(); @@ -926,8 +919,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { if (f->eof_reached()) { error = ERR_FILE_CORRUPT; - ERR_EXPLAIN("Premature End Of File: " + local_path); - ERR_FAIL(); + ERR_FAIL_MSG("Premature end of file (EOF): " + local_path + "."); } } @@ -991,10 +983,7 @@ Ref<ResourceInteractiveLoader> ResourceFormatLoaderBinary::load_interactive(cons Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - if (err != OK) { - - ERR_FAIL_COND_V(err != OK, Ref<ResourceInteractiveLoader>()); - } + ERR_FAIL_COND_V_MSG(err != OK, Ref<ResourceInteractiveLoader>(), "Cannot open file '" + p_path + "'."); Ref<ResourceInteractiveLoaderBinary> ria = memnew(ResourceInteractiveLoaderBinary); String path = p_original_path != "" ? p_original_path : p_path; @@ -1043,7 +1032,7 @@ bool ResourceFormatLoaderBinary::handles_type(const String &p_type) const { void ResourceFormatLoaderBinary::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "Cannot open file '" + p_path + "'."); Ref<ResourceInteractiveLoaderBinary> ria = memnew(ResourceInteractiveLoaderBinary); ria->local_path = ProjectSettings::get_singleton()->localize_path(p_path); @@ -1057,7 +1046,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons //Error error=OK; FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); + ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open file '" + p_path + "'."); FileAccess *fw = NULL; //=FileAccess::open(p_path+".depren"); @@ -1077,7 +1066,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons if (err) { memdelete(fac); memdelete(facw); - ERR_FAIL_COND_V(err, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Cannot create file '" + p_path + ".depren'."); } fw = facw; @@ -1087,14 +1076,13 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons //error=ERR_FILE_UNRECOGNIZED; memdelete(f); - ERR_EXPLAIN("Unrecognized binary resource file: " + local_path); - ERR_FAIL_V(ERR_FILE_UNRECOGNIZED); + ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unrecognized binary resource file '" + local_path + "'."); } else { fw = FileAccess::open(p_path + ".depren", FileAccess::WRITE); if (!fw) { memdelete(f); } - ERR_FAIL_COND_V(!fw, ERR_CANT_CREATE); + ERR_FAIL_COND_V_MSG(!fw, ERR_CANT_CREATE, "Cannot create file '" + p_path + ".depren'."); uint8_t magic[4] = { 'R', 'S', 'R', 'C' }; fw->store_buffer(magic, 4); @@ -1125,13 +1113,12 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons memdelete(da); //use the old approach - WARN_PRINT(("This file is old, so it can't refactor dependencies, opening and resaving: " + p_path).utf8().get_data()); + WARN_PRINTS("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); - if (err != OK) { - ERR_FAIL_COND_V(err != OK, ERR_FILE_CANT_OPEN); - } + + ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_OPEN, "Cannot open file '" + p_path + "'."); Ref<ResourceInteractiveLoaderBinary> ria = memnew(ResourceInteractiveLoaderBinary); ria->local_path = ProjectSettings::get_singleton()->localize_path(p_path); @@ -1157,8 +1144,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons memdelete(f); memdelete(fw); - ERR_EXPLAIN("File Format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path); - ERR_FAIL_V(ERR_FILE_UNRECOGNIZED); + ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "File format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path + "."); } // Since we're not actually converting the file contents, leave the version @@ -1481,7 +1467,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia case Variant::_RID: { f->store_32(VARIANT_RID); - WARN_PRINT("Can't save RIDs"); + WARN_PRINT("Can't save RIDs."); RID val = p_property; f->store_32(val.get_id()); } break; @@ -1501,8 +1487,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia if (!resource_set.has(res)) { f->store_32(OBJECT_EMPTY); - ERR_EXPLAIN("Resource was not pre cached for the resource section, most likely due to circular refedence."); - ERR_FAIL(); + ERR_FAIL_MSG("Resource was not pre cached for the resource section, most likely due to circular reference."); } f->store_32(OBJECT_INTERNAL_RESOURCE); @@ -1633,8 +1618,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; default: { - ERR_EXPLAIN("Invalid variant"); - ERR_FAIL(); + ERR_FAIL_MSG("Invalid variant."); } } } @@ -1698,7 +1682,7 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant int len = varray.size(); for (int i = 0; i < len; i++) { - Variant v = varray.get(i); + const Variant &v = varray.get(i); _find_resources(v); } @@ -1725,7 +1709,8 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant get_string_index(np.get_subname(i)); } break; - default: {} + default: { + } } } @@ -1766,7 +1751,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p f = FileAccess::open(p_path, FileAccess::WRITE, &err); } - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create file '" + p_path + "'."); relative_paths = p_flags & ResourceSaver::FLAG_RELATIVE_PATHS; skip_editor = p_flags & ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES; @@ -1801,6 +1786,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) { f->close(); + memdelete(f); return ERR_CANT_CREATE; } @@ -1953,10 +1939,12 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) { f->close(); + memdelete(f); return ERR_CANT_CREATE; } f->close(); + memdelete(f); return OK; } diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index a4894e4033..27777c8e8b 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -100,7 +100,6 @@ public: }; class ResourceFormatLoaderBinary : public ResourceFormatLoader { - GDCLASS(ResourceFormatLoaderBinary, ResourceFormatLoader) public: virtual Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; @@ -162,7 +161,6 @@ public: }; class ResourceFormatSaverBinary : public ResourceFormatSaver { - GDCLASS(ResourceFormatSaverBinary, ResourceFormatSaver) public: static ResourceFormatSaverBinary *singleton; virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index b5fa412576..63d7ba547c 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -94,6 +94,8 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy r_path_and_type.type = value; } else if (assign == "importer") { r_path_and_type.importer = value; + } else if (assign == "group_file") { + r_path_and_type.group_file = value; } else if (assign == "metadata") { r_path_and_type.metadata = value; } else if (assign == "valid") { @@ -159,7 +161,8 @@ void ResourceFormatImporter::get_recognized_extensions(List<String> *p_extension void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { if (p_type == "") { - return get_recognized_extensions(p_extensions); + get_recognized_extensions(p_extensions); + return; } Set<String> found; @@ -294,6 +297,14 @@ void ResourceFormatImporter::get_internal_resource_path_list(const String &p_pat memdelete(f); } +String ResourceFormatImporter::get_import_group_file(const String &p_path) const { + + bool valid = true; + PathAndType pat; + _get_path_and_type(p_path, pat, &valid); + return valid ? pat.group_file : String(); +} + bool ResourceFormatImporter::is_import_valid(const String &p_path) const { bool valid = true; @@ -337,7 +348,7 @@ void ResourceFormatImporter::get_dependencies(const String &p_path, List<String> return; } - return ResourceLoader::get_dependencies(pat.path, p_dependencies, p_add_types); + ResourceLoader::get_dependencies(pat.path, p_dependencies, p_add_types); } Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_name(const String &p_name) const { diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index 1c146c33d7..9cf298a7f5 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -37,12 +37,11 @@ class ResourceImporter; class ResourceFormatImporter : public ResourceFormatLoader { - GDCLASS(ResourceFormatImporter, ResourceFormatLoader) - struct PathAndType { String path; String type; String importer; + String group_file; Variant metadata; }; @@ -69,6 +68,7 @@ public: virtual bool is_import_valid(const String &p_path) const; virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); virtual bool is_imported(const String &p_path) const { return recognize_path(p_path); } + virtual String get_import_group_file(const String &p_path) const; virtual bool exists(const String &p_path) const; virtual bool can_be_imported(const String &p_path) const; @@ -94,7 +94,8 @@ public: class ResourceImporter : public Reference { - GDCLASS(ResourceImporter, Reference) + GDCLASS(ResourceImporter, Reference); + public: virtual String get_importer_name() const = 0; virtual String get_visible_name() const = 0; @@ -120,8 +121,11 @@ public: virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const = 0; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const = 0; + virtual String get_option_group_file() const { return String(); } virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL) = 0; + + virtual Error import_group_file(const String &p_group_file, const Map<String, Map<StringName, Variant> > &p_source_file_options, const Map<String, String> &p_base_paths) { return ERR_UNAVAILABLE; } virtual bool are_import_settings_valid(const String &p_path) const { return true; } virtual String get_import_settings_string() const { return String(); } }; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index e4b694b64f..6f64543b3e 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -205,10 +205,8 @@ RES ResourceFormatLoader::load(const String &p_path, const String &p_original_pa if (r_error) *r_error = err; - ERR_FAIL_COND_V(err != OK, RES()); + ERR_FAIL_COND_V_MSG(err != OK, RES(), "Failed to load resource '" + p_path + "'."); } - - return RES(); } void ResourceFormatLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { @@ -277,13 +275,9 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c return res; } - if (found) { - ERR_EXPLAIN("Failed loading resource: " + p_path); - } else { - ERR_EXPLAIN("No loader found for resource: " + p_path); - } - ERR_FAIL_V(RES()); - return RES(); + ERR_FAIL_COND_V_MSG(found, RES(), "Failed loading resource: " + p_path + "."); + + ERR_FAIL_V_MSG(RES(), "No loader found for resource: " + p_path + "."); } bool ResourceLoader::_add_to_loading_map(const String &p_path) { @@ -358,10 +352,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p { bool success = _add_to_loading_map(local_path); - if (!success) { - ERR_EXPLAIN("Resource: '" + local_path + "' is already being loaded. Cyclic reference?"); - ERR_FAIL_V(RES()); - } + ERR_FAIL_COND_V_MSG(!success, RES(), "Resource: '" + local_path + "' is already being loaded. Cyclic reference?"); } //lock first if possible @@ -398,8 +389,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); } - ERR_EXPLAIN("Remapping '" + local_path + "'failed."); - ERR_FAIL_V(RES()); + ERR_FAIL_V_MSG(RES(), "Remapping '" + local_path + "' failed."); } print_verbose("Loading resource: " + path); @@ -482,10 +472,7 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_ if (!p_no_cache) { bool success = _add_to_loading_map(local_path); - if (!success) { - ERR_EXPLAIN("Resource: '" + local_path + "' is already being loaded. Cyclic reference?"); - ERR_FAIL_V(RES()); - } + ERR_FAIL_COND_V_MSG(!success, RES(), "Resource: '" + local_path + "' is already being loaded. Cyclic reference?"); if (ResourceCache::has(local_path)) { @@ -506,8 +493,7 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_ if (!p_no_cache) { _remove_from_loading_map(local_path); } - ERR_EXPLAIN("Remapping '" + local_path + "'failed."); - ERR_FAIL_V(RES()); + ERR_FAIL_V_MSG(RES(), "Remapping '" + local_path + "' failed."); } print_verbose("Loading resource: " + path); @@ -537,13 +523,9 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_ _remove_from_loading_map(local_path); } - if (found) { - ERR_EXPLAIN("Failed loading resource: " + path); - } else { - ERR_EXPLAIN("No loader found for resource: " + path); - } - ERR_FAIL_V(Ref<ResourceInteractiveLoader>()); - return Ref<ResourceInteractiveLoader>(); + ERR_FAIL_COND_V_MSG(found, Ref<ResourceInteractiveLoader>(), "Failed loading resource: " + path + "."); + + ERR_FAIL_V_MSG(Ref<ResourceInteractiveLoader>(), "No loader found for resource: " + path + "."); } void ResourceLoader::add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front) { @@ -608,6 +590,30 @@ int ResourceLoader::get_import_order(const String &p_path) { return 0; } +String ResourceLoader::get_import_group_file(const String &p_path) { + String path = _path_remap(p_path); + + String local_path; + if (path.is_rel_path()) + local_path = "res://" + path; + else + local_path = ProjectSettings::get_singleton()->localize_path(path); + + for (int i = 0; i < loader_count; i++) { + + if (!loader[i]->recognize_path(local_path)) + continue; + /* + if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) + continue; + */ + + return loader[i]->get_import_group_file(p_path); + } + + return String(); //not found +} + bool ResourceLoader::is_import_valid(const String &p_path) { String path = _path_remap(p_path); @@ -728,26 +734,49 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem String new_path = p_path; - if (translation_remaps.has(new_path)) { + if (translation_remaps.has(p_path)) { + // translation_remaps has the following format: + // { "res://path.png": PoolStringArray( "res://path-ru.png:ru", "res://path-de.png:de" ) } + + // To find the path of the remapped resource, we extract the locale name after + // the last ':' to match the project locale. + // We also fall back in case of regional locales as done in TranslationServer::translate + // (e.g. 'ru_RU' -> 'ru' if the former has no specific mapping). - Vector<String> &v = *translation_remaps.getptr(new_path); String locale = TranslationServer::get_singleton()->get_locale(); - if (r_translation_remapped) { - *r_translation_remapped = true; - } - for (int i = 0; i < v.size(); i++) { + ERR_FAIL_COND_V_MSG(locale.length() < 2, p_path, "Could not remap path '" + p_path + "' for translation as configured locale '" + locale + "' is invalid."); + String lang = TranslationServer::get_language_code(locale); - int split = v[i].find_last(":"); - if (split == -1) - continue; - String l = v[i].right(split + 1).strip_edges(); - if (l == String()) + Vector<String> &res_remaps = *translation_remaps.getptr(new_path); + bool near_match = false; + + for (int i = 0; i < res_remaps.size(); i++) { + int split = res_remaps[i].find_last(":"); + if (split == -1) { continue; + } - if (l.begins_with(locale)) { - new_path = v[i].left(split); + String l = res_remaps[i].right(split + 1).strip_edges(); + if (l == locale) { // Exact match. + new_path = res_remaps[i].left(split); break; + } else if (near_match) { + continue; // Already found near match, keep going for potential exact match. } + + // No exact match (e.g. locale 'ru_RU' but remap is 'ru'), let's look further + // for a near match (same language code, i.e. first 2 or 3 letters before + // regional code, if included). + if (TranslationServer::get_language_code(l) == lang) { + // Language code matches, that's a near match. Keep looking for exact match. + near_match = true; + new_path = res_remaps[i].left(split); + continue; + } + } + + if (r_translation_remapped) { + *r_translation_remapped = true; } } @@ -755,8 +784,8 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem new_path = path_remaps[new_path]; } - if (new_path == p_path) { //did not remap - //try file remap + if (new_path == p_path) { // Did not remap. + // Try file remap. Error err; FileAccess *f = FileAccess::open(p_path + ".remap", FileAccess::READ, &err); @@ -781,7 +810,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_PRINTS("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text + "."); break; } @@ -912,16 +941,13 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) { Ref<Script> s = res; StringName ibt = s->get_instance_base_type(); bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatLoader"); - ERR_EXPLAIN("Script does not inherit a CustomResourceLoader: " + script_path); - ERR_FAIL_COND_V(!valid_type, false); + ERR_FAIL_COND_V_MSG(!valid_type, false, "Script does not inherit a CustomResourceLoader: " + script_path + "."); Object *obj = ClassDB::instance(ibt); - ERR_EXPLAIN("Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt)); - ERR_FAIL_COND_V(obj == NULL, false); + ERR_FAIL_COND_V_MSG(obj == NULL, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + "."); - ResourceFormatLoader *crl = NULL; - crl = Object::cast_to<ResourceFormatLoader>(obj); + ResourceFormatLoader *crl = Object::cast_to<ResourceFormatLoader>(obj); crl->set_script(s.get_ref_ptr()); ResourceLoader::add_resource_format_loader(crl); diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index ca7610a0d2..93df8cadb0 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -33,9 +33,6 @@ #include "core/os/thread.h" #include "core/resource.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ class ResourceInteractiveLoader : public Reference { @@ -62,7 +59,7 @@ public: class ResourceFormatLoader : public Reference { - GDCLASS(ResourceFormatLoader, Reference) + GDCLASS(ResourceFormatLoader, Reference); protected: static void _bind_methods(); @@ -81,6 +78,7 @@ public: virtual bool is_import_valid(const String &p_path) const { return true; } virtual bool is_imported(const String &p_path) const { return false; } virtual int get_import_order(const String &p_path) const { return 0; } + virtual String get_import_group_file(const String &p_path) const { return ""; } //no group virtual ~ResourceFormatLoader() {} }; @@ -155,6 +153,7 @@ public: static void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); static Error rename_dependencies(const String &p_path, const Map<String, String> &p_map); static bool is_import_valid(const String &p_path); + static String get_import_group_file(const String &p_path); static bool is_imported(const String &p_path); static int get_import_order(const String &p_path); diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 0cecca904d..7aa8732366 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -137,7 +137,6 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t save_callback(p_resource, p_path); return OK; - } else { } } @@ -159,7 +158,7 @@ void ResourceSaver::get_recognized_extensions(const RES &p_resource, List<String void ResourceSaver::add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front) { - ERR_FAIL_COND(p_format_saver.is_null()); + ERR_FAIL_COND_MSG(p_format_saver.is_null(), "It's not a reference to a valid ResourceFormatSaver object."); ERR_FAIL_COND(saver_count >= MAX_SAVERS); if (p_at_front) { @@ -175,7 +174,7 @@ void ResourceSaver::add_resource_format_saver(Ref<ResourceFormatSaver> p_format_ void ResourceSaver::remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver) { - ERR_FAIL_COND(p_format_saver.is_null()); + ERR_FAIL_COND_MSG(p_format_saver.is_null(), "It's not a reference to a valid ResourceFormatSaver object."); // Find saver int i = 0; @@ -215,16 +214,13 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) { Ref<Script> s = res; StringName ibt = s->get_instance_base_type(); bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatSaver"); - ERR_EXPLAIN("Script does not inherit a CustomResourceSaver: " + script_path); - ERR_FAIL_COND_V(!valid_type, false); + ERR_FAIL_COND_V_MSG(!valid_type, false, "Script does not inherit a CustomResourceSaver: " + script_path + "."); Object *obj = ClassDB::instance(ibt); - ERR_EXPLAIN("Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt)); - ERR_FAIL_COND_V(obj == NULL, false); + ERR_FAIL_COND_V_MSG(obj == NULL, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + "."); - ResourceFormatSaver *crl = NULL; - crl = Object::cast_to<ResourceFormatSaver>(obj); + ResourceFormatSaver *crl = Object::cast_to<ResourceFormatSaver>(obj); crl->set_script(s.get_ref_ptr()); ResourceSaver::add_resource_format_saver(crl); diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h index 7df3bfb1f8..20e05d827a 100644 --- a/core/io/resource_saver.h +++ b/core/io/resource_saver.h @@ -33,12 +33,8 @@ #include "core/resource.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - class ResourceFormatSaver : public Reference { - GDCLASS(ResourceFormatSaver, Reference) + GDCLASS(ResourceFormatSaver, Reference); protected: static void _bind_methods(); diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 6ad24a5f3a..f19e055b64 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -79,7 +79,7 @@ Array StreamPeer::_get_data(int p_bytes) { PoolVector<uint8_t>::Write w = data.write(); Error err = get_data(&w[0], p_bytes); - w = PoolVector<uint8_t>::Write(); + w.release(); ret.push_back(err); ret.push_back(data); return ret; @@ -101,7 +101,7 @@ Array StreamPeer::_get_partial_data(int p_bytes) { PoolVector<uint8_t>::Write w = data.write(); int received; Error err = get_partial_data(&w[0], p_bytes, received); - w = PoolVector<uint8_t>::Write(); + w.release(); if (err != OK) { data.resize(0); @@ -369,7 +369,9 @@ Variant StreamPeer::get_var(bool p_allow_objects) { ERR_FAIL_COND_V(err != OK, Variant()); Variant ret; - decode_variant(ret, var.ptr(), len, NULL, p_allow_objects); + err = decode_variant(ret, var.ptr(), len, NULL, p_allow_objects); + ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to decode Variant."); + return ret; } diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index 254ae84bf5..f2eaf57acc 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -30,34 +30,19 @@ #include "stream_peer_ssl.h" -#include "core/io/certs_compressed.gen.h" -#include "core/io/compression.h" -#include "core/os/file_access.h" -#include "core/project_settings.h" +#include "core/engine.h" StreamPeerSSL *(*StreamPeerSSL::_create)() = NULL; StreamPeerSSL *StreamPeerSSL::create() { - return _create(); + if (_create) + return _create(); + return NULL; } -StreamPeerSSL::LoadCertsFromMemory StreamPeerSSL::load_certs_func = NULL; bool StreamPeerSSL::available = false; -void StreamPeerSSL::load_certs_from_memory(const PoolByteArray &p_memory) { - if (load_certs_func) - load_certs_func(p_memory); -} - -void StreamPeerSSL::load_certs_from_file(String p_path) { - if (p_path != "") { - PoolByteArray certs = get_cert_file_as_array(p_path); - if (certs.size() > 0) - load_certs_func(certs); - } -} - bool StreamPeerSSL::is_available() { return available; } @@ -70,56 +55,11 @@ bool StreamPeerSSL::is_blocking_handshake_enabled() const { return blocking_handshake; } -PoolByteArray StreamPeerSSL::get_cert_file_as_array(String p_path) { - - PoolByteArray out; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (f) { - int flen = f->get_len(); - out.resize(flen + 1); - PoolByteArray::Write w = out.write(); - f->get_buffer(w.ptr(), flen); - w[flen] = 0; // Make sure it ends with string terminator - memdelete(f); -#ifdef DEBUG_ENABLED - print_verbose(vformat("Loaded certs from '%s'.", p_path)); -#endif - } - - return out; -} - -PoolByteArray StreamPeerSSL::get_project_cert_array() { - - PoolByteArray out; - String certs_path = GLOBAL_DEF("network/ssl/certificates", ""); - ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt")); - - if (certs_path != "") { - // Use certs defined in project settings. - return get_cert_file_as_array(certs_path); - } -#ifdef BUILTIN_CERTS_ENABLED - else { - // Use builtin certs only if user did not override it in project settings. - out.resize(_certs_uncompressed_size + 1); - PoolByteArray::Write w = out.write(); - Compression::decompress(w.ptr(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE); - w[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator -#ifdef DEBUG_ENABLED - print_verbose("Loaded builtin certs"); -#endif - } -#endif - - return out; -} - void StreamPeerSSL::_bind_methods() { ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll); - ClassDB::bind_method(D_METHOD("accept_stream", "base"), &StreamPeerSSL::accept_stream); - ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("accept_stream", "stream", "private_key", "certificate", "chain"), &StreamPeerSSL::accept_stream, DEFVAL(Ref<X509Certificate>())); + ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname", "valid_certificate"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String()), DEFVAL(Ref<X509Certificate>())); ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status); ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerSSL::disconnect_from_stream); ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerSSL::set_blocking_handshake_enabled); diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index 482576c4c6..dedc35b9ac 100644 --- a/core/io/stream_peer_ssl.h +++ b/core/io/stream_peer_ssl.h @@ -31,19 +31,16 @@ #ifndef STREAM_PEER_SSL_H #define STREAM_PEER_SSL_H +#include "core/crypto/crypto.h" #include "core/io/stream_peer.h" class StreamPeerSSL : public StreamPeer { GDCLASS(StreamPeerSSL, StreamPeer); -public: - typedef void (*LoadCertsFromMemory)(const PoolByteArray &p_certs); - protected: static StreamPeerSSL *(*_create)(); static void _bind_methods(); - static LoadCertsFromMemory load_certs_func; static bool available; bool blocking_handshake; @@ -61,18 +58,14 @@ public: bool is_blocking_handshake_enabled() const; virtual void poll() = 0; - virtual Error accept_stream(Ref<StreamPeer> p_base) = 0; - virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String()) = 0; + virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>()) = 0; + virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String(), Ref<X509Certificate> p_valid_cert = Ref<X509Certificate>()) = 0; virtual Status get_status() const = 0; virtual void disconnect_from_stream() = 0; static StreamPeerSSL *create(); - static PoolByteArray get_cert_file_as_array(String p_path); - static PoolByteArray get_project_cert_array(); - static void load_certs_from_file(String p_path); - static void load_certs_from_memory(const PoolByteArray &p_memory); static bool is_available(); StreamPeerSSL(); diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp index 45f3e46e35..b9c5896b24 100644 --- a/core/io/stream_peer_tcp.cpp +++ b/core/io/stream_peer_tcp.cpp @@ -30,6 +30,8 @@ #include "stream_peer_tcp.h" +#include "core/project_settings.h" + Error StreamPeerTCP::_poll_connection() { ERR_FAIL_COND_V(status != STATUS_CONNECTING || !_sock.is_valid() || !_sock->is_open(), FAILED); @@ -40,6 +42,12 @@ Error StreamPeerTCP::_poll_connection() { status = STATUS_CONNECTED; return OK; } else if (err == ERR_BUSY) { + // Check for connect timeout + if (OS::get_singleton()->get_ticks_msec() > timeout) { + disconnect_from_host(); + status = STATUS_ERROR; + return ERR_CONNECTION_ERROR; + } // Still trying to connect return OK; } @@ -54,6 +62,7 @@ void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint _sock = p_sock; _sock->set_blocking_enabled(false); + timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/tcp/connect_timeout_seconds")) * 1000); status = STATUS_CONNECTING; peer_host = p_host; @@ -74,6 +83,7 @@ Error StreamPeerTCP::connect_to_host(const IP_Address &p_host, uint16_t p_port) _sock->set_blocking_enabled(false); + timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/tcp/connect_timeout_seconds")) * 1000); err = _sock->connect_to_host(p_host, p_port); if (err == OK) { @@ -217,6 +227,11 @@ Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool to_read -= read; total_read += read; + + if (!p_block) { + r_received = total_read; + return OK; + } } } @@ -233,16 +248,7 @@ void StreamPeerTCP::set_no_delay(bool p_enabled) { bool StreamPeerTCP::is_connected_to_host() const { - if (status == STATUS_NONE || status == STATUS_ERROR) { - - return false; - } - - if (status != STATUS_CONNECTED) { - return true; - } - - return _sock.is_valid() && _sock->is_open(); + return _sock.is_valid() && _sock->is_open() && (status == STATUS_CONNECTED || status == STATUS_CONNECTING); } StreamPeerTCP::Status StreamPeerTCP::get_status() { @@ -276,6 +282,7 @@ void StreamPeerTCP::disconnect_from_host() { if (_sock.is_valid() && _sock->is_open()) _sock->close(); + timeout = 0; status = STATUS_NONE; peer_host = IP_Address(); peer_port = 0; @@ -351,8 +358,8 @@ void StreamPeerTCP::_bind_methods() { StreamPeerTCP::StreamPeerTCP() : _sock(Ref<NetSocket>(NetSocket::create())), + timeout(0), status(STATUS_NONE), - peer_host(IP_Address()), peer_port(0) { } diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h index 1ca39375aa..321fb3a6c8 100644 --- a/core/io/stream_peer_tcp.h +++ b/core/io/stream_peer_tcp.h @@ -52,6 +52,7 @@ public: protected: Ref<NetSocket> _sock; + uint64_t timeout; Status status; IP_Address peer_host; uint16_t peer_port; diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp index 6599c4eb5b..a2756164bc 100644 --- a/core/io/tcp_server.cpp +++ b/core/io/tcp_server.cpp @@ -34,6 +34,7 @@ void TCP_Server::_bind_methods() { ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCP_Server::listen, DEFVAL("*")); ClassDB::bind_method(D_METHOD("is_connection_available"), &TCP_Server::is_connection_available); + ClassDB::bind_method(D_METHOD("is_listening"), &TCP_Server::is_listening); ClassDB::bind_method(D_METHOD("take_connection"), &TCP_Server::take_connection); ClassDB::bind_method(D_METHOD("stop"), &TCP_Server::stop); } @@ -75,6 +76,12 @@ Error TCP_Server::listen(uint16_t p_port, const IP_Address &p_bind_address) { return OK; } +bool TCP_Server::is_listening() const { + ERR_FAIL_COND_V(!_sock.is_valid(), false); + + return _sock->is_open(); +} + bool TCP_Server::is_connection_available() const { ERR_FAIL_COND_V(!_sock.is_valid(), false); @@ -83,11 +90,7 @@ bool TCP_Server::is_connection_available() const { return false; Error err = _sock->poll(NetSocket::POLL_TYPE_IN, 0); - if (err != OK) { - return false; - } - - return true; + return (err == OK); } Ref<StreamPeerTCP> TCP_Server::take_connection() { diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h index 538db175ad..ef64044599 100644 --- a/core/io/tcp_server.h +++ b/core/io/tcp_server.h @@ -50,6 +50,7 @@ protected: public: Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")); + bool is_listening() const; bool is_connection_available() const; Ref<StreamPeerTCP> take_connection(); diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 67a0a905bd..9b6888ac21 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -67,8 +67,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S if (status == STATUS_READING_ID) { memdelete(f); - ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); - ERR_FAIL_V(RES()); + ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); } else { break; } @@ -79,8 +78,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S if (status == STATUS_READING_ID) { memdelete(f); - ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected 'msgid', was expecting 'msgstr' while parsing: "); - ERR_FAIL_V(RES()); + ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected 'msgid', was expecting 'msgstr' while parsing: "); } if (msg_id != "") { @@ -102,8 +100,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S if (status != STATUS_READING_ID) { memdelete(f); - ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected 'msgstr', was expecting 'msgid' while parsing: "); - ERR_FAIL_V(RES()); + ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected 'msgstr', was expecting 'msgid' while parsing: "); } l = l.substr(6, l.length()).strip_edges(); @@ -118,11 +115,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S continue; //nothing to read or comment } - if (!l.begins_with("\"") || status == STATUS_NONE) { - //not a string? failure! - ERR_EXPLAIN(p_path + ":" + itos(line) + " Invalid line '" + l + "' while parsing: "); - ERR_FAIL_V(RES()); - } + ERR_FAIL_COND_V_MSG(!l.begins_with("\"") || status == STATUS_NONE, RES(), p_path + ":" + itos(line) + " Invalid line '" + l + "' while parsing: "); l = l.substr(1, l.length()); //find final quote @@ -135,10 +128,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S } } - if (end_pos == -1) { - ERR_EXPLAIN(p_path + ":" + itos(line) + " Expected '\"' at end of message while parsing file: "); - ERR_FAIL_V(RES()); - } + ERR_FAIL_COND_V_MSG(end_pos == -1, RES(), p_path + ":" + itos(line) + " Expected '\"' at end of message while parsing file: "); l = l.substr(0, end_pos); l = l.c_unescape(); @@ -163,10 +153,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S config = msg_str; } - if (config == "") { - ERR_EXPLAIN("No config found in file: " + p_path); - ERR_FAIL_V(RES()); - } + ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + p_path + "."); Vector<String> configs = config.split("\n"); for (int i = 0; i < configs.size(); i++) { @@ -195,7 +182,7 @@ RES TranslationLoaderPO::load(const String &p_path, const String &p_original_pat *r_error = ERR_CANT_OPEN; FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V(!f, RES()); + ERR_FAIL_COND_V_MSG(!f, RES(), "Cannot open file '" + p_path + "'."); return load_translation(f, r_error); } diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h index d5fd264385..9d9c5d16ee 100644 --- a/core/io/translation_loader_po.h +++ b/core/io/translation_loader_po.h @@ -36,7 +36,6 @@ #include "core/translation.h" class TranslationLoaderPO : public ResourceFormatLoader { - GDCLASS(TranslationLoaderPO, ResourceFormatLoader) public: static RES load_translation(FileAccess *f, Error *r_error, const String &p_path = String()); virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index 4638ddcc09..575c78734f 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -348,7 +348,7 @@ uint64_t XMLParser::get_node_offset() const { Error XMLParser::seek(uint64_t p_pos) { - ERR_FAIL_COND_V(!data, ERR_FILE_EOF) + ERR_FAIL_COND_V(!data, ERR_FILE_EOF); ERR_FAIL_COND_V(p_pos >= length, ERR_FILE_EOF); P = data + p_pos; @@ -443,10 +443,8 @@ String XMLParser::get_attribute_value(const String &p_name) const { } } - if (idx < 0) { - ERR_EXPLAIN("Attribute not found: " + p_name); - } - ERR_FAIL_COND_V(idx < 0, ""); + ERR_FAIL_COND_V_MSG(idx < 0, "", "Attribute not found: " + p_name + "."); + return attributes[idx].value; } @@ -486,9 +484,7 @@ Error XMLParser::open(const String &p_path) { Error err; FileAccess *file = FileAccess::open(p_path, FileAccess::READ, &err); - if (err) { - ERR_FAIL_COND_V(err != OK, err); - } + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'."); length = file->get_len(); ERR_FAIL_COND_V(length < 1, ERR_FILE_CORRUPT); diff --git a/core/io/zip_io.h b/core/io/zip_io.h index fb63878a4c..4eb1c8b46c 100644 --- a/core/io/zip_io.h +++ b/core/io/zip_io.h @@ -33,7 +33,7 @@ #include "core/os/file_access.h" -// Not direclty used in this header, but assumed available in downstream users +// Not directly used in this header, but assumed available in downstream users // like platform/*/export/export.cpp. Could be fixed, but probably better to have // thirdparty includes in as little headers as possible. #include "thirdparty/minizip/unzip.h" diff --git a/core/list.h b/core/list.h index c26aad6463..c46888e01c 100644 --- a/core/list.h +++ b/core/list.h @@ -31,6 +31,7 @@ #ifndef GLOBALS_LIST_H #define GLOBALS_LIST_H +#include "core/error_macros.h" #include "core/os/memory.h" #include "core/sort_array.h" @@ -503,8 +504,7 @@ public: if (p_I->prev_ptr) p_I->prev_ptr->next_ptr = p_I->next_ptr; - if (p_I->next_ptr) - p_I->next_ptr->prev_ptr = p_I->prev_ptr; + p_I->next_ptr->prev_ptr = p_I->prev_ptr; _data->last->next_ptr = p_I; p_I->prev_ptr = _data->last; @@ -538,8 +538,7 @@ public: if (_data->last == p_I) _data->last = p_I->prev_ptr; - if (p_I->prev_ptr) - p_I->prev_ptr->next_ptr = p_I->next_ptr; + p_I->prev_ptr->next_ptr = p_I->next_ptr; if (p_I->next_ptr) p_I->next_ptr->prev_ptr = p_I->prev_ptr; @@ -604,9 +603,6 @@ public: Element *next = current->next_ptr; - //disconnect - current->next_ptr = NULL; - if (from != current) { current->prev_ptr = NULL; @@ -691,6 +687,10 @@ public: memdelete_arr(aux_buffer); } + const void *id() const { + return (void *)_data; + } + /** * copy constructor for the list */ diff --git a/core/make_binders.py b/core/make_binders.py index 4c61b90d99..c38db5cef4 100644 --- a/core/make_binders.py +++ b/core/make_binders.py @@ -9,6 +9,12 @@ public: $ifret R$ $ifnoret void$ (T::*method)($arg, P@$) $ifconst const$; #ifdef DEBUG_METHODS_ENABLED virtual Variant::Type _gen_argument_type(int p_arg) const { return _get_argument_type(p_arg); } + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { + $ifret if (p_arg==-1) return GetTypeInfo<R>::METADATA;$ + $arg if (p_arg==(@-1)) return GetTypeInfo<P@>::METADATA; + $ + return GodotTypeInfo::METADATA_NONE; + } Variant::Type _get_argument_type(int p_argument) const { $ifret if (p_argument==-1) return (Variant::Type)GetTypeInfo<R>::VARIANT_TYPE;$ $arg if (p_argument==(@-1)) return (Variant::Type)GetTypeInfo<P@>::VARIANT_TYPE; @@ -94,6 +100,12 @@ public: #ifdef DEBUG_METHODS_ENABLED virtual Variant::Type _gen_argument_type(int p_arg) const { return _get_argument_type(p_arg); } + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { + $ifret if (p_arg==-1) return GetTypeInfo<R>::METADATA;$ + $arg if (p_arg==(@-1)) return GetTypeInfo<P@>::METADATA; + $ + return GodotTypeInfo::METADATA_NONE; + } Variant::Type _get_argument_type(int p_argument) const { $ifret if (p_argument==-1) return (Variant::Type)GetTypeInfo<R>::VARIANT_TYPE;$ @@ -179,6 +191,96 @@ MethodBind* create_method_bind($ifret R$ $ifnoret void$ (T::*p_method)($arg, P@$ """ +template_typed_free_func = """ +#ifdef TYPED_METHOD_BIND +template<class T $ifret ,class R$ $ifargs ,$ $arg, class P@$> +class FunctionBind$argc$$ifret R$$ifconst C$ : public MethodBind { +public: + + $ifret R$ $ifnoret void$ (*method) ($ifconst const$ T *$ifargs , $$arg, P@$); +#ifdef DEBUG_METHODS_ENABLED + virtual Variant::Type _gen_argument_type(int p_arg) const { return _get_argument_type(p_arg); } + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { + $ifret if (p_arg==-1) return GetTypeInfo<R>::METADATA;$ + $arg if (p_arg==(@-1)) return GetTypeInfo<P@>::METADATA; + $ + return GodotTypeInfo::METADATA_NONE; + } + Variant::Type _get_argument_type(int p_argument) const { + $ifret if (p_argument==-1) return (Variant::Type)GetTypeInfo<R>::VARIANT_TYPE;$ + $arg if (p_argument==(@-1)) return (Variant::Type)GetTypeInfo<P@>::VARIANT_TYPE; + $ + return Variant::NIL; + } + virtual PropertyInfo _gen_argument_type_info(int p_argument) const { + $ifret if (p_argument==-1) return GetTypeInfo<R>::get_class_info();$ + $arg if (p_argument==(@-1)) return GetTypeInfo<P@>::get_class_info(); + $ + return PropertyInfo(); + } +#endif + virtual String get_instance_class() const { + return T::get_class_static(); + } + + virtual Variant call(Object* p_object,const Variant** p_args,int p_arg_count, Variant::CallError& r_error) { + + T *instance=Object::cast_to<T>(p_object); + r_error.error=Variant::CallError::CALL_OK; +#ifdef DEBUG_METHODS_ENABLED + + ERR_FAIL_COND_V(!instance,Variant()); + if (p_arg_count>get_argument_count()) { + r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument=get_argument_count(); + return Variant(); + + } + if (p_arg_count<(get_argument_count()-get_default_argument_count())) { + + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=get_argument_count()-get_default_argument_count(); + return Variant(); + } + $arg CHECK_ARG(@); + $ +#endif + $ifret Variant ret = $(method)(instance$ifargs , $$arg, _VC(@)$); + $ifret return Variant(ret);$ + $ifnoret return Variant();$ + } + +#ifdef PTRCALL_ENABLED + virtual void ptrcall(Object*p_object,const void** p_args,void *r_ret) { + + T *instance=Object::cast_to<T>(p_object); + $ifret PtrToArg<R>::encode( $ (method)(instance$ifargs , $$arg, PtrToArg<P@>::convert(p_args[@-1])$) $ifret ,r_ret)$ ; + } +#endif + FunctionBind$argc$$ifret R$$ifconst C$ () { +#ifdef DEBUG_METHODS_ENABLED + _set_const($ifconst true$$ifnoconst false$); + _generate_argument_types($argc$); +#else + set_argument_count($argc$); +#endif + + $ifret _set_returns(true); $ + }; +}; + +template<class T $ifret ,class R$ $ifargs ,$ $arg, class P@$> +MethodBind* create_method_bind($ifret R$ $ifnoret void$ (*p_method)($ifconst const$ T *$ifargs , $$arg, P@$) ) { + + FunctionBind$argc$$ifret R$$ifconst C$<T $ifret ,R$ $ifargs ,$ $arg, P@$> * a = memnew( (FunctionBind$argc$$ifret R$$ifconst C$<T $ifret ,R$ $ifargs ,$ $arg, P@$>) ); + a->method=p_method; + return a; +} +#endif +""" + + + def make_version(template, nargs, argmax, const, ret): intext = template @@ -232,9 +334,6 @@ def make_version(template, nargs, argmax, const, ret): elif (cmd == "noarg"): for i in range(nargs + 1, argmax + 1): outtext += data.replace("@", str(i)) - elif (cmd == "noarg"): - for i in range(nargs + 1, argmax + 1): - outtext += data.replace("@", str(i)) from_pos = end + 1 @@ -247,6 +346,9 @@ def run(target, source, env): versions_ext = 6 text = "" text_ext = "" + text_free_func = "#ifndef METHOD_BIND_FREE_FUNC_H\n#define METHOD_BIND_FREE_FUNC_H\n" + text_free_func += "\n//including this header file allows method binding to use free functions\n" + text_free_func += "//note that the free function must have a pointer to an instance of the class as its first parameter\n" for i in range(0, versions + 1): @@ -264,12 +366,22 @@ def run(target, source, env): else: text += t + text_free_func += make_version(template_typed_free_func, i, versions, False, False) + text_free_func += make_version(template_typed_free_func, i, versions, False, True) + text_free_func += make_version(template_typed_free_func, i, versions, True, False) + text_free_func += make_version(template_typed_free_func, i, versions, True, True) + + text_free_func += "#endif" + with open(target[0], "w") as f: f.write(text) with open(target[1], "w") as f: f.write(text_ext) + with open(target[2], "w") as f: + f.write(text_free_func) + if __name__ == '__main__': from platform_methods import subprocess_main diff --git a/core/map.h b/core/map.h index a701ba36f7..77e73d70cb 100644 --- a/core/map.h +++ b/core/map.h @@ -31,14 +31,11 @@ #ifndef MAP_H #define MAP_H +#include "core/error_macros.h" #include "core/set.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - // based on the very nice implementation of rb-trees by: -// http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html +// https://web.archive.org/web/20120507164830/http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html template <class K, class V, class C = Comparator<K>, class A = DefaultAllocator> class Map { diff --git a/core/math/SCsub b/core/math/SCsub index 1c5f954470..be438fcfbe 100644 --- a/core/math/SCsub +++ b/core/math/SCsub @@ -2,4 +2,6 @@ Import('env') -env.add_source_files(env.core_sources, "*.cpp") +env_math = env.Clone() + +env_math.add_source_files(env.core_sources, "*.cpp") diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index 6c3b84d49a..bfa8b90344 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -40,7 +40,17 @@ int AStar::get_available_point_id() const { return 1; } - return points.back()->key() + 1; + // calculate our new next available point id if bigger than before or next id already contained in set of points. + if (points.has(last_free_id)) { + int cur_new_id = last_free_id; + while (points.has(cur_new_id)) { + cur_new_id++; + } + int &non_const = const_cast<int &>(last_free_id); + non_const = cur_new_id; + } + + return last_free_id; } void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) { @@ -48,104 +58,165 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) { ERR_FAIL_COND(p_id < 0); ERR_FAIL_COND(p_weight_scale < 1); - if (!points.has(p_id)) { + Point *found_pt; + bool p_exists = points.lookup(p_id, found_pt); + + if (!p_exists) { Point *pt = memnew(Point); pt->id = p_id; pt->pos = p_pos; pt->weight_scale = p_weight_scale; pt->prev_point = NULL; - pt->last_pass = 0; - points[p_id] = pt; + pt->open_pass = 0; + pt->closed_pass = 0; + pt->enabled = true; + points.set(p_id, pt); } else { - points[p_id]->pos = p_pos; - points[p_id]->weight_scale = p_weight_scale; + found_pt->pos = p_pos; + found_pt->weight_scale = p_weight_scale; } } Vector3 AStar::get_point_position(int p_id) const { - ERR_FAIL_COND_V(!points.has(p_id), Vector3()); + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND_V(!p_exists, Vector3()); - return points[p_id]->pos; + return p->pos; } void AStar::set_point_position(int p_id, const Vector3 &p_pos) { - ERR_FAIL_COND(!points.has(p_id)); + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND(!p_exists); - points[p_id]->pos = p_pos; + p->pos = p_pos; } real_t AStar::get_point_weight_scale(int p_id) const { - ERR_FAIL_COND_V(!points.has(p_id), 0); + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND_V(!p_exists, 0); - return points[p_id]->weight_scale; + return p->weight_scale; } void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) { - ERR_FAIL_COND(!points.has(p_id)); + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND(!p_exists); ERR_FAIL_COND(p_weight_scale < 1); - points[p_id]->weight_scale = p_weight_scale; + p->weight_scale = p_weight_scale; } void AStar::remove_point(int p_id) { - ERR_FAIL_COND(!points.has(p_id)); + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND(!p_exists); - Point *p = points[p_id]; + for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) { - Map<int, Point *>::Element *PE = points.front(); - while (PE) { - for (Set<Point *>::Element *E = PE->get()->neighbours.front(); E; E = E->next()) { - Segment s(p_id, E->get()->id); - segments.erase(s); - E->get()->neighbours.erase(p); - } - PE = PE->next(); + Segment s(p_id, (*it.key)); + segments.erase(s); + + (*it.value)->neighbours.remove(p->id); + (*it.value)->unlinked_neighbours.remove(p->id); + } + + for (OAHashMap<int, Point *>::Iterator it = p->unlinked_neighbours.iter(); it.valid; it = p->unlinked_neighbours.next_iter(it)) { + + Segment s(p_id, (*it.key)); + segments.erase(s); + + (*it.value)->neighbours.remove(p->id); + (*it.value)->unlinked_neighbours.remove(p->id); } memdelete(p); - points.erase(p_id); + points.remove(p_id); + last_free_id = p_id; } void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) { - ERR_FAIL_COND(!points.has(p_id)); - ERR_FAIL_COND(!points.has(p_with_id)); ERR_FAIL_COND(p_id == p_with_id); - Point *a = points[p_id]; - Point *b = points[p_with_id]; - a->neighbours.insert(b); + Point *a; + bool from_exists = points.lookup(p_id, a); + ERR_FAIL_COND(!from_exists); - if (bidirectional) - b->neighbours.insert(a); + Point *b; + bool to_exists = points.lookup(p_with_id, b); + ERR_FAIL_COND(!to_exists); - Segment s(p_id, p_with_id); - if (s.from == p_id) { - s.from_point = a; - s.to_point = b; + a->neighbours.set(b->id, b); + + if (bidirectional) { + b->neighbours.set(a->id, a); } else { - s.from_point = b; - s.to_point = a; + b->unlinked_neighbours.set(a->id, a); + } + + Segment s(p_id, p_with_id); + if (bidirectional) s.direction = Segment::BIDIRECTIONAL; + + Set<Segment>::Element *element = segments.find(s); + if (element != NULL) { + s.direction |= element->get().direction; + if (s.direction == Segment::BIDIRECTIONAL) { + // Both are neighbours of each other now + a->unlinked_neighbours.remove(b->id); + b->unlinked_neighbours.remove(a->id); + } + segments.erase(element); } segments.insert(s); } -void AStar::disconnect_points(int p_id, int p_with_id) { - Segment s(p_id, p_with_id); - ERR_FAIL_COND(!segments.has(s)); +void AStar::disconnect_points(int p_id, int p_with_id, bool bidirectional) { + + Point *a; + bool a_exists = points.lookup(p_id, a); + ERR_FAIL_COND(!a_exists); - segments.erase(s); + Point *b; + bool b_exists = points.lookup(p_with_id, b); + ERR_FAIL_COND(!b_exists); - Point *a = points[p_id]; - Point *b = points[p_with_id]; - a->neighbours.erase(b); - b->neighbours.erase(a); + Segment s(p_id, p_with_id); + int remove_direction = bidirectional ? (int)Segment::BIDIRECTIONAL : s.direction; + + Set<Segment>::Element *element = segments.find(s); + if (element != NULL) { + // s is the new segment + // Erase the directions to be removed + s.direction = (element->get().direction & ~remove_direction); + + a->neighbours.remove(b->id); + if (bidirectional) { + b->neighbours.remove(a->id); + if (element->get().direction != Segment::BIDIRECTIONAL) { + a->unlinked_neighbours.remove(b->id); + b->unlinked_neighbours.remove(a->id); + } + } else { + if (s.direction == Segment::NONE) + b->unlinked_neighbours.remove(a->id); + else + a->unlinked_neighbours.set(b->id, b); + } + + segments.erase(element); + if (s.direction != Segment::NONE) + segments.insert(s); + } } bool AStar::has_point(int p_id) const { @@ -157,8 +228,8 @@ Array AStar::get_points() { Array point_list; - for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) { - point_list.push_back(E->key()); + for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) { + point_list.push_back(*(it.key)); } return point_list; @@ -166,46 +237,65 @@ Array AStar::get_points() { PoolVector<int> AStar::get_point_connections(int p_id) { - ERR_FAIL_COND_V(!points.has(p_id), PoolVector<int>()); + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND_V(!p_exists, PoolVector<int>()); PoolVector<int> point_list; - Point *p = points[p_id]; - - for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) { - point_list.push_back(E->get()->id); + for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) { + point_list.push_back((*it.key)); } return point_list; } -bool AStar::are_points_connected(int p_id, int p_with_id) const { +bool AStar::are_points_connected(int p_id, int p_with_id, bool bidirectional) const { Segment s(p_id, p_with_id); - return segments.has(s); + const Set<Segment>::Element *element = segments.find(s); + + return element != NULL && + (bidirectional || (element->get().direction & s.direction) == s.direction); } void AStar::clear() { - for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) { - - memdelete(E->get()); + last_free_id = 0; + for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) { + memdelete(*(it.value)); } segments.clear(); points.clear(); } -int AStar::get_closest_point(const Vector3 &p_point) const { +int AStar::get_point_count() const { + return points.get_num_elements(); +} + +int AStar::get_point_capacity() const { + return points.get_capacity(); +} + +void AStar::reserve_space(int p_num_nodes) { + ERR_FAIL_COND_MSG(p_num_nodes <= 0, "New capacity must be greater than 0, was: " + itos(p_num_nodes) + "."); + ERR_FAIL_COND_MSG((uint32_t)p_num_nodes < points.get_capacity(), "New capacity must be greater than current capacity: " + itos(points.get_capacity()) + ", new was: " + itos(p_num_nodes) + "."); + points.reserve(p_num_nodes); +} + +int AStar::get_closest_point(const Vector3 &p_point, bool p_include_disabled) const { int closest_id = -1; real_t closest_dist = 1e20; - for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) { + for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) { - real_t d = p_point.distance_squared_to(E->get()->pos); + if (!p_include_disabled && !(*it.value)->enabled) continue; // Disabled points should not be considered. + + real_t d = p_point.distance_squared_to((*it.value)->pos); if (closest_id < 0 || d < closest_dist) { closest_dist = d; - closest_id = E->key(); + closest_id = *(it.key); } } @@ -214,15 +304,23 @@ int AStar::get_closest_point(const Vector3 &p_point) const { Vector3 AStar::get_closest_position_in_segment(const Vector3 &p_point) const { - real_t closest_dist = 1e20; bool found = false; + real_t closest_dist = 1e20; Vector3 closest_point; for (const Set<Segment>::Element *E = segments.front(); E; E = E->next()) { + Point *from_point = nullptr, *to_point = nullptr; + points.lookup(E->get().u, from_point); + points.lookup(E->get().v, to_point); + + if (!(from_point->enabled && to_point->enabled)) { + continue; + } + Vector3 segment[2] = { - E->get().from_point->pos, - E->get().to_point->pos, + from_point->pos, + to_point->pos, }; Vector3 p = Geometry::get_closest_point_to_segment(p_point, segment); @@ -242,79 +340,60 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { pass++; - SelfList<Point>::List open_list; + if (!end_point->enabled) return false; bool found_route = false; - for (Set<Point *>::Element *E = begin_point->neighbours.front(); E; E = E->next()) { + Vector<Point *> open_list; + SortArray<Point *, SortPoints> sorter; - Point *n = E->get(); - n->prev_point = begin_point; - n->distance = _compute_cost(begin_point->id, n->id) * n->weight_scale; - n->last_pass = pass; - open_list.add(&n->list); - } + begin_point->g_score = 0; + begin_point->f_score = _estimate_cost(begin_point->id, end_point->id); + open_list.push_back(begin_point); - while (true) { + while (!open_list.empty()) { - if (open_list.first() == NULL) { - // No path found + Point *p = open_list[0]; // The currently processed point + + if (p == end_point) { + found_route = true; break; } - // Check open list - - SelfList<Point> *least_cost_point = open_list.first(); - real_t least_cost = Math_INF; - // TODO: Cache previous results - for (SelfList<Point> *E = open_list.first(); E; E = E->next()) { + sorter.pop_heap(0, open_list.size(), open_list.ptrw()); // Remove the current point from the open list + open_list.remove(open_list.size() - 1); + p->closed_pass = pass; // Mark the point as closed - Point *p = E->self(); + for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) { - real_t cost = p->distance; - cost += _estimate_cost(p->id, end_point->id); + Point *e = *(it.value); // The neighbour point - if (cost < least_cost) { - least_cost_point = E; - least_cost = cost; + if (!e->enabled || e->closed_pass == pass) { + continue; } - } - - Point *p = least_cost_point->self(); - if (p == end_point) { - found_route = true; - break; - } - for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) { + real_t tentative_g_score = p->g_score + _compute_cost(p->id, e->id) * e->weight_scale; - Point *e = E->get(); + bool new_point = false; - real_t distance = _compute_cost(p->id, e->id) * e->weight_scale + p->distance; + if (e->open_pass != pass) { // The point wasn't inside the open list. + e->open_pass = pass; + open_list.push_back(e); + new_point = true; + } else if (tentative_g_score >= e->g_score) { // The new path is worse than the previous. + continue; + } - if (e->last_pass == pass) { - // Already visited, is this cheaper? + e->prev_point = p; + e->g_score = tentative_g_score; + e->f_score = e->g_score + _estimate_cost(e->id, end_point->id); - if (e->distance > distance) { - e->prev_point = p; - e->distance = distance; - } + if (new_point) { // The position of the new points is already known. + sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptrw()); } else { - // Add to open neighbours - - e->prev_point = p; - e->distance = distance; - e->last_pass = pass; // Mark as used - open_list.add(&e->list); + sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptrw()); } } - - open_list.remove(least_cost_point); - } - - // Clear the openf list - while (open_list.first()) { - open_list.remove(open_list.first()); } return found_route; @@ -325,7 +404,15 @@ float AStar::_estimate_cost(int p_from_id, int p_to_id) { if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost)) return get_script_instance()->call(SceneStringNames::get_singleton()->_estimate_cost, p_from_id, p_to_id); - return points[p_from_id]->pos.distance_to(points[p_to_id]->pos); + Point *from_point; + bool from_exists = points.lookup(p_from_id, from_point); + ERR_FAIL_COND_V(!from_exists, 0); + + Point *to_point; + bool to_exists = points.lookup(p_to_id, to_point); + ERR_FAIL_COND_V(!to_exists, 0); + + return from_point->pos.distance_to(to_point->pos); } float AStar::_compute_cost(int p_from_id, int p_to_id) { @@ -333,18 +420,26 @@ float AStar::_compute_cost(int p_from_id, int p_to_id) { if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost)) return get_script_instance()->call(SceneStringNames::get_singleton()->_compute_cost, p_from_id, p_to_id); - return points[p_from_id]->pos.distance_to(points[p_to_id]->pos); + Point *from_point; + bool from_exists = points.lookup(p_from_id, from_point); + ERR_FAIL_COND_V(!from_exists, 0); + + Point *to_point; + bool to_exists = points.lookup(p_to_id, to_point); + ERR_FAIL_COND_V(!to_exists, 0); + + return from_point->pos.distance_to(to_point->pos); } PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) { - ERR_FAIL_COND_V(!points.has(p_from_id), PoolVector<Vector3>()); - ERR_FAIL_COND_V(!points.has(p_to_id), PoolVector<Vector3>()); + Point *a; + bool from_exists = points.lookup(p_from_id, a); + ERR_FAIL_COND_V(!from_exists, PoolVector<Vector3>()); - pass++; - - Point *a = points[p_from_id]; - Point *b = points[p_to_id]; + Point *b; + bool to_exists = points.lookup(p_to_id, b); + ERR_FAIL_COND_V(!to_exists, PoolVector<Vector3>()); if (a == b) { PoolVector<Vector3> ret; @@ -356,11 +451,8 @@ PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) { Point *end_point = b; bool found_route = _solve(begin_point, end_point); + if (!found_route) return PoolVector<Vector3>(); - if (!found_route) - return PoolVector<Vector3>(); - - // Midpoints Point *p = end_point; int pc = 1; // Begin point while (p != begin_point) { @@ -389,13 +481,13 @@ PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) { PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) { - ERR_FAIL_COND_V(!points.has(p_from_id), PoolVector<int>()); - ERR_FAIL_COND_V(!points.has(p_to_id), PoolVector<int>()); - - pass++; + Point *a; + bool from_exists = points.lookup(p_from_id, a); + ERR_FAIL_COND_V(!from_exists, PoolVector<int>()); - Point *a = points[p_from_id]; - Point *b = points[p_to_id]; + Point *b; + bool to_exists = points.lookup(p_to_id, b); + ERR_FAIL_COND_V(!to_exists, PoolVector<int>()); if (a == b) { PoolVector<int> ret; @@ -407,11 +499,8 @@ PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) { Point *end_point = b; bool found_route = _solve(begin_point, end_point); + if (!found_route) return PoolVector<int>(); - if (!found_route) - return PoolVector<int>(); - - // Midpoints Point *p = end_point; int pc = 1; // Begin point while (p != begin_point) { @@ -438,6 +527,24 @@ PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) { return path; } +void AStar::set_point_disabled(int p_id, bool p_disabled) { + + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND(!p_exists); + + p->enabled = !p_disabled; +} + +bool AStar::is_point_disabled(int p_id) const { + + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND_V(!p_exists, false); + + return !p->enabled; +} + void AStar::_bind_methods() { ClassDB::bind_method(D_METHOD("get_available_point_id"), &AStar::get_available_point_id); @@ -448,17 +555,22 @@ void AStar::_bind_methods() { ClassDB::bind_method(D_METHOD("set_point_weight_scale", "id", "weight_scale"), &AStar::set_point_weight_scale); ClassDB::bind_method(D_METHOD("remove_point", "id"), &AStar::remove_point); ClassDB::bind_method(D_METHOD("has_point", "id"), &AStar::has_point); + ClassDB::bind_method(D_METHOD("get_point_connections", "id"), &AStar::get_point_connections); ClassDB::bind_method(D_METHOD("get_points"), &AStar::get_points); - ClassDB::bind_method(D_METHOD("get_point_connections", "id"), &AStar::get_point_connections); + ClassDB::bind_method(D_METHOD("set_point_disabled", "id", "disabled"), &AStar::set_point_disabled, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("is_point_disabled", "id"), &AStar::is_point_disabled); ClassDB::bind_method(D_METHOD("connect_points", "id", "to_id", "bidirectional"), &AStar::connect_points, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("disconnect_points", "id", "to_id"), &AStar::disconnect_points); - ClassDB::bind_method(D_METHOD("are_points_connected", "id", "to_id"), &AStar::are_points_connected); + ClassDB::bind_method(D_METHOD("disconnect_points", "id", "to_id", "bidirectional"), &AStar::disconnect_points, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("are_points_connected", "id", "to_id", "bidirectional"), &AStar::are_points_connected, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_point_count"), &AStar::get_point_count); + ClassDB::bind_method(D_METHOD("get_point_capacity"), &AStar::get_point_capacity); + ClassDB::bind_method(D_METHOD("reserve_space", "num_nodes"), &AStar::reserve_space); ClassDB::bind_method(D_METHOD("clear"), &AStar::clear); - ClassDB::bind_method(D_METHOD("get_closest_point", "to_position"), &AStar::get_closest_point); + ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar::get_closest_point, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar::get_closest_position_in_segment); ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar::get_point_path); @@ -469,12 +581,157 @@ void AStar::_bind_methods() { } AStar::AStar() { - + last_free_id = 0; pass = 1; } AStar::~AStar() { - - pass = 1; clear(); } + +///////////////////////////////////////////////////////////// + +int AStar2D::get_available_point_id() const { + return astar.get_available_point_id(); +} + +void AStar2D::add_point(int p_id, const Vector2 &p_pos, real_t p_weight_scale) { + astar.add_point(p_id, Vector3(p_pos.x, p_pos.y, 0), p_weight_scale); +} + +Vector2 AStar2D::get_point_position(int p_id) const { + Vector3 p = astar.get_point_position(p_id); + return Vector2(p.x, p.y); +} + +void AStar2D::set_point_position(int p_id, const Vector2 &p_pos) { + astar.set_point_position(p_id, Vector3(p_pos.x, p_pos.y, 0)); +} + +real_t AStar2D::get_point_weight_scale(int p_id) const { + return astar.get_point_weight_scale(p_id); +} + +void AStar2D::set_point_weight_scale(int p_id, real_t p_weight_scale) { + astar.set_point_weight_scale(p_id, p_weight_scale); +} + +void AStar2D::remove_point(int p_id) { + astar.remove_point(p_id); +} + +bool AStar2D::has_point(int p_id) const { + return astar.has_point(p_id); +} + +PoolVector<int> AStar2D::get_point_connections(int p_id) { + return astar.get_point_connections(p_id); +} + +Array AStar2D::get_points() { + return astar.get_points(); +} + +void AStar2D::set_point_disabled(int p_id, bool p_disabled) { + astar.set_point_disabled(p_id, p_disabled); +} + +bool AStar2D::is_point_disabled(int p_id) const { + return astar.is_point_disabled(p_id); +} + +void AStar2D::connect_points(int p_id, int p_with_id, bool p_bidirectional) { + astar.connect_points(p_id, p_with_id, p_bidirectional); +} + +void AStar2D::disconnect_points(int p_id, int p_with_id) { + astar.disconnect_points(p_id, p_with_id); +} + +bool AStar2D::are_points_connected(int p_id, int p_with_id) const { + return astar.are_points_connected(p_id, p_with_id); +} + +int AStar2D::get_point_count() const { + return astar.get_point_count(); +} + +int AStar2D::get_point_capacity() const { + return astar.get_point_capacity(); +} + +void AStar2D::clear() { + astar.clear(); +} + +void AStar2D::reserve_space(int p_num_nodes) { + astar.reserve_space(p_num_nodes); +} + +int AStar2D::get_closest_point(const Vector2 &p_point, bool p_include_disabled) const { + return astar.get_closest_point(Vector3(p_point.x, p_point.y, 0), p_include_disabled); +} + +Vector2 AStar2D::get_closest_position_in_segment(const Vector2 &p_point) const { + Vector3 p = astar.get_closest_position_in_segment(Vector3(p_point.x, p_point.y, 0)); + return Vector2(p.x, p.y); +} + +PoolVector<Vector2> AStar2D::get_point_path(int p_from_id, int p_to_id) { + + PoolVector3Array pv = astar.get_point_path(p_from_id, p_to_id); + int size = pv.size(); + PoolVector2Array path; + path.resize(size); + { + PoolVector<Vector3>::Read r = pv.read(); + PoolVector<Vector2>::Write w = path.write(); + for (int i = 0; i < size; i++) { + Vector3 p = r[i]; + w[i] = Vector2(p.x, p.y); + } + } + return path; +} + +PoolVector<int> AStar2D::get_id_path(int p_from_id, int p_to_id) { + return astar.get_id_path(p_from_id, p_to_id); +} + +void AStar2D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("get_available_point_id"), &AStar2D::get_available_point_id); + ClassDB::bind_method(D_METHOD("add_point", "id", "position", "weight_scale"), &AStar2D::add_point, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStar2D::get_point_position); + ClassDB::bind_method(D_METHOD("set_point_position", "id", "position"), &AStar2D::set_point_position); + ClassDB::bind_method(D_METHOD("get_point_weight_scale", "id"), &AStar2D::get_point_weight_scale); + ClassDB::bind_method(D_METHOD("set_point_weight_scale", "id", "weight_scale"), &AStar2D::set_point_weight_scale); + ClassDB::bind_method(D_METHOD("remove_point", "id"), &AStar2D::remove_point); + ClassDB::bind_method(D_METHOD("has_point", "id"), &AStar2D::has_point); + ClassDB::bind_method(D_METHOD("get_point_connections", "id"), &AStar2D::get_point_connections); + ClassDB::bind_method(D_METHOD("get_points"), &AStar2D::get_points); + + ClassDB::bind_method(D_METHOD("set_point_disabled", "id", "disabled"), &AStar2D::set_point_disabled, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("is_point_disabled", "id"), &AStar2D::is_point_disabled); + + ClassDB::bind_method(D_METHOD("connect_points", "id", "to_id", "bidirectional"), &AStar2D::connect_points, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("disconnect_points", "id", "to_id"), &AStar2D::disconnect_points); + ClassDB::bind_method(D_METHOD("are_points_connected", "id", "to_id"), &AStar2D::are_points_connected); + + ClassDB::bind_method(D_METHOD("get_point_count"), &AStar2D::get_point_count); + ClassDB::bind_method(D_METHOD("get_point_capacity"), &AStar2D::get_point_capacity); + ClassDB::bind_method(D_METHOD("reserve_space", "num_nodes"), &AStar2D::reserve_space); + ClassDB::bind_method(D_METHOD("clear"), &AStar2D::clear); + + ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar2D::get_closest_point, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar2D::get_closest_position_in_segment); + + ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar2D::get_point_path); + ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar2D::get_id_path); +} + +AStar2D::AStar2D() { +} + +AStar2D::~AStar2D() { +} diff --git a/core/math/a_star.h b/core/math/a_star.h index d094bc4863..8ff62e646b 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -31,8 +31,8 @@ #ifndef ASTAR_H #define ASTAR_H +#include "core/oa_hash_map.h" #include "core/reference.h" -#include "core/self_list.h" /** A* pathfinding algorithm @@ -42,55 +42,81 @@ class AStar : public Reference { - GDCLASS(AStar, Reference) - - uint64_t pass; + GDCLASS(AStar, Reference); struct Point { - SelfList<Point> list; + Point() : + neighbours(4u), + unlinked_neighbours(4u) {} int id; Vector3 pos; real_t weight_scale; - uint64_t last_pass; + bool enabled; - Set<Point *> neighbours; + OAHashMap<int, Point *> neighbours; + OAHashMap<int, Point *> unlinked_neighbours; - // Used for pathfinding + // Used for pathfinding. Point *prev_point; - real_t distance; - - Point() : - list(this) {} + real_t g_score; + real_t f_score; + uint64_t open_pass; + uint64_t closed_pass; }; - Map<int, Point *> points; + struct SortPoints { + _FORCE_INLINE_ bool operator()(const Point *A, const Point *B) const { // Returns true when the Point A is worse than Point B. + if (A->f_score > B->f_score) { + return true; + } else if (A->f_score < B->f_score) { + return false; + } else { + return A->g_score < B->g_score; // If the f_costs are the same then prioritize the points that are further away from the start. + } + } + }; struct Segment { union { struct { - int32_t from; - int32_t to; + int32_t u; + int32_t v; }; uint64_t key; }; - Point *from_point; - Point *to_point; + enum { + NONE = 0, + FORWARD = 1, + BACKWARD = 2, + BIDIRECTIONAL = FORWARD | BACKWARD + }; + unsigned char direction; bool operator<(const Segment &p_s) const { return key < p_s.key; } - Segment() { key = 0; } + Segment() { + key = 0; + direction = NONE; + } Segment(int p_from, int p_to) { - if (p_from > p_to) { - SWAP(p_from, p_to); + if (p_from < p_to) { + u = p_from; + v = p_to; + direction = FORWARD; + } else { + u = p_to; + v = p_from; + direction = BACKWARD; } - - from = p_from; - to = p_to; } }; + int last_free_id; + uint64_t pass; + + OAHashMap<int, Point *> points; Set<Segment> segments; bool _solve(Point *begin_point, Point *end_point); @@ -114,13 +140,19 @@ public: PoolVector<int> get_point_connections(int p_id); Array get_points(); + void set_point_disabled(int p_id, bool p_disabled = true); + bool is_point_disabled(int p_id) const; + void connect_points(int p_id, int p_with_id, bool bidirectional = true); - void disconnect_points(int p_id, int p_with_id); - bool are_points_connected(int p_id, int p_with_id) const; + void disconnect_points(int p_id, int p_with_id, bool bidirectional = true); + bool are_points_connected(int p_id, int p_with_id, bool bidirectional = true) const; + int get_point_count() const; + int get_point_capacity() const; + void reserve_space(int p_num_nodes); void clear(); - int get_closest_point(const Vector3 &p_point) const; + int get_closest_point(const Vector3 &p_point, bool p_include_disabled = false) const; Vector3 get_closest_position_in_segment(const Vector3 &p_point) const; PoolVector<Vector3> get_point_path(int p_from_id, int p_to_id); @@ -130,4 +162,46 @@ public: ~AStar(); }; +class AStar2D : public Reference { + GDCLASS(AStar2D, Reference); + AStar astar; + +protected: + static void _bind_methods(); + +public: + int get_available_point_id() const; + + void add_point(int p_id, const Vector2 &p_pos, real_t p_weight_scale = 1); + Vector2 get_point_position(int p_id) const; + void set_point_position(int p_id, const Vector2 &p_pos); + real_t get_point_weight_scale(int p_id) const; + void set_point_weight_scale(int p_id, real_t p_weight_scale); + void remove_point(int p_id); + bool has_point(int p_id) const; + PoolVector<int> get_point_connections(int p_id); + Array get_points(); + + void set_point_disabled(int p_id, bool p_disabled = true); + bool is_point_disabled(int p_id) const; + + void connect_points(int p_id, int p_with_id, bool p_bidirectional = true); + void disconnect_points(int p_id, int p_with_id); + bool are_points_connected(int p_id, int p_with_id) const; + + int get_point_count() const; + int get_point_capacity() const; + void reserve_space(int p_num_nodes); + void clear(); + + int get_closest_point(const Vector2 &p_point, bool p_include_disabled = false) const; + Vector2 get_closest_position_in_segment(const Vector2 &p_point) const; + + PoolVector<Vector2> get_point_path(int p_from_id, int p_to_id); + PoolVector<int> get_id_path(int p_from_id, int p_to_id); + + AStar2D(); + ~AStar2D(); +}; + #endif // ASTAR_H diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index a4eb1fe2a5..27da061274 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -69,6 +69,11 @@ void AABB::merge_with(const AABB &p_aabb) { size = max - min; } +bool AABB::is_equal_approx(const AABB &p_aabb) const { + + return position.is_equal_approx(p_aabb.position) && size.is_equal_approx(p_aabb.size); +} + AABB AABB::intersection(const AABB &p_aabb) const { Vector3 src_min = position; diff --git a/core/math/aabb.h b/core/math/aabb.h index 52e5ed3626..c3ce33c6f4 100644 --- a/core/math/aabb.h +++ b/core/math/aabb.h @@ -64,6 +64,7 @@ public: bool operator==(const AABB &p_rval) const; bool operator!=(const AABB &p_rval) const; + bool is_equal_approx(const AABB &p_aabb) const; _FORCE_INLINE_ bool intersects(const AABB &p_aabb) const; /// Both AABBs overlap _FORCE_INLINE_ bool intersects_inclusive(const AABB &p_aabb) const; /// Both AABBs (or their faces) overlap _FORCE_INLINE_ bool encloses(const AABB &p_aabb) const; /// p_aabb is completely inside this diff --git a/core/math/audio_frame.h b/core/math/audio_frame.h index ebe0356c93..98e4e33021 100644 --- a/core/math/audio_frame.h +++ b/core/math/audio_frame.h @@ -31,6 +31,7 @@ #ifndef AUDIOFRAME_H #define AUDIOFRAME_H +#include "core/math/vector2.h" #include "core/typedefs.h" static inline float undenormalise(volatile float f) { @@ -128,6 +129,14 @@ struct AudioFrame { return *this; } + _ALWAYS_INLINE_ operator Vector2() const { + return Vector2(l, r); + } + + _ALWAYS_INLINE_ AudioFrame(const Vector2 &p_v2) { + l = p_v2.x; + r = p_v2.y; + } _ALWAYS_INLINE_ AudioFrame() {} }; diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 82b2f7006d..09d04a8a2a 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -106,17 +106,17 @@ Basis Basis::orthonormalized() const { } bool Basis::is_orthogonal() const { - Basis id; + Basis identity; Basis m = (*this) * transposed(); - return is_equal_approx(id, m); + return m.is_equal_approx(identity); } bool Basis::is_diagonal() const { return ( - Math::is_equal_approx(elements[0][1], 0) && Math::is_equal_approx(elements[0][2], 0) && - Math::is_equal_approx(elements[1][0], 0) && Math::is_equal_approx(elements[1][2], 0) && - Math::is_equal_approx(elements[2][0], 0) && Math::is_equal_approx(elements[2][1], 0)); + Math::is_zero_approx(elements[0][1]) && Math::is_zero_approx(elements[0][2]) && + Math::is_zero_approx(elements[1][0]) && Math::is_zero_approx(elements[1][2]) && + Math::is_zero_approx(elements[2][0]) && Math::is_zero_approx(elements[2][1])); } bool Basis::is_rotation() const { @@ -557,19 +557,12 @@ void Basis::set_euler_yxz(const Vector3 &p_euler) { *this = ymat * xmat * zmat; } -bool Basis::is_equal_approx(const Basis &a, const Basis &b,real_t p_epsilon) const { +bool Basis::is_equal_approx(const Basis &p_basis) const { - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - if (!Math::is_equal_approx(a.elements[i][j], b.elements[i][j], p_epsilon)) - return false; - } - } - - return true; + return elements[0].is_equal_approx(p_basis.elements[0]) && elements[1].is_equal_approx(p_basis.elements[1]) && elements[2].is_equal_approx(p_basis.elements[2]); } -bool Basis::is_equal_approx_ratio(const Basis &a, const Basis &b,real_t p_epsilon) const { +bool Basis::is_equal_approx_ratio(const Basis &a, const Basis &b, real_t p_epsilon) const { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { @@ -618,10 +611,7 @@ Basis::operator String() const { Quat Basis::get_quat() const { #ifdef MATH_CHECKS - if (!is_rotation()) { - ERR_EXPLAIN("Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quat() or call orthonormalized() instead."); - ERR_FAIL_V(Quat()); - } + ERR_FAIL_COND_V_MSG(!is_rotation(), Quat(), "Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quat() or call orthonormalized() instead."); #endif /* Allow getting a quaternion from an unnormalized transform */ Basis m = *this; @@ -749,8 +739,8 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { if ((xx > yy) && (xx > zz)) { // elements[0][0] is the largest diagonal term if (xx < epsilon) { x = 0; - y = 0.7071; - z = 0.7071; + y = Math_SQRT12; + z = Math_SQRT12; } else { x = Math::sqrt(xx); y = xy / x; @@ -758,9 +748,9 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { } } else if (yy > zz) { // elements[1][1] is the largest diagonal term if (yy < epsilon) { - x = 0.7071; + x = Math_SQRT12; y = 0; - z = 0.7071; + z = Math_SQRT12; } else { y = Math::sqrt(yy); x = xy / y; @@ -768,8 +758,8 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { } } else { // elements[2][2] is the largest diagonal term so base result on this if (zz < epsilon) { - x = 0.7071; - y = 0.7071; + x = Math_SQRT12; + y = Math_SQRT12; z = 0; } else { z = Math::sqrt(zz); @@ -810,24 +800,31 @@ void Basis::set_quat(const Quat &p_quat) { void Basis::set_axis_angle(const Vector3 &p_axis, real_t p_phi) { // Rotation matrix from axis and angle, see https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_angle #ifdef MATH_CHECKS - ERR_FAIL_COND(!p_axis.is_normalized()); + ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "Axis must be normalized."); #endif Vector3 axis_sq(p_axis.x * p_axis.x, p_axis.y * p_axis.y, p_axis.z * p_axis.z); - real_t cosine = Math::cos(p_phi); + elements[0][0] = axis_sq.x + cosine * (1.0 - axis_sq.x); + elements[1][1] = axis_sq.y + cosine * (1.0 - axis_sq.y); + elements[2][2] = axis_sq.z + cosine * (1.0 - axis_sq.z); + real_t sine = Math::sin(p_phi); + real_t t = 1 - cosine; - elements[0][0] = axis_sq.x + cosine * (1.0 - axis_sq.x); - elements[0][1] = p_axis.x * p_axis.y * (1.0 - cosine) - p_axis.z * sine; - elements[0][2] = p_axis.z * p_axis.x * (1.0 - cosine) + p_axis.y * sine; + real_t xyzt = p_axis.x * p_axis.y * t; + real_t zyxs = p_axis.z * sine; + elements[0][1] = xyzt - zyxs; + elements[1][0] = xyzt + zyxs; - elements[1][0] = p_axis.x * p_axis.y * (1.0 - cosine) + p_axis.z * sine; - elements[1][1] = axis_sq.y + cosine * (1.0 - axis_sq.y); - elements[1][2] = p_axis.y * p_axis.z * (1.0 - cosine) - p_axis.x * sine; + xyzt = p_axis.x * p_axis.z * t; + zyxs = p_axis.y * sine; + elements[0][2] = xyzt + zyxs; + elements[2][0] = xyzt - zyxs; - elements[2][0] = p_axis.z * p_axis.x * (1.0 - cosine) - p_axis.y * sine; - elements[2][1] = p_axis.y * p_axis.z * (1.0 - cosine) + p_axis.x * sine; - elements[2][2] = axis_sq.z + cosine * (1.0 - axis_sq.z); + xyzt = p_axis.y * p_axis.z * t; + zyxs = p_axis.x * sine; + elements[1][2] = xyzt - zyxs; + elements[2][1] = xyzt + zyxs; } void Basis::set_axis_angle_scale(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { @@ -845,7 +842,7 @@ void Basis::set_quat_scale(const Quat &p_quat, const Vector3 &p_scale) { rotate(p_quat); } -void Basis::set_diagonal(const Vector3 p_diag) { +void Basis::set_diagonal(const Vector3 &p_diag) { elements[0][0] = p_diag.x; elements[0][1] = 0; elements[0][2] = 0; diff --git a/core/math/basis.h b/core/math/basis.h index aa0ddb280f..9b2e38b3d3 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -28,17 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -// Circular dependency between Vector3 and Basis :/ -#include "core/math/vector3.h" - #ifndef BASIS_H #define BASIS_H #include "core/math/quat.h" - -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ +#include "core/math/vector3.h" class Basis { public: @@ -133,8 +127,10 @@ public: return elements[0][2] * v[0] + elements[1][2] * v[1] + elements[2][2] * v[2]; } - bool is_equal_approx(const Basis &a, const Basis &b, real_t p_epsilon=CMP_EPSILON) const; - bool is_equal_approx_ratio(const Basis &a, const Basis &b, real_t p_epsilon=UNIT_EPSILON) const; + bool is_equal_approx(const Basis &p_basis) const; + // TODO: Break compatibility in 4.0 by getting rid of this so that it's only an instance method. See also TODO in variant_call.cpp + bool is_equal_approx(const Basis &a, const Basis &b) const { return a.is_equal_approx(b); } + bool is_equal_approx_ratio(const Basis &a, const Basis &b, real_t p_epsilon = UNIT_EPSILON) const; bool operator==(const Basis &p_matrix) const; bool operator!=(const Basis &p_matrix) const; @@ -153,7 +149,7 @@ public: int get_orthogonal_index() const; void set_orthogonal_index(int p_index); - void set_diagonal(const Vector3 p_diag); + void set_diagonal(const Vector3 &p_diag); bool is_orthogonal() const; bool is_diagonal() const; diff --git a/core/math/bsp_tree.cpp b/core/math/bsp_tree.cpp index d7e6e82cd9..f1d4505c35 100644 --- a/core/math/bsp_tree.cpp +++ b/core/math/bsp_tree.cpp @@ -142,7 +142,7 @@ int BSP_Tree::_get_points_inside(int p_node, const Vector3 *p_points, int *p_ind } return _get_points_inside(node->over, p_points, p_indices, p_center, p_half_extents, p_indices_count); - } else if (dist_min <= 0) { //all points behind plane + } else { //all points behind plane if (node->under == UNDER_LEAF) { @@ -150,8 +150,6 @@ int BSP_Tree::_get_points_inside(int p_node, const Vector3 *p_points, int *p_ind } return _get_points_inside(node->under, p_points, p_indices, p_center, p_half_extents, p_indices_count); } - - return 0; } int BSP_Tree::get_points_inside(const Vector3 *p_points, int p_point_count) const { @@ -194,14 +192,14 @@ int BSP_Tree::get_points_inside(const Vector3 *p_points, int p_point_count) cons #ifdef DEBUG_ENABLED int plane_count = planes.size(); uint16_t plane = nodesptr[idx].plane; - ERR_FAIL_INDEX_V(plane, plane_count, false); + ERR_FAIL_UNSIGNED_INDEX_V(plane, plane_count, 0); #endif idx = planesptr[nodesptr[idx].plane].is_point_over(point) ? nodes[idx].over : nodes[idx].under; #ifdef DEBUG_ENABLED - ERR_FAIL_COND_V(idx < MAX_NODES && idx >= node_count, false); + ERR_FAIL_COND_V(idx < MAX_NODES && idx >= node_count, 0); #endif } @@ -260,7 +258,7 @@ bool BSP_Tree::point_is_inside(const Vector3 &p_point) const { #ifdef DEBUG_ENABLED int plane_count = planes.size(); uint16_t plane = nodesptr[idx].plane; - ERR_FAIL_INDEX_V(plane, plane_count, false); + ERR_FAIL_UNSIGNED_INDEX_V(plane, plane_count, false); #endif bool over = planesptr[nodesptr[idx].plane].is_point_over(p_point); @@ -271,8 +269,6 @@ bool BSP_Tree::point_is_inside(const Vector3 &p_point) const { ERR_FAIL_COND_V(idx < MAX_NODES && idx >= node_count, false); #endif } - - return false; } static int _bsp_find_best_half_plane(const Face3 *p_faces, const Vector<int> &p_indices, real_t p_tolerance) { @@ -368,7 +364,7 @@ static int _bsp_create_node(const Face3 *p_faces, const Vector<int> &p_indices, const Face3 &f = p_faces[indices[i]]; /* - if (f.get_plane().is_almost_like(divisor_plane)) + if (f.get_plane().is_equal_approx(divisor_plane)) continue; */ @@ -416,7 +412,7 @@ static int _bsp_create_node(const Face3 *p_faces, const Vector<int> &p_indices, for (int i = 0; i < p_planes.size(); i++) { - if (p_planes[i].is_almost_like(divisor_plane)) { + if (p_planes[i].is_equal_approx(divisor_plane)) { divisor_plane_idx = i; break; } diff --git a/core/math/bsp_tree.h b/core/math/bsp_tree.h index a7a3697990..90b5e8322a 100644 --- a/core/math/bsp_tree.h +++ b/core/math/bsp_tree.h @@ -38,9 +38,7 @@ #include "core/pool_vector.h" #include "core/variant.h" #include "core/vector.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ + class BSP_Tree { public: enum { diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp index caf08c7379..b9b0f4ac54 100644 --- a/core/math/camera_matrix.cpp +++ b/core/math/camera_matrix.cpp @@ -183,6 +183,10 @@ void CameraMatrix::set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear void CameraMatrix::set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far) { + ERR_FAIL_COND(p_right <= p_left); + ERR_FAIL_COND(p_top <= p_bottom); + ERR_FAIL_COND(p_far <= p_near); + real_t *te = &matrix[0][0]; real_t x = 2 * p_near / (p_right - p_left); real_t y = 2 * p_near / (p_top - p_bottom); @@ -210,6 +214,14 @@ void CameraMatrix::set_frustum(real_t p_left, real_t p_right, real_t p_bottom, r te[15] = 0; } +void CameraMatrix::set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov) { + if (!p_flip_fov) { + p_size *= p_aspect; + } + + set_frustum(-p_size / 2 + p_offset.x, +p_size / 2 + p_offset.x, -p_size / p_aspect / 2 + p_offset.y, +p_size / p_aspect / 2 + p_offset.y, p_near, p_far); +} + real_t CameraMatrix::get_z_far() const { const real_t *matrix = (const real_t *)this->matrix; @@ -294,8 +306,8 @@ Vector<Plane> CameraMatrix::get_projection_planes(const Transform &p_transform) /** Fast Plane Extraction from combined modelview/projection matrices. * References: - * http://www.markmorley.com/opengl/frustumculling.html - * http://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf + * https://web.archive.org/web/20011221205252/http://www.markmorley.com/opengl/frustumculling.html + * https://web.archive.org/web/20061020020112/http://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf */ Vector<Plane> planes; @@ -499,21 +511,21 @@ void CameraMatrix::set_light_bias() { real_t *m = &matrix[0][0]; - m[0] = 0.5, - m[1] = 0.0, - m[2] = 0.0, - m[3] = 0.0, - m[4] = 0.0, - m[5] = 0.5, - 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.5, - m[13] = 0.5, - m[14] = 0.5, + m[0] = 0.5; + m[1] = 0.0; + m[2] = 0.0; + m[3] = 0.0; + m[4] = 0.0; + m[5] = 0.5; + 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.5; + m[13] = 0.5; + m[14] = 0.5; m[15] = 1.0; } @@ -521,21 +533,21 @@ void CameraMatrix::set_light_atlas_rect(const Rect2 &p_rect) { real_t *m = &matrix[0][0]; - m[0] = p_rect.size.width, - m[1] = 0.0, - m[2] = 0.0, - m[3] = 0.0, - m[4] = 0.0, - m[5] = p_rect.size.height, - m[6] = 0.0, - m[7] = 0.0, - m[8] = 0.0, - m[9] = 0.0, - m[10] = 1.0, - m[11] = 0.0, - m[12] = p_rect.position.x, - m[13] = p_rect.position.y, - m[14] = 0.0, + m[0] = p_rect.size.width; + m[1] = 0.0; + m[2] = 0.0; + m[3] = 0.0; + m[4] = 0.0; + m[5] = p_rect.size.height; + m[6] = 0.0; + m[7] = 0.0; + m[8] = 0.0; + m[9] = 0.0; + m[10] = 1.0; + m[11] = 0.0; + m[12] = p_rect.position.x; + m[13] = p_rect.position.y; + m[14] = 0.0; m[15] = 1.0; } diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h index 015588a8cb..63cc88553d 100644 --- a/core/math/camera_matrix.h +++ b/core/math/camera_matrix.h @@ -34,10 +34,6 @@ #include "core/math/rect2.h" #include "core/math/transform.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - struct CameraMatrix { enum Planes { @@ -61,6 +57,7 @@ struct CameraMatrix { void set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar); void set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov = false); void set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far); + void set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false); static real_t get_fovy(real_t p_fovx, real_t p_aspect) { diff --git a/core/math/delaunay.h b/core/math/delaunay.h index bd0cf97937..89a34de082 100644 --- a/core/math/delaunay.h +++ b/core/math/delaunay.h @@ -80,11 +80,11 @@ public: } static bool edge_compare(const Vector<Vector2> &p_vertices, const Edge &p_a, const Edge &p_b) { - if (p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[0]]) < CMP_EPSILON && p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[1]]) < CMP_EPSILON) { + if (p_vertices[p_a.edge[0]].is_equal_approx(p_vertices[p_b.edge[0]]) && p_vertices[p_a.edge[1]].is_equal_approx(p_vertices[p_b.edge[1]])) { return true; } - if (p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[1]]) < CMP_EPSILON && p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[0]]) < CMP_EPSILON) { + if (p_vertices[p_a.edge[0]].is_equal_approx(p_vertices[p_b.edge[1]]) && p_vertices[p_a.edge[1]].is_equal_approx(p_vertices[p_b.edge[0]])) { return true; } diff --git a/core/math/disjoint_set.cpp b/core/math/disjoint_set.cpp new file mode 100644 index 0000000000..c9d47aa0ae --- /dev/null +++ b/core/math/disjoint_set.cpp @@ -0,0 +1,31 @@ +/*************************************************************************/ +/* disjoint_set.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "disjoint_set.h" diff --git a/core/math/disjoint_set.h b/core/math/disjoint_set.h new file mode 100644 index 0000000000..c9b3d0b65d --- /dev/null +++ b/core/math/disjoint_set.h @@ -0,0 +1,155 @@ +/*************************************************************************/ +/* disjoint_set.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 DISJOINT_SET_H +#define DISJOINT_SET_H + +#include "core/map.h" +#include "core/vector.h" + +/** + @author Marios Staikopoulos <marios@staik.net> +*/ + +/* This DisjointSet class uses Find with path compression and Union by rank */ +template <typename T, class C = Comparator<T>, class AL = DefaultAllocator> +class DisjointSet { + + struct Element { + T object; + Element *parent = nullptr; + int rank = 0; + }; + + typedef Map<T, Element *, C, AL> MapT; + + MapT elements; + + Element *get_parent(Element *element); + + _FORCE_INLINE_ Element *insert_or_get(T object); + +public: + ~DisjointSet(); + + _FORCE_INLINE_ void insert(T object) { (void)insert_or_get(object); } + + void create_union(T a, T b); + + void get_representatives(Vector<T> &out_roots); + + void get_members(Vector<T> &out_members, T representative); +}; + +/* FUNCTIONS */ + +template <typename T, class C, class AL> +DisjointSet<T, C, AL>::~DisjointSet() { + for (typename MapT::Element *itr = elements.front(); itr != nullptr; itr = itr->next()) { + memdelete_allocator<Element, AL>(itr->value()); + } +} + +template <typename T, class C, class AL> +typename DisjointSet<T, C, AL>::Element *DisjointSet<T, C, AL>::get_parent(Element *element) { + if (element->parent != element) { + element->parent = get_parent(element->parent); + } + + return element->parent; +} + +template <typename T, class C, class AL> +typename DisjointSet<T, C, AL>::Element *DisjointSet<T, C, AL>::insert_or_get(T object) { + typename MapT::Element *itr = elements.find(object); + if (itr != nullptr) { + return itr->value(); + } + + Element *new_element = memnew_allocator(Element, AL); + new_element->object = object; + new_element->parent = new_element; + elements.insert(object, new_element); + + return new_element; +} + +template <typename T, class C, class AL> +void DisjointSet<T, C, AL>::create_union(T a, T b) { + + Element *x = insert_or_get(a); + Element *y = insert_or_get(b); + + Element *x_root = get_parent(x); + Element *y_root = get_parent(y); + + // Already in the same set + if (x_root == y_root) + return; + + // Not in the same set, merge + if (x_root->rank < y_root->rank) { + SWAP(x_root, y_root); + } + + // Merge y_root into x_root + y_root->parent = x_root; + if (x_root->rank == y_root->rank) { + ++x_root->rank; + } +} + +template <typename T, class C, class AL> +void DisjointSet<T, C, AL>::get_representatives(Vector<T> &out_representatives) { + for (typename MapT::Element *itr = elements.front(); itr != nullptr; itr = itr->next()) { + Element *element = itr->value(); + if (element->parent == element) { + out_representatives.push_back(element->object); + } + } +} + +template <typename T, class C, class AL> +void DisjointSet<T, C, AL>::get_members(Vector<T> &out_members, T representative) { + typename MapT::Element *rep_itr = elements.find(representative); + ERR_FAIL_COND(rep_itr == nullptr); + + Element *rep_element = rep_itr->value(); + ERR_FAIL_COND(rep_element->parent != rep_element); + + for (typename MapT::Element *itr = elements.front(); itr != nullptr; itr = itr->next()) { + Element *parent = get_parent(itr->value()); + if (parent == rep_element) { + out_members.push_back(itr->key()); + } + } +} + +#endif diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 708054e4ab..46f81ce5c3 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -52,6 +52,7 @@ const char *Expression::func_name[Expression::FUNC_MAX] = { "sqrt", "fmod", "fposmod", + "posmod", "floor", "ceil", "round", @@ -64,10 +65,14 @@ const char *Expression::func_name[Expression::FUNC_MAX] = { "is_inf", "ease", "decimals", + "step_decimals", "stepify", "lerp", + "lerp_angle", "inverse_lerp", "range_lerp", + "smoothstep", + "move_toward", "dectime", "randomize", "randi", @@ -148,6 +153,7 @@ int Expression::get_func_argument_count(BuiltinFunc p_func) { case MATH_ISNAN: case MATH_ISINF: case MATH_DECIMALS: + case MATH_STEP_DECIMALS: case MATH_SEED: case MATH_RANDSEED: case MATH_DEG2RAD: @@ -171,6 +177,7 @@ int Expression::get_func_argument_count(BuiltinFunc p_func) { case MATH_ATAN2: case MATH_FMOD: case MATH_FPOSMOD: + case MATH_POSMOD: case MATH_POW: case MATH_EASE: case MATH_STEPIFY: @@ -184,7 +191,10 @@ int Expression::get_func_argument_count(BuiltinFunc p_func) { case COLORN: return 2; case MATH_LERP: + case MATH_LERP_ANGLE: case MATH_INVERSE_LERP: + case MATH_SMOOTHSTEP: + case MATH_MOVE_TOWARD: case MATH_DECTIME: case MATH_WRAP: case MATH_WRAPF: @@ -277,6 +287,12 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant VALIDATE_ARG_NUM(1); *r_return = Math::fposmod((double)*p_inputs[0], (double)*p_inputs[1]); } break; + case MATH_POSMOD: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::posmod((int)*p_inputs[0], (int)*p_inputs[1]); + } break; case MATH_FLOOR: { VALIDATE_ARG_NUM(0); @@ -363,6 +379,11 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant VALIDATE_ARG_NUM(0); *r_return = Math::step_decimals((double)*p_inputs[0]); } break; + case MATH_STEP_DECIMALS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::step_decimals((double)*p_inputs[0]); + } break; case MATH_STEPIFY: { VALIDATE_ARG_NUM(0); @@ -376,6 +397,13 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant VALIDATE_ARG_NUM(2); *r_return = Math::lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); } break; + case MATH_LERP_ANGLE: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::lerp_angle((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; case MATH_INVERSE_LERP: { VALIDATE_ARG_NUM(0); @@ -392,6 +420,19 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant VALIDATE_ARG_NUM(4); *r_return = Math::range_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]); } break; + case MATH_SMOOTHSTEP: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::smoothstep((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_MOVE_TOWARD: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::move_toward((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; case MATH_DECTIME: { VALIDATE_ARG_NUM(0); @@ -752,7 +793,8 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return = String(color); } break; - default: {} + default: { + } } } @@ -768,17 +810,13 @@ Error Expression::_get_token(Token &r_token) { #define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++]) CharType cchar = GET_CHAR(); - if (cchar == 0) { - r_token.type = TK_EOF; - return OK; - } switch (cchar) { case 0: { r_token.type = TK_EOF; return OK; - } break; + }; case '{': { r_token.type = TK_CURLY_BRACKET_OPEN; @@ -1671,7 +1709,8 @@ Expression::ENode *Expression::_parse_expression() { case TK_OP_BIT_OR: op = Variant::OP_BIT_OR; break; case TK_OP_BIT_XOR: op = Variant::OP_BIT_XOR; break; case TK_OP_BIT_INVERT: op = Variant::OP_BIT_NEGATE; break; - default: {}; + default: { + }; } if (op == Variant::OP_MAX) { //stop appending stuff @@ -1768,7 +1807,7 @@ Expression::ENode *Expression::_parse_expression() { if (next_op == -1) { _set_error("Yet another parser bug...."); - ERR_FAIL_COND_V(next_op == -1, NULL); + ERR_FAIL_V(NULL); } // OK! create operator.. @@ -2122,10 +2161,8 @@ Error Expression::parse(const String &p_expression, const Vector<String> &p_inpu } Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) { - if (error_set) { - ERR_EXPLAIN("There was previously a parse error: " + error_str); - ERR_FAIL_V(Variant()); - } + + ERR_FAIL_COND_V_MSG(error_set, Variant(), "There was previously a parse error: " + error_str + "."); execution_error = false; Variant output; @@ -2134,10 +2171,7 @@ Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) { if (err) { execution_error = true; error_str = error_txt; - if (p_show_error) { - ERR_EXPLAIN(error_str); - ERR_FAIL_V(Variant()); - } + ERR_FAIL_COND_V_MSG(p_show_error, Variant(), error_str); } return output; diff --git a/core/math/expression.h b/core/math/expression.h index fa0878c93c..833220592c 100644 --- a/core/math/expression.h +++ b/core/math/expression.h @@ -34,7 +34,8 @@ #include "core/reference.h" class Expression : public Reference { - GDCLASS(Expression, Reference) + GDCLASS(Expression, Reference); + public: enum BuiltinFunc { MATH_SIN, @@ -50,6 +51,7 @@ public: MATH_SQRT, MATH_FMOD, MATH_FPOSMOD, + MATH_POSMOD, MATH_FLOOR, MATH_CEIL, MATH_ROUND, @@ -62,10 +64,14 @@ public: MATH_ISINF, MATH_EASE, MATH_DECIMALS, + MATH_STEP_DECIMALS, MATH_STEPIFY, MATH_LERP, + MATH_LERP_ANGLE, MATH_INVERSE_LERP, MATH_RANGE_LERP, + MATH_SMOOTHSTEP, + MATH_MOVE_TOWARD, MATH_DECTIME, MATH_RANDOMIZE, MATH_RAND, diff --git a/core/math/face3.cpp b/core/math/face3.cpp index ab09142b2d..666d214f98 100644 --- a/core/math/face3.cpp +++ b/core/math/face3.cpp @@ -393,7 +393,7 @@ Vector3 Face3::get_closest_point_to(const Vector3 &p_point) const { s = CLAMP(numer / denom, 0.f, 1.f); t = 1 - s; } else { - s = CLAMP(-e / c, 0.f, 1.f); + s = CLAMP(-d / a, 0.f, 1.f); t = 0.f; } } else { diff --git a/core/math/geometry.cpp b/core/math/geometry.cpp index a84b5a16c7..ada5107a2c 100644 --- a/core/math/geometry.cpp +++ b/core/math/geometry.cpp @@ -31,9 +31,13 @@ #include "geometry.h" #include "core/print_string.h" +#include "thirdparty/misc/clipper.hpp" #include "thirdparty/misc/triangulator.h" -/* this implementation is very inefficient, commenting unless bugs happen. See the other one. +#define SCALE_FACTOR 100000.0 // Based on CMP_EPSILON. + +// This implementation is very inefficient, commenting unless bugs happen. See the other one. +/* bool Geometry::is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) { Vector<int> indices = Geometry::triangulate_polygon(p_polygon); @@ -121,8 +125,8 @@ struct _FaceClassify { }; static bool _connect_faces(_FaceClassify *p_faces, int len, int p_group) { - /* connect faces, error will occur if an edge is shared between more than 2 faces */ - /* clear connections */ + // Connect faces, error will occur if an edge is shared between more than 2 faces. + // Clear connections. bool error = false; @@ -192,13 +196,6 @@ static bool _connect_faces(_FaceClassify *p_faces, int len, int p_group) { if (p_faces[i].links[j].face == -1) p_faces[i].valid = false; } - /*printf("face %i is valid: %i, group %i. connected to %i:%i,%i:%i,%i:%i\n",i,p_faces[i].valid,p_faces[i].group, - p_faces[i].links[0].face, - p_faces[i].links[0].edge, - p_faces[i].links[1].face, - p_faces[i].links[1].edge, - p_faces[i].links[2].face, - p_faces[i].links[2].edge);*/ } return error; } @@ -244,12 +241,9 @@ PoolVector<PoolVector<Face3> > Geometry::separate_objects(PoolVector<Face3> p_ar bool error = _connect_faces(_fcptr, len, -1); - if (error) { - - ERR_FAIL_COND_V(error, PoolVector<PoolVector<Face3> >()); // invalid geometry - } + ERR_FAIL_COND_V_MSG(error, PoolVector<PoolVector<Face3> >(), "Invalid geometry."); - /* group connected faces in separate objects */ + // Group connected faces in separate objects. int group = 0; for (int i = 0; i < len; i++) { @@ -261,7 +255,7 @@ PoolVector<PoolVector<Face3> > Geometry::separate_objects(PoolVector<Face3> p_ar } } - /* group connected faces in separate objects */ + // Group connected faces in separate objects. for (int i = 0; i < len; i++) { @@ -373,7 +367,7 @@ static inline void _plot_face(uint8_t ***p_cell_status, int x, int y, int z, int static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z, int len_x, int len_y, int len_z) { if (p_cell_status[x][y][z] & 3) - return; // nothing to do, already used and/or visited + return; // Nothing to do, already used and/or visited. p_cell_status[x][y][z] = _CELL_PREV_FIRST; @@ -381,29 +375,20 @@ static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z, uint8_t &c = p_cell_status[x][y][z]; - //printf("at %i,%i,%i\n",x,y,z); - if ((c & _CELL_STEP_MASK) == _CELL_STEP_NONE) { - /* Haven't been in here, mark as outside */ + // Haven't been in here, mark as outside. p_cell_status[x][y][z] |= _CELL_EXTERIOR; - //printf("not marked as anything, marking exterior\n"); } - //printf("cell step is %i\n",(c&_CELL_STEP_MASK)); - if ((c & _CELL_STEP_MASK) != _CELL_STEP_DONE) { - /* if not done, increase step */ + // If not done, increase step. c += 1 << 2; - //printf("incrementing cell step\n"); } if ((c & _CELL_STEP_MASK) == _CELL_STEP_DONE) { - /* Go back */ - //printf("done, going back a cell\n"); - + // Go back. switch (c & _CELL_PREV_MASK) { case _CELL_PREV_FIRST: { - //printf("at end, finished marking\n"); return; } break; case _CELL_PREV_Y_POS: { @@ -437,8 +422,6 @@ static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z, continue; } - //printf("attempting new cell!\n"); - int next_x = x, next_y = y, next_z = z; uint8_t prev = 0; @@ -472,8 +455,6 @@ static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z, default: ERR_FAIL(); } - //printf("testing if new cell will be ok...!\n"); - if (next_x < 0 || next_x >= len_x) continue; if (next_y < 0 || next_y >= len_y) @@ -481,13 +462,9 @@ static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z, if (next_z < 0 || next_z >= len_z) continue; - //printf("testing if new cell is traversable\n"); - if (p_cell_status[next_x][next_y][next_z] & 3) continue; - //printf("move to it\n"); - x = next_x; y = next_y; z = next_z; @@ -504,17 +481,6 @@ static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, i if (p_cell_status[x][y][z] & _CELL_EXTERIOR) return; -/* static const Vector3 vertices[8]={ - Vector3(0,0,0), - Vector3(0,0,1), - Vector3(0,1,0), - Vector3(0,1,1), - Vector3(1,0,0), - Vector3(1,0,1), - Vector3(1,1,0), - Vector3(1,1,1), - }; -*/ #define vert(m_idx) Vector3(((m_idx)&4) >> 2, ((m_idx)&2) >> 1, (m_idx)&1) static const uint8_t indices[6][4] = { @@ -526,22 +492,6 @@ static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, i { 0, 4, 6, 2 }, }; - /* - - {0,1,2,3}, - {0,1,4,5}, - {0,2,4,6}, - {4,5,6,7}, - {2,3,7,6}, - {1,3,5,7}, - - {0,2,3,1}, - {0,1,5,4}, - {0,4,6,2}, - {7,6,4,5}, - {7,3,2,6}, - {7,5,1,3}, -*/ for (int i = 0; i < 6; i++) { @@ -604,9 +554,9 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e } } - global_aabb.grow_by(0.01); // avoid numerical error + global_aabb.grow_by(0.01); // Avoid numerical error. - // determine amount of cells in grid axis + // Determine amount of cells in grid axis. int div_x, div_y, div_z; if (global_aabb.size.x / _MIN_SIZE < _MAX_LENGTH) @@ -629,7 +579,7 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e voxelsize.y /= div_y; voxelsize.z /= div_z; - // create and initialize cells to zero + // Create and initialize cells to zero. uint8_t ***cell_status = memnew_arr(uint8_t **, div_x); for (int i = 0; i < div_x; i++) { @@ -647,7 +597,7 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e } } - // plot faces into cells + // Plot faces into cells. for (int i = 0; i < face_count; i++) { @@ -659,7 +609,7 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e _plot_face(cell_status, 0, 0, 0, div_x, div_y, div_z, voxelsize, f); } - // determine which cells connect to the outside by traversing the outside and recursively flood-fill marking + // Determine which cells connect to the outside by traversing the outside and recursively flood-fill marking. for (int i = 0; i < div_x; i++) { @@ -688,7 +638,7 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e } } - // build faces for the inside-outside cell divisors + // Build faces for the inside-outside cell divisors. PoolVector<Face3> wrapped_faces; @@ -703,7 +653,7 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e } } - // transform face vertices to global coords + // Transform face vertices to global coords. int wrapped_faces_count = wrapped_faces.size(); PoolVector<Face3>::Write wrapped_facesw = wrapped_faces.write(); @@ -750,7 +700,7 @@ Vector<Vector<Vector2> > Geometry::decompose_polygon_in_convex(Vector<Point2> po inp.SetOrientation(TRIANGULATOR_CCW); in_poly.push_back(inp); TriangulatorPartition tpart; - if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { //failed! + if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { // Failed. ERR_PRINT("Convex decomposing failed!"); return decomp; } @@ -762,7 +712,7 @@ Vector<Vector<Vector2> > Geometry::decompose_polygon_in_convex(Vector<Point2> po decomp.write[idx].resize(tp.GetNumPoints()); - for (int i = 0; i < tp.GetNumPoints(); i++) { + for (int64_t i = 0; i < tp.GetNumPoints(); i++) { decomp.write[idx].write[i] = tp.GetPoint(i); } @@ -778,7 +728,7 @@ Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes #define SUBPLANE_SIZE 1024.0 - real_t subplane_size = 1024.0; // should compute this from the actual plane + real_t subplane_size = 1024.0; // Should compute this from the actual plane. for (int i = 0; i < p_planes.size(); i++) { Plane p = p_planes[i]; @@ -786,7 +736,7 @@ Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes Vector3 ref = Vector3(0.0, 1.0, 0.0); if (ABS(p.normal.dot(ref)) > 0.95) - ref = Vector3(0.0, 0.0, 1.0); // change axis + ref = Vector3(0.0, 0.0, 1.0); // Change axis. Vector3 right = p.normal.cross(ref).normalized(); Vector3 up = p.normal.cross(right).normalized(); @@ -824,20 +774,20 @@ Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes real_t dist0 = clip.distance_to(edge0_A); real_t dist1 = clip.distance_to(edge1_A); - if (dist0 <= 0) { // behind plane + if (dist0 <= 0) { // Behind plane. new_vertices.push_back(vertices[k]); } - // check for different sides and non coplanar + // Check for different sides and non coplanar. if ((dist0 * dist1) < 0) { - // calculate intersection + // Calculate intersection. Vector3 rel = edge1_A - edge0_A; real_t den = clip.normal.dot(rel); - if (Math::abs(den) < CMP_EPSILON) - continue; // point too short + if (Math::is_zero_approx(den)) + continue; // Point too short. real_t dist = -(clip.normal.dot(edge0_A) - clip.d) / den; Vector3 inters = edge0_A + rel * dist; @@ -851,11 +801,11 @@ Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes if (vertices.size() < 3) continue; - //result is a clockwise face + // Result is a clockwise face. MeshData::Face face; - // add face indices + // Add face indices. for (int j = 0; j < vertices.size(); j++) { int idx = -1; @@ -879,7 +829,7 @@ Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes face.plane = p; mesh.faces.push_back(face); - //add edge + // Add edge. for (int j = 0; j < face.indices.size(); j++) { @@ -969,7 +919,7 @@ PoolVector<Plane> Geometry::build_sphere_planes(real_t p_radius, int p_lats, int for (int j = 1; j <= p_lats; j++) { - //todo this is stupid, fix + // FIXME: This is stupid. Vector3 angle = normal.linear_interpolate(axis, j / (real_t)p_lats).normalized(); Vector3 pos = angle * p_radius; planes.push_back(Plane(pos, angle)); @@ -1029,12 +979,12 @@ struct _AtlasWorkRectResult { void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size) { - //super simple, almost brute force scanline stacking fitter - //it's pretty basic for now, but it tries to make sure that the aspect ratio of the - //resulting atlas is somehow square. This is necessary because video cards have limits - //on texture size (usually 2048 or 4096), so the more square a texture, the more chances - //it will work in every hardware. - // for example, it will prioritize a 1024x1024 atlas (works everywhere) instead of a + // Super simple, almost brute force scanline stacking fitter. + // It's pretty basic for now, but it tries to make sure that the aspect ratio of the + // resulting atlas is somehow square. This is necessary because video cards have limits. + // On texture size (usually 2048 or 4096), so the more square a texture, the more chances. + // It will work in every hardware. + // For example, it will prioritize a 1024x1024 atlas (works everywhere) instead of a // 256x8192 atlas (won't work anywhere). ERR_FAIL_COND(p_rects.size() == 0); @@ -1063,7 +1013,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu for (int j = 0; j < w; j++) hmax.write[j] = 0; - //place them + // Place them. int ofs = 0; int limit_h = 0; for (int j = 0; j < wrects.size(); j++) { @@ -1098,7 +1048,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu if (end_w > max_w) max_w = end_w; - if (ofs == 0 || end_h > limit_h) //while h limit not reached, keep stacking + if (ofs == 0 || end_h > limit_h) // While h limit not reached, keep stacking. ofs += wrects[j].s.width; } @@ -1109,7 +1059,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu results.push_back(result); } - //find the result with the best aspect ratio + // Find the result with the best aspect ratio. int best = -1; real_t best_aspect = 1e20; @@ -1134,3 +1084,106 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu r_size = Size2(results[best].max_w, results[best].max_h); } + +Vector<Vector<Point2> > Geometry::_polypaths_do_operation(PolyBooleanOperation p_op, const Vector<Point2> &p_polypath_a, const Vector<Point2> &p_polypath_b, bool is_a_open) { + + using namespace ClipperLib; + + ClipType op = ctUnion; + + switch (p_op) { + case OPERATION_UNION: op = ctUnion; break; + case OPERATION_DIFFERENCE: op = ctDifference; break; + case OPERATION_INTERSECTION: op = ctIntersection; break; + case OPERATION_XOR: op = ctXor; break; + } + Path path_a, path_b; + + // Need to scale points (Clipper's requirement for robust computation). + for (int i = 0; i != p_polypath_a.size(); ++i) { + path_a << IntPoint(p_polypath_a[i].x * SCALE_FACTOR, p_polypath_a[i].y * SCALE_FACTOR); + } + for (int i = 0; i != p_polypath_b.size(); ++i) { + path_b << IntPoint(p_polypath_b[i].x * SCALE_FACTOR, p_polypath_b[i].y * SCALE_FACTOR); + } + Clipper clp; + clp.AddPath(path_a, ptSubject, !is_a_open); // Forward compatible with Clipper 10.0.0. + clp.AddPath(path_b, ptClip, true); // Polylines cannot be set as clip. + + Paths paths; + + if (is_a_open) { + PolyTree tree; // Needed to populate polylines. + clp.Execute(op, tree); + OpenPathsFromPolyTree(tree, paths); + } else { + clp.Execute(op, paths); // Works on closed polygons only. + } + // Have to scale points down now. + Vector<Vector<Point2> > polypaths; + + for (Paths::size_type i = 0; i < paths.size(); ++i) { + Vector<Vector2> polypath; + + const Path &scaled_path = paths[i]; + + for (Paths::size_type j = 0; j < scaled_path.size(); ++j) { + polypath.push_back(Point2( + static_cast<real_t>(scaled_path[j].X) / SCALE_FACTOR, + static_cast<real_t>(scaled_path[j].Y) / SCALE_FACTOR)); + } + polypaths.push_back(polypath); + } + return polypaths; +} + +Vector<Vector<Point2> > Geometry::_polypath_offset(const Vector<Point2> &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { + + using namespace ClipperLib; + + JoinType jt = jtSquare; + + switch (p_join_type) { + case JOIN_SQUARE: jt = jtSquare; break; + case JOIN_ROUND: jt = jtRound; break; + case JOIN_MITER: jt = jtMiter; break; + } + + EndType et = etClosedPolygon; + + switch (p_end_type) { + case END_POLYGON: et = etClosedPolygon; break; + case END_JOINED: et = etClosedLine; break; + case END_BUTT: et = etOpenButt; break; + case END_SQUARE: et = etOpenSquare; break; + case END_ROUND: et = etOpenRound; break; + } + ClipperOffset co(2.0, 0.25 * SCALE_FACTOR); // Defaults from ClipperOffset. + Path path; + + // Need to scale points (Clipper's requirement for robust computation). + for (int i = 0; i != p_polypath.size(); ++i) { + path << IntPoint(p_polypath[i].x * SCALE_FACTOR, p_polypath[i].y * SCALE_FACTOR); + } + co.AddPath(path, jt, et); + + Paths paths; + co.Execute(paths, p_delta * SCALE_FACTOR); // Inflate/deflate. + + // Have to scale points down now. + Vector<Vector<Point2> > polypaths; + + for (Paths::size_type i = 0; i < paths.size(); ++i) { + Vector<Vector2> polypath; + + const Path &scaled_path = paths[i]; + + for (Paths::size_type j = 0; j < scaled_path.size(); ++j) { + polypath.push_back(Point2( + static_cast<real_t>(scaled_path[j].X) / SCALE_FACTOR, + static_cast<real_t>(scaled_path[j].Y) / SCALE_FACTOR)); + } + polypaths.push_back(polypath); + } + return polypaths; +} diff --git a/core/math/geometry.h b/core/math/geometry.h index 7347cb742a..8b0a51c651 100644 --- a/core/math/geometry.h +++ b/core/math/geometry.h @@ -31,6 +31,7 @@ #ifndef GEOMETRY_H #define GEOMETRY_H +#include "core/math/delaunay.h" #include "core/math/face3.h" #include "core/math/rect2.h" #include "core/math/triangulate.h" @@ -40,47 +41,43 @@ #include "core/print_string.h" #include "core/vector.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - class Geometry { Geometry(); public: static real_t get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2, Vector2 &c1, Vector2 &c2) { - Vector2 d1 = q1 - p1; // Direction vector of segment S1 - Vector2 d2 = q2 - p2; // Direction vector of segment S2 + Vector2 d1 = q1 - p1; // Direction vector of segment S1. + Vector2 d2 = q2 - p2; // Direction vector of segment S2. Vector2 r = p1 - p2; - real_t a = d1.dot(d1); // Squared length of segment S1, always nonnegative - real_t e = d2.dot(d2); // Squared length of segment S2, always nonnegative + real_t a = d1.dot(d1); // Squared length of segment S1, always nonnegative. + real_t e = d2.dot(d2); // Squared length of segment S2, always nonnegative. real_t f = d2.dot(r); real_t s, t; - // Check if either or both segments degenerate into points + // Check if either or both segments degenerate into points. if (a <= CMP_EPSILON && e <= CMP_EPSILON) { - // Both segments degenerate into points + // Both segments degenerate into points. c1 = p1; c2 = p2; return Math::sqrt((c1 - c2).dot(c1 - c2)); } if (a <= CMP_EPSILON) { - // First segment degenerates into a point + // First segment degenerates into a point. s = 0.0; t = f / e; // s = 0 => t = (b*s + f) / e = f / e t = CLAMP(t, 0.0, 1.0); } else { real_t c = d1.dot(r); if (e <= CMP_EPSILON) { - // Second segment degenerates into a point + // Second segment degenerates into a point. t = 0.0; s = CLAMP(-c / a, 0.0, 1.0); // t = 0 => s = (b*t - c) / a = -c / a } else { - // The general nondegenerate case starts here + // The general nondegenerate case starts here. real_t b = d1.dot(d2); - real_t denom = a * e - b * b; // Always nonnegative + real_t denom = a * e - b * b; // Always nonnegative. // If segments not parallel, compute closest point on L1 to L2 and - // clamp to segment S1. Else pick arbitrary s (here 0) + // clamp to segment S1. Else pick arbitrary s (here 0). if (denom != 0.0) { s = CLAMP((b * f - c * e) / denom, 0.0, 1.0); } else @@ -91,7 +88,7 @@ public: //If t in [0,1] done. Else clamp t, recompute s for the new value // of t using s = Dot((P2 + D2*t) - P1,D1) / Dot(D1,D1)= (t*b - c) / a - // and clamp s to [0, 1] + // and clamp s to [0, 1]. if (t < 0.0) { t = 0.0; s = CLAMP(-c / a, 0.0, 1.0); @@ -108,14 +105,14 @@ public: static void get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2, Vector3 &c1, Vector3 &c2) { -//do the function 'd' as defined by pb. I think is is dot product of some sort +// Do the function 'd' as defined by pb. I think is is dot product of some sort. #define d_of(m, n, o, p) ((m.x - n.x) * (o.x - p.x) + (m.y - n.y) * (o.y - p.y) + (m.z - n.z) * (o.z - p.z)) - //calculate the parametric position on the 2 curves, mua and mub + // Calculate the parametric position on the 2 curves, mua and mub. real_t mua = (d_of(p1, q1, q2, q1) * d_of(q2, q1, p2, p1) - d_of(p1, q1, p2, p1) * d_of(q2, q1, q2, q1)) / (d_of(p2, p1, p2, p1) * d_of(q2, q1, q2, q1) - d_of(q2, q1, p2, p1) * d_of(q2, q1, p2, p1)); real_t mub = (d_of(p1, q1, q2, q1) + mua * d_of(q2, q1, p2, p1)) / d_of(q2, q1, q2, q1); - //clip the value between [0..1] constraining the solution to lie on the original curves + // Clip the value between [0..1] constraining the solution to lie on the original curves. if (mua < 0) mua = 0; if (mub < 0) mub = 0; if (mua > 1) mua = 1; @@ -128,38 +125,38 @@ public: Vector3 u = p_to_a - p_from_a; Vector3 v = p_to_b - p_from_b; Vector3 w = p_from_a - p_to_a; - real_t a = u.dot(u); // always >= 0 + real_t a = u.dot(u); // Always >= 0 real_t b = u.dot(v); - real_t c = v.dot(v); // always >= 0 + real_t c = v.dot(v); // Always >= 0 real_t d = u.dot(w); real_t e = v.dot(w); - real_t D = a * c - b * b; // always >= 0 + real_t D = a * c - b * b; // Always >= 0 real_t sc, sN, sD = D; // sc = sN / sD, default sD = D >= 0 real_t tc, tN, tD = D; // tc = tN / tD, default tD = D >= 0 - // compute the line parameters of the two closest points - if (D < CMP_EPSILON) { // the lines are almost parallel - sN = 0.0; // force using point P0 on segment S1 - sD = 1.0; // to prevent possible division by 0.0 later + // Compute the line parameters of the two closest points. + if (D < CMP_EPSILON) { // The lines are almost parallel. + sN = 0.0; // Force using point P0 on segment S1 + sD = 1.0; // to prevent possible division by 0.0 later. tN = e; tD = c; - } else { // get the closest points on the infinite lines + } else { // Get the closest points on the infinite lines sN = (b * e - c * d); tN = (a * e - b * d); - if (sN < 0.0) { // sc < 0 => the s=0 edge is visible + if (sN < 0.0) { // sc < 0 => the s=0 edge is visible. sN = 0.0; tN = e; tD = c; - } else if (sN > sD) { // sc > 1 => the s=1 edge is visible + } else if (sN > sD) { // sc > 1 => the s=1 edge is visible. sN = sD; tN = e + b; tD = c; } } - if (tN < 0.0) { // tc < 0 => the t=0 edge is visible + if (tN < 0.0) { // tc < 0 => the t=0 edge is visible. tN = 0.0; - // recompute sc for this edge + // Recompute sc for this edge. if (-d < 0.0) sN = 0.0; else if (-d > a) @@ -168,9 +165,9 @@ public: sN = -d; sD = a; } - } else if (tN > tD) { // tc > 1 => the t=1 edge is visible + } else if (tN > tD) { // tc > 1 => the t=1 edge is visible. tN = tD; - // recompute sc for this edge + // Recompute sc for this edge. if ((-d + b) < 0.0) sN = 0; else if ((-d + b) > a) @@ -180,14 +177,14 @@ public: sD = a; } } - // finally do the division to get sc and tc - sc = (Math::abs(sN) < CMP_EPSILON ? 0.0 : sN / sD); - tc = (Math::abs(tN) < CMP_EPSILON ? 0.0 : tN / tD); + // Finally do the division to get sc and tc. + sc = (Math::is_zero_approx(sN) ? 0.0 : sN / sD); + tc = (Math::is_zero_approx(tN) ? 0.0 : tN / tD); - // get the difference of the two closest points + // Get the difference of the two closest points. Vector3 dP = w + (sc * u) - (tc * v); // = S1(sc) - S2(tc) - return dP.length(); // return the closest distance + return dP.length(); // Return the closest distance. } static inline bool ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2, Vector3 *r_res = 0) { @@ -195,7 +192,7 @@ public: Vector3 e2 = p_v2 - p_v0; Vector3 h = p_dir.cross(e2); real_t a = e1.dot(h); - if (a > -CMP_EPSILON && a < CMP_EPSILON) // parallel test + if (Math::is_zero_approx(a)) // Parallel test. return false; real_t f = 1.0 / a; @@ -213,16 +210,15 @@ public: if (v < 0.0 || u + v > 1.0) return false; - // at this stage we can compute t to find out where - // the intersection point is on the line + // At this stage we can compute t to find out where + // the intersection point is on the line. real_t t = f * e2.dot(q); if (t > 0.00001) { // ray intersection if (r_res) *r_res = p_from + p_dir * t; return true; - } else // this means that there is a line intersection - // but not a ray intersection + } else // This means that there is a line intersection but not a ray intersection. return false; } @@ -233,7 +229,7 @@ public: Vector3 e2 = p_v2 - p_v0; Vector3 h = rel.cross(e2); real_t a = e1.dot(h); - if (a > -CMP_EPSILON && a < CMP_EPSILON) // parallel test + if (Math::is_zero_approx(a)) // Parallel test. return false; real_t f = 1.0 / a; @@ -251,16 +247,15 @@ public: if (v < 0.0 || u + v > 1.0) return false; - // at this stage we can compute t to find out where - // the intersection point is on the line + // At this stage we can compute t to find out where + // the intersection point is on the line. real_t t = f * e2.dot(q); - if (t > CMP_EPSILON && t <= 1.0) { // ray intersection + if (t > CMP_EPSILON && t <= 1.0) { // Ray intersection. if (r_res) *r_res = p_from + rel * t; return true; - } else // this means that there is a line intersection - // but not a ray intersection + } else // This means that there is a line intersection but not a ray intersection. return false; } @@ -270,13 +265,11 @@ public: Vector3 rel = (p_to - p_from); real_t rel_l = rel.length(); if (rel_l < CMP_EPSILON) - return false; // both points are the same + return false; // Both points are the same. Vector3 normal = rel / rel_l; real_t sphere_d = normal.dot(sphere_pos); - //Vector3 ray_closest=normal*sphere_d; - real_t ray_distance = sphere_pos.distance_to(normal * sphere_d); if (ray_distance >= p_sphere_radius) @@ -288,7 +281,7 @@ public: if (inters_d2 >= CMP_EPSILON) inters_d -= Math::sqrt(inters_d2); - // check in segment + // Check in segment. if (inters_d < 0 || inters_d > rel_l) return false; @@ -307,9 +300,9 @@ public: Vector3 rel = (p_to - p_from); real_t rel_l = rel.length(); if (rel_l < CMP_EPSILON) - return false; // both points are the same + return false; // Both points are the same. - // first check if they are parallel + // First check if they are parallel. Vector3 normal = (rel / rel_l); Vector3 crs = normal.cross(Vector3(0, 0, 1)); real_t crs_l = crs.length(); @@ -317,8 +310,7 @@ public: Vector3 z_dir; if (crs_l < CMP_EPSILON) { - //blahblah parallel - z_dir = Vector3(1, 0, 0); //any x/y vector ok + z_dir = Vector3(1, 0, 0); // Any x/y vector OK. } else { z_dir = crs / crs_l; } @@ -326,12 +318,12 @@ public: real_t dist = z_dir.dot(p_from); if (dist >= p_radius) - return false; // too far away + return false; // Too far away. - // convert to 2D + // Convert to 2D. real_t w2 = p_radius * p_radius - dist * dist; if (w2 < CMP_EPSILON) - return false; //avoid numerical error + return false; // Avoid numerical error. Size2 size(Math::sqrt(w2), p_height * 0.5); Vector3 x_dir = z_dir.cross(Vector3(0, 0, 1)).normalized(); @@ -378,7 +370,7 @@ public: return false; } - // convert to 3D again + // Convert to 3D again. Vector3 result = p_from + (rel * min); Vector3 res_normal = result; @@ -419,19 +411,18 @@ public: real_t den = p.normal.dot(dir); - //printf("den is %i\n",den); if (Math::abs(den) <= CMP_EPSILON) - continue; // ignore parallel plane + continue; // Ignore parallel plane. real_t dist = -p.distance_to(p_from) / den; if (den > 0) { - //backwards facing plane + // Backwards facing plane. if (dist < max) max = dist; } else { - //front facing plane + // Front facing plane. if (dist > min) { min = dist; min_index = i; @@ -439,8 +430,8 @@ public: } } - if (max <= min || min < 0 || min > rel_l || min_index == -1) // exit conditions - return false; // no intersection + if (max <= min || min < 0 || min > rel_l || min_index == -1) // Exit conditions. + return false; // No intersection. if (p_res) *p_res = p_from + dir * min; @@ -454,52 +445,49 @@ public: Vector3 p = p_point - p_segment[0]; Vector3 n = p_segment[1] - p_segment[0]; - real_t l = n.length(); - if (l < 1e-10) - return p_segment[0]; // both points are the same, just give any - n /= l; + real_t l2 = n.length_squared(); + if (l2 < 1e-20) + return p_segment[0]; // Both points are the same, just give any. - real_t d = n.dot(p); + real_t d = n.dot(p) / l2; if (d <= 0.0) - return p_segment[0]; // before first point - else if (d >= l) - return p_segment[1]; // after first point + return p_segment[0]; // Before first point. + else if (d >= 1.0) + return p_segment[1]; // After first point. else - return p_segment[0] + n * d; // inside + return p_segment[0] + n * d; // Inside. } static Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 *p_segment) { Vector3 p = p_point - p_segment[0]; Vector3 n = p_segment[1] - p_segment[0]; - real_t l = n.length(); - if (l < 1e-10) - return p_segment[0]; // both points are the same, just give any - n /= l; + real_t l2 = n.length_squared(); + if (l2 < 1e-20) + return p_segment[0]; // Both points are the same, just give any. - real_t d = n.dot(p); + real_t d = n.dot(p) / l2; - return p_segment[0] + n * d; // inside + return p_segment[0] + n * d; // Inside. } static Vector2 get_closest_point_to_segment_2d(const Vector2 &p_point, const Vector2 *p_segment) { Vector2 p = p_point - p_segment[0]; Vector2 n = p_segment[1] - p_segment[0]; - real_t l = n.length(); - if (l < 1e-10) - return p_segment[0]; // both points are the same, just give any - n /= l; + real_t l2 = n.length_squared(); + if (l2 < 1e-20) + return p_segment[0]; // Both points are the same, just give any. - real_t d = n.dot(p); + real_t d = n.dot(p) / l2; if (d <= 0.0) - return p_segment[0]; // before first point - else if (d >= l) - return p_segment[1]; // after first point + return p_segment[0]; // Before first point. + else if (d >= 1.0) + return p_segment[1]; // After first point. else - return p_segment[0] + n * d; // inside + return p_segment[0] + n * d; // Inside. } static bool is_point_in_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) { @@ -514,28 +502,25 @@ public: return (cn.cross(an) > 0) == orientation; } - //static bool is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon); - static Vector2 get_closest_point_to_segment_uncapped_2d(const Vector2 &p_point, const Vector2 *p_segment) { Vector2 p = p_point - p_segment[0]; Vector2 n = p_segment[1] - p_segment[0]; - real_t l = n.length(); - if (l < 1e-10) - return p_segment[0]; // both points are the same, just give any - n /= l; + real_t l2 = n.length_squared(); + if (l2 < 1e-20) + return p_segment[0]; // Both points are the same, just give any. - real_t d = n.dot(p); + real_t d = n.dot(p) / l2; - return p_segment[0] + n * d; // inside + return p_segment[0] + n * d; // Inside. } static bool line_intersects_line_2d(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b, Vector2 &r_result) { - // see http://paulbourke.net/geometry/pointlineplane/ + // See http://paulbourke.net/geometry/pointlineplane/ const real_t denom = p_dir_b.y * p_dir_a.x - p_dir_b.x * p_dir_a.y; - if (Math::abs(denom) < CMP_EPSILON) { // parallel? + if (Math::is_zero_approx(denom)) { // Parallel? return false; } @@ -563,11 +548,11 @@ public: real_t ABpos = D.x + (C.x - D.x) * D.y / (D.y - C.y); - // Fail if segment C-D crosses line A-B outside of segment A-B. + // Fail if segment C-D crosses line A-B outside of segment A-B. if (ABpos < 0 || ABpos > 1.0) return false; - // (4) Apply the discovered position to line A-B in the original coordinate system. + // (4) Apply the discovered position to line A-B in the original coordinate system. if (r_result) *r_result = p_from_a + B * ABpos; @@ -600,7 +585,7 @@ public: real_t d = p_normal.dot(p_sphere_pos) - p_normal.dot(p_triangle[0]); - if (d > p_sphere_radius || d < -p_sphere_radius) // not touching the plane of the face, return + if (d > p_sphere_radius || d < -p_sphere_radius) // Not touching the plane of the face, return. return false; Vector3 contact = p_sphere_pos - (p_normal * d); @@ -620,25 +605,25 @@ public: for (int i = 0; i < 3; i++) { - // check edge cylinder + // Check edge cylinder. Vector3 n1 = verts[i] - verts[i + 1]; Vector3 n2 = p_sphere_pos - verts[i + 1]; - ///@TODO i could discard by range here to make the algorithm quicker? dunno.. + ///@TODO Maybe discard by range here to make the algorithm quicker. - // check point within cylinder radius + // Check point within cylinder radius. Vector3 axis = n1.cross(n2).cross(n1); - axis.normalize(); // ugh + axis.normalize(); real_t ad = axis.dot(n2); if (ABS(ad) > p_sphere_radius) { - // no chance with this edge, too far away + // No chance with this edge, too far away. continue; } - // check point within edge capsule cylinder + // Check point within edge capsule cylinder. /** 4th TEST INSIDE EDGE POINTS **/ real_t sphere_at = n1.dot(n2); @@ -647,8 +632,7 @@ public: r_triangle_contact = p_sphere_pos - axis * (axis.dot(n2)); r_sphere_contact = p_sphere_pos - axis * p_sphere_radius; - // point inside here - //printf("solved inside edge\n"); + // Point inside here. return true; } @@ -658,48 +642,51 @@ public: Vector3 n = (p_sphere_pos - verts[i + 1]).normalized(); - //r_triangle_contact=verts[i+1]+n*p_sphere_radius;p_sphere_pos+axis*(p_sphere_radius-axis.dot(n2)); r_triangle_contact = verts[i + 1]; r_sphere_contact = p_sphere_pos - n * p_sphere_radius; - //printf("solved inside point segment 1\n"); return true; } if (n2.distance_squared_to(n1) < r2) { Vector3 n = (p_sphere_pos - verts[i]).normalized(); - //r_triangle_contact=verts[i]+n*p_sphere_radius;p_sphere_pos+axis*(p_sphere_radius-axis.dot(n2)); r_triangle_contact = verts[i]; r_sphere_contact = p_sphere_pos - n * p_sphere_radius; - //printf("solved inside point segment 1\n"); return true; } - break; // It's pointless to continue at this point, so save some cpu cycles + break; // It's pointless to continue at this point, so save some CPU cycles. } return false; } + static inline bool is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius) { + + return p_point.distance_squared_to(p_circle_pos) <= p_circle_radius * p_circle_radius; + } + static real_t segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) { Vector2 line_vec = p_to - p_from; Vector2 vec_to_line = p_from - p_circle_pos; - /* create a quadratic formula of the form ax^2 + bx + c = 0 */ + // Create a quadratic formula of the form ax^2 + bx + c = 0 real_t a, b, c; a = line_vec.dot(line_vec); b = 2 * vec_to_line.dot(line_vec); c = vec_to_line.dot(vec_to_line) - p_circle_radius * p_circle_radius; - /* solve for t */ + // Solve for t. real_t sqrtterm = b * b - 4 * a * c; - /* if the term we intend to square root is less than 0 then the answer won't be real, so it definitely won't be t in the range 0 to 1 */ + // If the term we intend to square root is less than 0 then the answer won't be real, + // so it definitely won't be t in the range 0 to 1. if (sqrtterm < 0) return -1; - /* if we can assume that the line segment starts outside the circle (e.g. for continuous time collision detection) then the following can be skipped and we can just return the equivalent of res1 */ + // If we can assume that the line segment starts outside the circle (e.g. for continuous time collision detection) + // then the following can be skipped and we can just return the equivalent of res1. sqrtterm = Math::sqrt(sqrtterm); real_t res1 = (-b - sqrtterm) / (2 * a); real_t res2 = (-b + sqrtterm) / (2 * a); @@ -725,7 +712,6 @@ public: int outside_count = 0; for (int a = 0; a < polygon.size(); a++) { - //real_t p_plane.d = (*this) * polygon[a]; real_t dist = p_plane.distance_to(polygon[a]); if (dist < -CMP_POINT_IN_PLANE_EPSILON) { location_cache[a] = LOC_INSIDE; @@ -742,11 +728,11 @@ public: if (outside_count == 0) { - return polygon; // no changes + return polygon; // No changes. } else if (inside_count == 0) { - return Vector<Vector3>(); //empty + return Vector<Vector3>(); // Empty. } long previous = polygon.size() - 1; @@ -785,6 +771,80 @@ public: return clipped; } + enum PolyBooleanOperation { + OPERATION_UNION, + OPERATION_DIFFERENCE, + OPERATION_INTERSECTION, + OPERATION_XOR + }; + enum PolyJoinType { + JOIN_SQUARE, + JOIN_ROUND, + JOIN_MITER + }; + enum PolyEndType { + END_POLYGON, + END_JOINED, + END_BUTT, + END_SQUARE, + END_ROUND + }; + + static Vector<Vector<Point2> > merge_polygons_2d(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { + + return _polypaths_do_operation(OPERATION_UNION, p_polygon_a, p_polygon_b); + } + + static Vector<Vector<Point2> > clip_polygons_2d(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { + + return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polygon_a, p_polygon_b); + } + + static Vector<Vector<Point2> > intersect_polygons_2d(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { + + return _polypaths_do_operation(OPERATION_INTERSECTION, p_polygon_a, p_polygon_b); + } + + static Vector<Vector<Point2> > exclude_polygons_2d(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { + + return _polypaths_do_operation(OPERATION_XOR, p_polygon_a, p_polygon_b); + } + + static Vector<Vector<Point2> > clip_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { + + return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polyline, p_polygon, true); + } + + static Vector<Vector<Point2> > intersect_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { + + return _polypaths_do_operation(OPERATION_INTERSECTION, p_polyline, p_polygon, true); + } + + static Vector<Vector<Point2> > offset_polygon_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) { + + return _polypath_offset(p_polygon, p_delta, p_join_type, END_POLYGON); + } + + static Vector<Vector<Point2> > offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { + + ERR_FAIL_COND_V_MSG(p_end_type == END_POLYGON, Vector<Vector<Point2> >(), "Attempt to offset a polyline like a polygon (use offset_polygon_2d instead)."); + + return _polypath_offset(p_polygon, p_delta, p_join_type, p_end_type); + } + + static Vector<int> triangulate_delaunay_2d(const Vector<Vector2> &p_points) { + + Vector<Delaunay2D::Triangle> tr = Delaunay2D::triangulate(p_points); + Vector<int> triangles; + + for (int i = 0; i < tr.size(); i++) { + triangles.push_back(tr[i].points[0]); + triangles.push_back(tr[i].points[1]); + triangles.push_back(tr[i].points[2]); + } + return triangles; + } + static Vector<int> triangulate_polygon(const Vector<Vector2> &p_polygon) { Vector<int> triangles; @@ -817,7 +877,7 @@ public: return sum > 0.0f; } - /* alternate implementation that should be faster */ + // Alternate implementation that should be faster. static bool is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) { int c = p_polygon.size(); if (c < 3) @@ -833,7 +893,8 @@ public: further_away_opposite.y = MIN(p[i].y, further_away_opposite.y); } - further_away += (further_away - further_away_opposite) * Vector2(1.221313, 1.512312); // make point outside that wont intersect with points in segment from p_point + // Make point outside that won't intersect with points in segment from p_point. + further_away += (further_away - further_away_opposite) * Vector2(1.221313, 1.512312); int intersections = 0; for (int i = 0; i < c; i++) { @@ -849,7 +910,8 @@ public: static PoolVector<PoolVector<Face3> > separate_objects(PoolVector<Face3> p_array); - static PoolVector<Face3> wrap_geometry(PoolVector<Face3> p_array, real_t *p_error = NULL); ///< create a "wrap" that encloses the given geometry + // Create a "wrap" that encloses the given geometry. + static PoolVector<Face3> wrap_geometry(PoolVector<Face3> p_array, real_t *p_error = NULL); struct MeshData { @@ -931,17 +993,17 @@ public: Vector<Point2> H; H.resize(2 * n); - // Sort points lexicographically + // Sort points lexicographically. P.sort(); - // Build lower hull + // Build lower hull. for (int i = 0; i < n; ++i) { while (k >= 2 && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0) k--; H.write[k++] = P[i]; } - // Build upper hull + // Build upper hull. for (int i = n - 2, t = k + 1; i >= 0; i--) { while (k >= t && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0) k--; @@ -951,7 +1013,6 @@ public: H.resize(k); return H; } - static Vector<Vector<Vector2> > decompose_polygon_in_convex(Vector<Point2> polygon); static MeshData build_convex_mesh(const PoolVector<Plane> &p_planes); @@ -961,6 +1022,10 @@ public: static PoolVector<Plane> build_capsule_planes(real_t p_radius, real_t p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z); static void make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size); + +private: + static Vector<Vector<Point2> > _polypaths_do_operation(PolyBooleanOperation p_op, const Vector<Point2> &p_polypath_a, const Vector<Point2> &p_polypath_b, bool is_a_open = false); + static Vector<Vector<Point2> > _polypath_offset(const Vector<Point2> &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type); }; #endif diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp index 5b5fd8e283..50fcdb2c13 100644 --- a/core/math/math_funcs.cpp +++ b/core/math/math_funcs.cpp @@ -30,6 +30,8 @@ #include "math_funcs.h" +#include "core/error_macros.h" + RandomPCG Math::default_rand(RandomPCG::DEFAULT_SEED, RandomPCG::DEFAULT_INC); #define PHI 0x9e3779b9 @@ -79,6 +81,15 @@ int Math::step_decimals(double p_step) { return 0; } +// Only meant for editor usage in float ranges, where a step of 0 +// means that decimal digits should not be limited in String::num. +int Math::range_step_decimals(double p_step) { + if (p_step < 0.0000000000001) { + return 16; // Max value hardcoded in String::num + } + return step_decimals(p_step); +} + double Math::dectime(double p_value, double p_amount, double p_step) { double sgn = p_value < 0 ? -1.0 : 1.0; double val = Math::abs(p_value); @@ -161,8 +172,6 @@ uint32_t Math::larger_prime(uint32_t p_val) { return primes[idx]; idx++; } - - return 0; } double Math::random(double from, double to) { diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index 6ac6839827..a94b27fcc5 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -61,6 +61,12 @@ public: static _ALWAYS_INLINE_ double sinh(double p_x) { return ::sinh(p_x); } static _ALWAYS_INLINE_ float sinh(float p_x) { return ::sinhf(p_x); } + static _ALWAYS_INLINE_ float sinc(float p_x) { return p_x == 0 ? 1 : ::sin(p_x) / p_x; } + static _ALWAYS_INLINE_ double sinc(double p_x) { return p_x == 0 ? 1 : ::sin(p_x) / p_x; } + + static _ALWAYS_INLINE_ float sincn(float p_x) { return sinc(Math_PI * p_x); } + static _ALWAYS_INLINE_ double sincn(double p_x) { return sinc(Math_PI * p_x); } + static _ALWAYS_INLINE_ double cosh(double p_x) { return ::cosh(p_x); } static _ALWAYS_INLINE_ float cosh(float p_x) { return ::coshf(p_x); } @@ -192,6 +198,13 @@ public: value += 0.0; return value; } + static _ALWAYS_INLINE_ int posmod(int p_x, int p_y) { + int value = p_x % p_y; + if ((value < 0 && p_y > 0) || (value > 0 && p_y < 0)) { + value += p_y; + } + return value; + } static _ALWAYS_INLINE_ double deg2rad(double p_y) { return p_y * Math_PI / 180.0; } static _ALWAYS_INLINE_ float deg2rad(float p_y) { return p_y * Math_PI / 180.0; } @@ -202,12 +215,36 @@ public: static _ALWAYS_INLINE_ double lerp(double p_from, double p_to, double p_weight) { return p_from + (p_to - p_from) * p_weight; } static _ALWAYS_INLINE_ float lerp(float p_from, float p_to, float p_weight) { return p_from + (p_to - p_from) * p_weight; } + static _ALWAYS_INLINE_ double lerp_angle(double p_from, double p_to, double p_weight) { + double difference = fmod(p_to - p_from, Math_TAU); + double distance = fmod(2.0 * difference, Math_TAU) - difference; + return p_from + distance * p_weight; + } + static _ALWAYS_INLINE_ float lerp_angle(float p_from, float p_to, float p_weight) { + float difference = fmod(p_to - p_from, (float)Math_TAU); + float distance = fmod(2.0f * difference, (float)Math_TAU) - difference; + return p_from + distance * p_weight; + } + static _ALWAYS_INLINE_ double inverse_lerp(double p_from, double p_to, double p_value) { return (p_value - p_from) / (p_to - p_from); } static _ALWAYS_INLINE_ float inverse_lerp(float p_from, float p_to, float p_value) { return (p_value - p_from) / (p_to - p_from); } static _ALWAYS_INLINE_ double range_lerp(double p_value, double p_istart, double p_istop, double p_ostart, double p_ostop) { return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); } static _ALWAYS_INLINE_ float range_lerp(float p_value, float p_istart, float p_istop, float p_ostart, float p_ostop) { return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); } + static _ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_weight) { + if (is_equal_approx(p_from, p_to)) return p_from; + double x = CLAMP((p_weight - p_from) / (p_to - p_from), 0.0, 1.0); + return x * x * (3.0 - 2.0 * x); + } + static _ALWAYS_INLINE_ float smoothstep(float p_from, float p_to, float p_weight) { + if (is_equal_approx(p_from, p_to)) return p_from; + float x = CLAMP((p_weight - p_from) / (p_to - p_from), 0.0f, 1.0f); + return x * x * (3.0f - 2.0f * x); + } + static _ALWAYS_INLINE_ double move_toward(double p_from, double p_to, double p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + SGN(p_to - p_from) * p_delta; } + static _ALWAYS_INLINE_ float move_toward(float p_from, float p_to, float p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + SGN(p_to - p_from) * p_delta; } + static _ALWAYS_INLINE_ double linear2db(double p_linear) { return Math::log(p_linear) * 8.6858896380650365530225783783321; } static _ALWAYS_INLINE_ float linear2db(float p_linear) { return Math::log(p_linear) * 8.6858896380650365530225783783321; } @@ -218,21 +255,22 @@ public: static _ALWAYS_INLINE_ float round(float p_val) { return (p_val >= 0) ? Math::floor(p_val + 0.5) : -Math::floor(-p_val + 0.5); } static _ALWAYS_INLINE_ int64_t wrapi(int64_t value, int64_t min, int64_t max) { - int64_t rng = max - min; - return (rng != 0) ? min + ((((value - min) % rng) + rng) % rng) : min; + int64_t range = max - min; + return range == 0 ? min : min + ((((value - min) % range) + range) % range); } static _ALWAYS_INLINE_ double wrapf(double value, double min, double max) { - double rng = max - min; - return (!is_equal_approx(rng, 0.0)) ? value - (rng * Math::floor((value - min) / rng)) : min; + double range = max - min; + return is_zero_approx(range) ? min : value - (range * Math::floor((value - min) / range)); } static _ALWAYS_INLINE_ float wrapf(float value, float min, float max) { - float rng = max - min; - return (!is_equal_approx(rng, 0.0f)) ? value - (rng * Math::floor((value - min) / rng)) : min; + float range = max - min; + return is_zero_approx(range) ? min : value - (range * Math::floor((value - min) / range)); } // double only, as these functions are mainly used by the editor and not performance-critical, static double ease(double p_x, double p_c); static int step_decimals(double p_step); + static int range_step_decimals(double p_step); static double stepify(double p_value, double p_step); static double dectime(double p_value, double p_amount, double p_step); @@ -261,13 +299,30 @@ public: return diff < epsilon; } - static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b, real_t epsilon = CMP_EPSILON) { - // TODO: Comparing floats for approximate-equality is non-trivial. - // Using epsilon should cover the typical cases in Godot (where a == b is used to compare two reals), such as matrix and vector comparison operators. - // A proper implementation in terms of ULPs should eventually replace the contents of this function. - // See https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ for details. + static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b) { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) { + return true; + } + // Then check for approximate equality. + real_t tolerance = CMP_EPSILON * abs(a); + if (tolerance < CMP_EPSILON) { + tolerance = CMP_EPSILON; + } + return abs(a - b) < tolerance; + } + + static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b, real_t tolerance) { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) { + return true; + } + // Then check for approximate equality. + return abs(a - b) < tolerance; + } - return abs(a - b) < epsilon; + static _ALWAYS_INLINE_ bool is_zero_approx(real_t s) { + return abs(s) < CMP_EPSILON; } static _ALWAYS_INLINE_ float absf(float g) { @@ -417,7 +472,7 @@ public: return p_step != 0 ? Math::stepify(p_target - p_offset, p_step) + p_offset : p_target; } - static _ALWAYS_INLINE_ float snap_scalar_seperation(float p_offset, float p_step, float p_target, float p_separation) { + static _ALWAYS_INLINE_ float snap_scalar_separation(float p_offset, float p_step, float p_target, float p_separation) { if (p_step != 0) { float a = Math::stepify(p_target - p_offset, p_step + p_separation) + p_offset; float b = a; diff --git a/core/math/octree.h b/core/math/octree.h index d6fc9776bc..db15c8a1f8 100644 --- a/core/math/octree.h +++ b/core/math/octree.h @@ -38,10 +38,6 @@ #include "core/print_string.h" #include "core/variant.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - typedef uint32_t OctreeElementID; #define OCTREE_ELEMENT_INVALID_ID 0 @@ -568,10 +564,7 @@ void Octree<T, use_pairs, AL>::_ensure_valid_root(const AABB &p_aabb) { while (!base.encloses(p_aabb)) { - if (base.size.x > OCTREE_SIZE_LIMIT) { - ERR_EXPLAIN("Octree upper size limit reeached, does the AABB supplied contain NAN?"); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(base.size.x > OCTREE_SIZE_LIMIT, "Octree upper size limit reached, does the AABB supplied contain NAN?"); Octant *gp = memnew_allocator(Octant, AL); octant_count++; diff --git a/core/math/plane.cpp b/core/math/plane.cpp index cd3cbce300..d55957cd0a 100644 --- a/core/math/plane.cpp +++ b/core/math/plane.cpp @@ -32,9 +32,6 @@ #include "core/math/math_funcs.h" -#define _PLANE_EQ_DOT_EPSILON 0.999 -#define _PLANE_EQ_D_EPSILON 0.0001 - void Plane::set_normal(const Vector3 &p_normal) { normal = p_normal; @@ -91,7 +88,7 @@ bool Plane::intersect_3(const Plane &p_plane1, const Plane &p_plane2, Vector3 *r real_t denom = vec3_cross(normal0, normal1).dot(normal2); - if (ABS(denom) <= CMP_EPSILON) + if (Math::is_zero_approx(denom)) return false; if (r_result) { @@ -110,7 +107,7 @@ bool Plane::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 real_t den = normal.dot(segment); //printf("den is %i\n",den); - if (Math::abs(den) <= CMP_EPSILON) { + if (Math::is_zero_approx(den)) { return false; } @@ -135,7 +132,7 @@ bool Plane::intersects_segment(const Vector3 &p_begin, const Vector3 &p_end, Vec real_t den = normal.dot(segment); //printf("den is %i\n",den); - if (Math::abs(den) <= CMP_EPSILON) { + if (Math::is_zero_approx(den)) { return false; } @@ -156,9 +153,9 @@ bool Plane::intersects_segment(const Vector3 &p_begin, const Vector3 &p_end, Vec /* misc */ -bool Plane::is_almost_like(const Plane &p_plane) const { +bool Plane::is_equal_approx(const Plane &p_plane) const { - return (normal.dot(p_plane.normal) > _PLANE_EQ_DOT_EPSILON && Math::absd(d - p_plane.d) < _PLANE_EQ_D_EPSILON); + return normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d); } Plane::operator String() const { diff --git a/core/math/plane.h b/core/math/plane.h index 1c6e4b816b..9abf24fbba 100644 --- a/core/math/plane.h +++ b/core/math/plane.h @@ -68,7 +68,7 @@ public: /* misc */ Plane operator-() const { return Plane(-normal, -d); } - bool is_almost_like(const Plane &p_plane) const; + bool is_equal_approx(const Plane &p_plane) const; _FORCE_INLINE_ bool operator==(const Plane &p_plane) const; _FORCE_INLINE_ bool operator!=(const Plane &p_plane) const; diff --git a/core/math/quat.cpp b/core/math/quat.cpp index 1a67be7384..a4f91844b9 100644 --- a/core/math/quat.cpp +++ b/core/math/quat.cpp @@ -121,6 +121,11 @@ Quat Quat::operator*(const Quat &q) const { return r; } +bool Quat::is_equal_approx(const Quat &p_quat) const { + + return Math::is_equal_approx(x, p_quat.x) && Math::is_equal_approx(y, p_quat.y) && Math::is_equal_approx(z, p_quat.z) && Math::is_equal_approx(w, p_quat.w); +} + real_t Quat::length() const { return Math::sqrt(length_squared()); diff --git a/core/math/quat.h b/core/math/quat.h index 8ed2fa7cc2..27885f4152 100644 --- a/core/math/quat.h +++ b/core/math/quat.h @@ -38,15 +38,12 @@ #include "core/math/math_funcs.h" #include "core/ustring.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - class Quat { public: real_t x, y, z, w; _FORCE_INLINE_ real_t length_squared() const; + bool is_equal_approx(const Quat &p_quat) const; real_t length() const; void normalize(); Quat normalized() const; diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index fc2eb1454d..f71f00afd6 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -401,7 +401,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me ERR_CONTINUE(O == E); ERR_CONTINUE(O == NULL); - if (O->get().plane.is_almost_like(f.plane)) { + if (O->get().plane.is_equal_approx(f.plane)) { //merge and delete edge and contiguous face, while repointing edges (uuugh!) int ois = O->get().indices.size(); int merged = 0; diff --git a/core/math/random_number_generator.cpp b/core/math/random_number_generator.cpp index 6add00c1d8..54a88d5cd8 100644 --- a/core/math/random_number_generator.cpp +++ b/core/math/random_number_generator.cpp @@ -30,8 +30,7 @@ #include "random_number_generator.h" -RandomNumberGenerator::RandomNumberGenerator() : - randbase() {} +RandomNumberGenerator::RandomNumberGenerator() {} void RandomNumberGenerator::_bind_methods() { ClassDB::bind_method(D_METHOD("set_seed", "seed"), &RandomNumberGenerator::set_seed); diff --git a/core/math/random_number_generator.h b/core/math/random_number_generator.h index 6b6bcdd2cd..9b54ea9b2e 100644 --- a/core/math/random_number_generator.h +++ b/core/math/random_number_generator.h @@ -47,7 +47,7 @@ public: _FORCE_INLINE_ uint64_t get_seed() { return randbase.get_seed(); } - _FORCE_INLINE_ void randomize() { return randbase.randomize(); } + _FORCE_INLINE_ void randomize() { randbase.randomize(); } _FORCE_INLINE_ uint32_t randi() { return randbase.rand(); } @@ -59,7 +59,10 @@ public: _FORCE_INLINE_ int randi_range(int from, int to) { unsigned int ret = randbase.rand(); - return ret % (to - from + 1) + from; + if (to < from) + return ret % (from - to + 1) + to; + else + return ret % (to - from + 1) + from; } RandomNumberGenerator(); diff --git a/core/math/random_pcg.cpp b/core/math/random_pcg.cpp index 8351bd138e..00c0af515d 100644 --- a/core/math/random_pcg.cpp +++ b/core/math/random_pcg.cpp @@ -43,13 +43,9 @@ void RandomPCG::randomize() { } double RandomPCG::random(double p_from, double p_to) { - unsigned int r = rand(); - double ret = (double)r / (double)RANDOM_MAX; - return (ret) * (p_to - p_from) + p_from; + return randd() * (p_to - p_from) + p_from; } float RandomPCG::random(float p_from, float p_to) { - unsigned int r = rand(); - float ret = (float)r / (float)RANDOM_MAX; - return (ret) * (p_to - p_from) + p_from; + return randf() * (p_to - p_from) + p_from; } diff --git a/core/math/random_pcg.h b/core/math/random_pcg.h index 0d1b311c0d..aa25914638 100644 --- a/core/math/random_pcg.h +++ b/core/math/random_pcg.h @@ -37,6 +37,28 @@ #include "thirdparty/misc/pcg.h" +#if defined(__GNUC__) || (_llvm_has_builtin(__builtin_clz)) +#define CLZ32(x) __builtin_clz(x) +#elif defined(_MSC_VER) +#include "intrin.h" +static int __bsr_clz32(uint32_t x) { + unsigned long index; + _BitScanReverse(&index, x); + return 31 - index; +} +#define CLZ32(x) __bsr_clz32(x) +#else +#endif + +#if defined(__GNUC__) || (_llvm_has_builtin(__builtin_ldexp) && _llvm_has_builtin(__builtin_ldexpf)) +#define LDEXP(s, e) __builtin_ldexp(s, e) +#define LDEXPF(s, e) __builtin_ldexpf(s, e) +#else +#include "math.h" +#define LDEXP(s, e) ldexp(s, e) +#define LDEXPF(s, e) ldexp(s, e) +#endif + class RandomPCG { pcg32_random_t pcg; uint64_t current_seed; // seed with this to get the same state @@ -60,8 +82,44 @@ public: current_seed = pcg.state; return pcg32_random_r(&pcg); } - _FORCE_INLINE_ double randd() { return (double)rand() / (double)RANDOM_MAX; } - _FORCE_INLINE_ float randf() { return (float)rand() / (float)RANDOM_MAX; } + + // Obtaining floating point numbers in [0, 1] range with "good enough" uniformity. + // These functions sample the output of rand() as the fraction part of an infinite binary number, + // with some tricks applied to reduce ops and branching: + // 1. Instead of shifting to the first 1 and connecting random bits, we simply set the MSB and LSB to 1. + // Provided that the RNG is actually uniform bit by bit, this should have the exact same effect. + // 2. In order to compensate for exponent info loss, we count zeros from another random number, + // and just add that to the initial offset. + // This has the same probability as counting and shifting an actual bit stream: 2^-n for n zeroes. + // For all numbers above 2^-96 (2^-64 for floats), the functions should be uniform. + // However, all numbers below that threshold are floored to 0. + // The thresholds are chosen to minimize rand() calls while keeping the numbers within a totally subjective quality standard. + // If clz or ldexp isn't available, fall back to bit truncation for performance, sacrificing uniformity. + _FORCE_INLINE_ double randd() { +#if defined(CLZ32) + uint32_t proto_exp_offset = rand(); + if (unlikely(proto_exp_offset == 0)) { + return 0; + } + uint64_t significand = (((uint64_t)rand()) << 32) | rand() | 0x8000000000000001U; + return LDEXP((double)significand, -64 - CLZ32(proto_exp_offset)); +#else +#pragma message("RandomPCG::randd - intrinsic clz is not available, falling back to bit truncation") + return (double)(((((uint64_t)rand()) << 32) | rand()) & 0x1FFFFFFFFFFFFFU) / (double)0x1FFFFFFFFFFFFFU; +#endif + } + _FORCE_INLINE_ float randf() { +#if defined(CLZ32) + uint32_t proto_exp_offset = rand(); + if (unlikely(proto_exp_offset == 0)) { + return 0; + } + return LDEXPF((float)(rand() | 0x80000001), -32 - CLZ32(proto_exp_offset)); +#else +#pragma message("RandomPCG::randf - intrinsic clz is not available, falling back to bit truncation") + return (float)(rand() & 0xFFFFFF) / (float)0xFFFFFF; +#endif + } _FORCE_INLINE_ double randfn(double p_mean, double p_deviation) { return p_mean + p_deviation * (cos(Math_TAU * randd()) * sqrt(-2.0 * log(randd()))); // Box-Muller transform diff --git a/core/math/rect2.cpp b/core/math/rect2.cpp index fea128afbd..e31776672e 100644 --- a/core/math/rect2.cpp +++ b/core/math/rect2.cpp @@ -30,6 +30,11 @@ #include "core/math/transform_2d.h" // Includes rect2.h but Rect2 needs Transform2D +bool Rect2::is_equal_approx(const Rect2 &p_rect) const { + + return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size); +} + bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const { real_t min = 0, max = 1; diff --git a/core/math/rect2.h b/core/math/rect2.h index 901d372132..71221ffb1b 100644 --- a/core/math/rect2.h +++ b/core/math/rect2.h @@ -67,7 +67,7 @@ struct Rect2 { if (p_point.x < position.x) { real_t d = position.x - p_point.x; - dist = inside ? d : MIN(dist, d); + dist = d; inside = false; } if (p_point.y < position.y) { @@ -99,11 +99,11 @@ struct Rect2 { inline bool encloses(const Rect2 &p_rect) const { return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && - ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && - ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); + ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) && + ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y)); } - inline bool has_no_area() const { + _FORCE_INLINE_ bool has_no_area() const { return (size.x <= 0 || size.y <= 0); } @@ -153,8 +153,7 @@ struct Rect2 { return true; } - - inline bool no_area() const { return (size.width <= 0 || size.height <= 0); } + bool is_equal_approx(const Rect2 &p_rect) const; bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; } bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; } @@ -189,7 +188,7 @@ struct Rect2 { return g; } - inline Rect2 expand(const Vector2 &p_vector) const { + _FORCE_INLINE_ Rect2 expand(const Vector2 &p_vector) const { Rect2 r = *this; r.expand_to(p_vector); @@ -215,7 +214,7 @@ struct Rect2 { size = end - begin; } - inline Rect2 abs() const { + _FORCE_INLINE_ Rect2 abs() const { return Rect2(Point2(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs()); } @@ -265,7 +264,7 @@ struct Rect2i { ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); } - inline bool has_no_area() const { + _FORCE_INLINE_ bool has_no_area() const { return (size.x <= 0 || size.y <= 0); } @@ -316,8 +315,6 @@ struct Rect2i { return true; } - bool no_area() { return (size.width <= 0 || size.height <= 0); } - bool operator==(const Rect2i &p_rect) const { return position == p_rect.position && size == p_rect.size; } bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; } @@ -331,6 +328,33 @@ struct Rect2i { return g; } + inline Rect2i grow_margin(Margin p_margin, int p_amount) const { + Rect2i g = *this; + g = g.grow_individual((MARGIN_LEFT == p_margin) ? p_amount : 0, + (MARGIN_TOP == p_margin) ? p_amount : 0, + (MARGIN_RIGHT == p_margin) ? p_amount : 0, + (MARGIN_BOTTOM == p_margin) ? p_amount : 0); + return g; + } + + inline Rect2i grow_individual(int p_left, int p_top, int p_right, int p_bottom) const { + + Rect2i g = *this; + g.position.x -= p_left; + g.position.y -= p_top; + g.size.width += p_left + p_right; + g.size.height += p_top + p_bottom; + + return g; + } + + _FORCE_INLINE_ Rect2i expand(const Vector2i &p_vector) const { + + Rect2i r = *this; + r.expand_to(p_vector); + return r; + } + inline void expand_to(const Point2i &p_vector) { Point2i begin = position; diff --git a/core/math/transform.cpp b/core/math/transform.cpp index 7ff7cac914..5dcc6ab9f0 100644 --- a/core/math/transform.cpp +++ b/core/math/transform.cpp @@ -182,6 +182,11 @@ Transform Transform::orthonormalized() const { return _copy; } +bool Transform::is_equal_approx(const Transform &p_transform) const { + + return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin); +} + bool Transform::operator==(const Transform &p_transform) const { return (basis == p_transform.basis && origin == p_transform.origin); @@ -213,3 +218,8 @@ Transform::Transform(const Basis &p_basis, const Vector3 &p_origin) : basis(p_basis), origin(p_origin) { } + +Transform::Transform(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz) { + basis = Basis(xx, xy, xz, yx, yy, yz, zx, zy, zz); + origin = Vector3(ox, oy, oz); +} diff --git a/core/math/transform.h b/core/math/transform.h index 2f43f6b035..da65a183cf 100644 --- a/core/math/transform.h +++ b/core/math/transform.h @@ -34,10 +34,7 @@ #include "core/math/aabb.h" #include "core/math/basis.h" #include "core/math/plane.h" - -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ +#include "core/pool_vector.h" class Transform { public: @@ -73,6 +70,7 @@ public: void orthonormalize(); Transform orthonormalized() const; + bool is_equal_approx(const Transform &p_transform) const; bool operator==(const Transform &p_transform) const; bool operator!=(const Transform &p_transform) const; @@ -86,6 +84,9 @@ public: _FORCE_INLINE_ AABB xform(const AABB &p_aabb) const; _FORCE_INLINE_ AABB xform_inv(const AABB &p_aabb) const; + _FORCE_INLINE_ PoolVector<Vector3> xform(const PoolVector<Vector3> &p_array) const; + _FORCE_INLINE_ PoolVector<Vector3> xform_inv(const PoolVector<Vector3> &p_array) const; + void operator*=(const Transform &p_transform); Transform operator*(const Transform &p_transform) const; @@ -108,6 +109,7 @@ public: operator String() const; + Transform(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz); Transform(const Basis &p_basis, const Vector3 &p_origin = Vector3()); Transform() {} }; @@ -157,22 +159,29 @@ _FORCE_INLINE_ Plane Transform::xform_inv(const Plane &p_plane) const { } _FORCE_INLINE_ AABB Transform::xform(const AABB &p_aabb) const { - /* define vertices */ - Vector3 x = basis.get_axis(0) * p_aabb.size.x; - Vector3 y = basis.get_axis(1) * p_aabb.size.y; - Vector3 z = basis.get_axis(2) * p_aabb.size.z; - Vector3 pos = xform(p_aabb.position); - //could be even further optimized - AABB new_aabb; - new_aabb.position = pos; - new_aabb.expand_to(pos + x); - new_aabb.expand_to(pos + y); - new_aabb.expand_to(pos + z); - new_aabb.expand_to(pos + x + y); - new_aabb.expand_to(pos + x + z); - new_aabb.expand_to(pos + y + z); - new_aabb.expand_to(pos + x + y + z); - return new_aabb; + + /* http://dev.theomader.com/transform-bounding-boxes/ */ + Vector3 min = p_aabb.position; + Vector3 max = p_aabb.position + p_aabb.size; + Vector3 tmin, tmax; + for (int i = 0; i < 3; i++) { + tmin[i] = tmax[i] = origin[i]; + for (int j = 0; j < 3; j++) { + real_t e = basis[i][j] * min[j]; + real_t f = basis[i][j] * max[j]; + if (e < f) { + tmin[i] += e; + tmax[i] += f; + } else { + tmin[i] += f; + tmax[i] += e; + } + } + } + AABB r_aabb; + r_aabb.position = tmin; + r_aabb.size = tmax - tmin; + return r_aabb; } _FORCE_INLINE_ AABB Transform::xform_inv(const AABB &p_aabb) const { @@ -201,4 +210,32 @@ _FORCE_INLINE_ AABB Transform::xform_inv(const AABB &p_aabb) const { return ret; } +PoolVector<Vector3> Transform::xform(const PoolVector<Vector3> &p_array) const { + + PoolVector<Vector3> array; + array.resize(p_array.size()); + + PoolVector<Vector3>::Read r = p_array.read(); + PoolVector<Vector3>::Write w = array.write(); + + for (int i = 0; i < p_array.size(); ++i) { + w[i] = xform(r[i]); + } + return array; +} + +PoolVector<Vector3> Transform::xform_inv(const PoolVector<Vector3> &p_array) const { + + PoolVector<Vector3> array; + array.resize(p_array.size()); + + PoolVector<Vector3>::Read r = p_array.read(); + PoolVector<Vector3>::Write w = array.write(); + + for (int i = 0; i < p_array.size(); ++i) { + w[i] = xform_inv(r[i]); + } + return array; +} + #endif // TRANSFORM_H diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index 7d00158f3d..a1c0814637 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -106,7 +106,7 @@ Size2 Transform2D::get_scale() const { return Size2(elements[0].length(), det_sign * elements[1].length()); } -void Transform2D::set_scale(Size2 &p_scale) { +void Transform2D::set_scale(const Size2 &p_scale) { elements[0].normalize(); elements[1].normalize(); elements[0] *= p_scale.x; @@ -147,6 +147,7 @@ void Transform2D::orthonormalize() { elements[0] = x; elements[1] = y; } + Transform2D Transform2D::orthonormalized() const { Transform2D on = *this; @@ -154,6 +155,11 @@ Transform2D Transform2D::orthonormalized() const { return on; } +bool Transform2D::is_equal_approx(const Transform2D &p_transform) const { + + return elements[0].is_equal_approx(p_transform.elements[0]) && elements[1].is_equal_approx(p_transform.elements[1]) && elements[2].is_equal_approx(p_transform.elements[2]); +} + bool Transform2D::operator==(const Transform2D &p_transform) const { for (int i = 0; i < 3; i++) { diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index b9e7a36fb3..0ec39a1765 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -32,6 +32,7 @@ #define TRANSFORM_2D_H #include "core/math/rect2.h" // also includes vector2, math_funcs, and ustring +#include "core/pool_vector.h" struct Transform2D { // Warning #1: basis of Transform2D is stored differently from Basis. In terms of elements array, the basis matrix looks like "on paper": @@ -81,7 +82,7 @@ struct Transform2D { real_t basis_determinant() const; Size2 get_scale() const; - void set_scale(Size2 &p_scale); + void set_scale(const Size2 &p_scale); _FORCE_INLINE_ const Vector2 &get_origin() const { return elements[2]; } _FORCE_INLINE_ void set_origin(const Vector2 &p_origin) { elements[2] = p_origin; } @@ -95,6 +96,7 @@ struct Transform2D { void orthonormalize(); Transform2D orthonormalized() const; + bool is_equal_approx(const Transform2D &p_transform) const; bool operator==(const Transform2D &p_transform) const; bool operator!=(const Transform2D &p_transform) const; @@ -110,6 +112,8 @@ struct Transform2D { _FORCE_INLINE_ Vector2 xform_inv(const Vector2 &p_vec) const; _FORCE_INLINE_ Rect2 xform(const Rect2 &p_rect) const; _FORCE_INLINE_ Rect2 xform_inv(const Rect2 &p_rect) const; + _FORCE_INLINE_ PoolVector<Vector2> xform(const PoolVector<Vector2> &p_array) const; + _FORCE_INLINE_ PoolVector<Vector2> xform_inv(const PoolVector<Vector2> &p_array) const; operator String() const; @@ -199,4 +203,32 @@ Rect2 Transform2D::xform_inv(const Rect2 &p_rect) const { return new_rect; } +PoolVector<Vector2> Transform2D::xform(const PoolVector<Vector2> &p_array) const { + + PoolVector<Vector2> array; + array.resize(p_array.size()); + + PoolVector<Vector2>::Read r = p_array.read(); + PoolVector<Vector2>::Write w = array.write(); + + for (int i = 0; i < p_array.size(); ++i) { + w[i] = xform(r[i]); + } + return array; +} + +PoolVector<Vector2> Transform2D::xform_inv(const PoolVector<Vector2> &p_array) const { + + PoolVector<Vector2> array; + array.resize(p_array.size()); + + PoolVector<Vector2>::Read r = p_array.read(); + PoolVector<Vector2>::Write w = array.write(); + + for (int i = 0; i < p_array.size(); ++i) { + w[i] = xform_inv(r[i]); + } + return array; +} + #endif // TRANSFORM_2D_H diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp index 83784a1fa7..546981be44 100644 --- a/core/math/triangle_mesh.cpp +++ b/core/math/triangle_mesh.cpp @@ -182,7 +182,7 @@ void TriangleMesh::create(const PoolVector<Vector3> &p_faces) { int max_alloc = fc; _create_bvh(bw.ptr(), bwp.ptr(), 0, fc, 1, max_depth, max_alloc); - bw = PoolVector<BVH>::Write(); //clearup + bw.release(); //clearup bvh.resize(max_alloc); //resize back valid = true; @@ -751,7 +751,7 @@ PoolVector<Face3> TriangleMesh::get_faces() const { } } - w = PoolVector<Face3>::Write(); + w.release(); return faces; } diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h index ee7bf0f6b5..8b01080852 100644 --- a/core/math/triangle_mesh.h +++ b/core/math/triangle_mesh.h @@ -97,7 +97,7 @@ public: PoolVector<Triangle> get_triangles() const { return triangles; } PoolVector<Vector3> get_vertices() const { return vertices; } - void get_indices(PoolVector<int> *p_triangles_indices) const; + void get_indices(PoolVector<int> *r_triangles_indices) const; void create(const PoolVector<Vector3> &p_faces); TriangleMesh(); diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index 5c1ea5943d..fbedeb8eb2 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -98,6 +98,11 @@ real_t Vector2::cross(const Vector2 &p_other) const { return x * p_other.y - y * p_other.x; } +Vector2 Vector2::sign() const { + + return Vector2(SGN(x), SGN(y)); +} + Vector2 Vector2::floor() const { return Vector2(Math::floor(x), Math::floor(y)); @@ -121,6 +126,14 @@ Vector2 Vector2::rotated(real_t p_by) const { return v; } +Vector2 Vector2::posmod(const real_t p_mod) const { + return Vector2(Math::fposmod(x, p_mod), Math::fposmod(y, p_mod)); +} + +Vector2 Vector2::posmodv(const Vector2 &p_modv) const { + return Vector2(Math::fposmod(x, p_modv.x), Math::fposmod(y, p_modv.y)); +} + Vector2 Vector2::project(const Vector2 &p_b) const { return p_b * (dot(p_b) / p_b.length_squared()); } @@ -164,6 +177,13 @@ Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, c return out; } +Vector2 Vector2::move_toward(const Vector2 &p_to, const real_t p_delta) const { + Vector2 v = *this; + Vector2 vd = p_to - v; + real_t len = vd.length(); + return len <= p_delta || len < CMP_EPSILON ? p_to : v + vd / len * p_delta; +} + // slide returns the component of the vector along the given plane, specified by its normal vector. Vector2 Vector2::slide(const Vector2 &p_normal) const { #ifdef MATH_CHECKS @@ -183,6 +203,10 @@ Vector2 Vector2::reflect(const Vector2 &p_normal) const { return 2.0 * p_normal * this->dot(p_normal) - *this; } +bool Vector2::is_equal_approx(const Vector2 &p_v) const { + return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y); +} + /* Vector2i */ Vector2i Vector2i::operator+(const Vector2i &p_v) const { diff --git a/core/math/vector2.h b/core/math/vector2.h index a20326f667..7fcaadab00 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -38,6 +38,11 @@ struct Vector2i; struct Vector2 { + enum Axis { + AXIS_X, + AXIS_Y, + }; + union { real_t x; real_t width; @@ -65,9 +70,12 @@ struct Vector2 { real_t distance_squared_to(const Vector2 &p_vector2) const; real_t angle_to(const Vector2 &p_vector2) const; real_t angle_to_point(const Vector2 &p_vector2) const; + _FORCE_INLINE_ Vector2 direction_to(const Vector2 &p_b) const; real_t dot(const Vector2 &p_other) const; real_t cross(const Vector2 &p_other) const; + Vector2 posmod(const real_t p_mod) const; + Vector2 posmodv(const Vector2 &p_modv) const; Vector2 project(const Vector2 &p_b) const; Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const; @@ -78,11 +86,14 @@ struct Vector2 { _FORCE_INLINE_ Vector2 linear_interpolate(const Vector2 &p_b, real_t p_t) const; _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_b, real_t p_t) const; Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const; + Vector2 move_toward(const Vector2 &p_to, const real_t p_delta) const; Vector2 slide(const Vector2 &p_normal) const; Vector2 bounce(const Vector2 &p_normal) const; Vector2 reflect(const Vector2 &p_normal) const; + bool is_equal_approx(const Vector2 &p_v) const; + Vector2 operator+(const Vector2 &p_v) const; void operator+=(const Vector2 &p_v); Vector2 operator-(const Vector2 &p_v) const; @@ -98,14 +109,17 @@ struct Vector2 { Vector2 operator/(const real_t &rvalue) const; void operator/=(const real_t &rvalue); + void operator/=(const Vector2 &rvalue) { *this = *this / rvalue; } Vector2 operator-() const; bool operator==(const Vector2 &p_vec2) const; bool operator!=(const Vector2 &p_vec2) const; - bool operator<(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } - bool operator<=(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y <= p_vec2.y) : (x <= p_vec2.x); } + bool operator<(const Vector2 &p_vec2) const { return Math::is_equal_approx(x, p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } + bool operator>(const Vector2 &p_vec2) const { return Math::is_equal_approx(x, p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); } + bool operator<=(const Vector2 &p_vec2) const { return Math::is_equal_approx(x, p_vec2.x) ? (y <= p_vec2.y) : (x < p_vec2.x); } + bool operator>=(const Vector2 &p_vec2) const { return Math::is_equal_approx(x, p_vec2.x) ? (y >= p_vec2.y) : (x > p_vec2.x); } real_t angle() const; @@ -126,6 +140,7 @@ struct Vector2 { return Vector2(y, -x); } + Vector2 sign() const; Vector2 floor() const; Vector2 ceil() const; Vector2 round() const; @@ -138,10 +153,7 @@ struct Vector2 { x = p_x; y = p_y; } - _FORCE_INLINE_ Vector2() { - x = 0; - y = 0; - } + _FORCE_INLINE_ Vector2() { x = y = 0; } }; _FORCE_INLINE_ Vector2 Vector2::plane_project(real_t p_d, const Vector2 &p_vec) const { @@ -236,6 +248,12 @@ Vector2 Vector2::slerp(const Vector2 &p_b, real_t p_t) const { return rotated(theta * p_t); } +Vector2 Vector2::direction_to(const Vector2 &p_b) const { + Vector2 ret(p_b.x - x, p_b.y - y); + ret.normalize(); + return ret; +} + Vector2 Vector2::linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t) { Vector2 res = p_a; @@ -253,6 +271,11 @@ typedef Vector2 Point2; struct Vector2i { + enum Axis { + AXIS_X, + AXIS_Y, + }; + union { int x; int width; diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index 1c28934422..e3211c8fb1 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -127,6 +127,33 @@ Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, c return out; } +Vector3 Vector3::move_toward(const Vector3 &p_to, const real_t p_delta) const { + Vector3 v = *this; + Vector3 vd = p_to - v; + real_t len = vd.length(); + return len <= p_delta || len < CMP_EPSILON ? p_to : v + vd / len * p_delta; +} + +Basis Vector3::outer(const Vector3 &p_b) const { + + Vector3 row0(x * p_b.x, x * p_b.y, x * p_b.z); + Vector3 row1(y * p_b.x, y * p_b.y, y * p_b.z); + Vector3 row2(z * p_b.x, z * p_b.y, z * p_b.z); + + return Basis(row0, row1, row2); +} + +Basis Vector3::to_diagonal_matrix() const { + return Basis(x, 0, 0, + 0, y, 0, + 0, 0, z); +} + +bool Vector3::is_equal_approx(const Vector3 &p_v) const { + + return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z); +} + Vector3::operator String() const { return (rtos(x) + ", " + rtos(y) + ", " + rtos(z)); diff --git a/core/math/vector3.h b/core/math/vector3.h index b11838d16e..43fa09ffac 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -31,9 +31,7 @@ #ifndef VECTOR3_H #define VECTOR3_H -#include "core/math/math_defs.h" #include "core/math/math_funcs.h" -#include "core/typedefs.h" #include "core/ustring.h" class Basis; @@ -94,11 +92,12 @@ struct Vector3 { _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_b, real_t p_t) const; Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const; Vector3 cubic_interpolaten(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const; + Vector3 move_toward(const Vector3 &p_to, const real_t p_delta) const; _FORCE_INLINE_ Vector3 cross(const Vector3 &p_b) const; _FORCE_INLINE_ real_t dot(const Vector3 &p_b) const; - _FORCE_INLINE_ Basis outer(const Vector3 &p_b) const; - _FORCE_INLINE_ Basis to_diagonal_matrix() const; + Basis outer(const Vector3 &p_b) const; + Basis to_diagonal_matrix() const; _FORCE_INLINE_ Vector3 abs() const; _FORCE_INLINE_ Vector3 floor() const; @@ -109,14 +108,19 @@ struct Vector3 { _FORCE_INLINE_ real_t distance_to(const Vector3 &p_b) const; _FORCE_INLINE_ real_t distance_squared_to(const Vector3 &p_b) const; + _FORCE_INLINE_ Vector3 posmod(const real_t p_mod) const; + _FORCE_INLINE_ Vector3 posmodv(const Vector3 &p_modv) const; _FORCE_INLINE_ Vector3 project(const Vector3 &p_b) const; _FORCE_INLINE_ real_t angle_to(const Vector3 &p_b) const; + _FORCE_INLINE_ Vector3 direction_to(const Vector3 &p_b) const; _FORCE_INLINE_ Vector3 slide(const Vector3 &p_normal) const; _FORCE_INLINE_ Vector3 bounce(const Vector3 &p_normal) const; _FORCE_INLINE_ Vector3 reflect(const Vector3 &p_normal) const; + bool is_equal_approx(const Vector3 &p_v) const; + /* Operators */ _FORCE_INLINE_ Vector3 &operator+=(const Vector3 &p_v); @@ -139,20 +143,19 @@ struct Vector3 { _FORCE_INLINE_ bool operator!=(const Vector3 &p_v) const; _FORCE_INLINE_ bool operator<(const Vector3 &p_v) const; _FORCE_INLINE_ bool operator<=(const Vector3 &p_v) const; + _FORCE_INLINE_ bool operator>(const Vector3 &p_v) const; + _FORCE_INLINE_ bool operator>=(const Vector3 &p_v) const; operator String() const; - _FORCE_INLINE_ Vector3() { x = y = z = 0; } _FORCE_INLINE_ Vector3(real_t p_x, real_t p_y, real_t p_z) { x = p_x; y = p_y; z = p_z; } + _FORCE_INLINE_ Vector3() { x = y = z = 0; } }; -// Should be included after class definition, otherwise we get circular refs -#include "core/math/basis.h" - Vector3 Vector3::cross(const Vector3 &p_b) const { Vector3 ret( @@ -168,21 +171,6 @@ real_t Vector3::dot(const Vector3 &p_b) const { return x * p_b.x + y * p_b.y + z * p_b.z; } -Basis Vector3::outer(const Vector3 &p_b) const { - - Vector3 row0(x * p_b.x, x * p_b.y, x * p_b.z); - Vector3 row1(y * p_b.x, y * p_b.y, y * p_b.z); - Vector3 row2(z * p_b.x, z * p_b.y, z * p_b.z); - - return Basis(row0, row1, row2); -} - -Basis Vector3::to_diagonal_matrix() const { - return Basis(x, 0, 0, - 0, y, 0, - 0, 0, z); -} - Vector3 Vector3::abs() const { return Vector3(Math::abs(x), Math::abs(y), Math::abs(z)); @@ -217,12 +205,8 @@ Vector3 Vector3::linear_interpolate(const Vector3 &p_b, real_t p_t) const { } Vector3 Vector3::slerp(const Vector3 &p_b, real_t p_t) const { -#ifdef MATH_CHECKS - ERR_FAIL_COND_V(!is_normalized(), Vector3()); -#endif - real_t theta = angle_to(p_b); - return rotated(cross(p_b), theta * p_t); + return rotated(cross(p_b).normalized(), theta * p_t); } real_t Vector3::distance_to(const Vector3 &p_b) const { @@ -235,6 +219,14 @@ real_t Vector3::distance_squared_to(const Vector3 &p_b) const { return (p_b - *this).length_squared(); } +Vector3 Vector3::posmod(const real_t p_mod) const { + return Vector3(Math::fposmod(x, p_mod), Math::fposmod(y, p_mod), Math::fposmod(z, p_mod)); +} + +Vector3 Vector3::posmodv(const Vector3 &p_modv) const { + return Vector3(Math::fposmod(x, p_modv.x), Math::fposmod(y, p_modv.y), Math::fposmod(z, p_modv.z)); +} + Vector3 Vector3::project(const Vector3 &p_b) const { return p_b * (dot(p_b) / p_b.length_squared()); } @@ -244,6 +236,12 @@ real_t Vector3::angle_to(const Vector3 &p_b) const { return Math::atan2(cross(p_b).length(), dot(p_b)); } +Vector3 Vector3::direction_to(const Vector3 &p_b) const { + Vector3 ret(p_b.x - x, p_b.y - y, p_b.z - z); + ret.normalize(); + return ret; +} + /* Operators */ Vector3 &Vector3::operator+=(const Vector3 &p_v) { @@ -334,17 +332,18 @@ Vector3 Vector3::operator-() const { bool Vector3::operator==(const Vector3 &p_v) const { - return (x == p_v.x && y == p_v.y && z == p_v.z); + return x == p_v.x && y == p_v.y && z == p_v.z; } bool Vector3::operator!=(const Vector3 &p_v) const { - return (x != p_v.x || y != p_v.y || z != p_v.z); + + return x != p_v.x || y != p_v.y || z != p_v.z; } bool Vector3::operator<(const Vector3 &p_v) const { - if (x == p_v.x) { - if (y == p_v.y) + if (Math::is_equal_approx(x, p_v.x)) { + if (Math::is_equal_approx(y, p_v.y)) return z < p_v.z; else return y < p_v.y; @@ -353,10 +352,22 @@ bool Vector3::operator<(const Vector3 &p_v) const { } } +bool Vector3::operator>(const Vector3 &p_v) const { + + if (Math::is_equal_approx(x, p_v.x)) { + if (Math::is_equal_approx(y, p_v.y)) + return z > p_v.z; + else + return y > p_v.y; + } else { + return x > p_v.x; + } +} + bool Vector3::operator<=(const Vector3 &p_v) const { - if (x == p_v.x) { - if (y == p_v.y) + if (Math::is_equal_approx(x, p_v.x)) { + if (Math::is_equal_approx(y, p_v.y)) return z <= p_v.z; else return y < p_v.y; @@ -365,6 +376,18 @@ bool Vector3::operator<=(const Vector3 &p_v) const { } } +bool Vector3::operator>=(const Vector3 &p_v) const { + + if (Math::is_equal_approx(x, p_v.x)) { + if (Math::is_equal_approx(y, p_v.y)) + return z >= p_v.z; + else + return y > p_v.y; + } else { + return x > p_v.x; + } +} + _FORCE_INLINE_ Vector3 vec3_cross(const Vector3 &p_a, const Vector3 &p_b) { return p_a.cross(p_b); @@ -395,13 +418,14 @@ real_t Vector3::length_squared() const { void Vector3::normalize() { - real_t l = length(); - if (l == 0) { + real_t lengthsq = length_squared(); + if (lengthsq == 0) { x = y = z = 0; } else { - x /= l; - y /= l; - z /= l; + real_t length = Math::sqrt(lengthsq); + x /= length; + y /= length; + z /= length; } } diff --git a/core/message_queue.cpp b/core/message_queue.cpp index c57bd4081c..a254da89c6 100644 --- a/core/message_queue.cpp +++ b/core/message_queue.cpp @@ -52,13 +52,12 @@ Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, const V type = ObjectDB::get_instance(p_id)->get_class(); print_line("Failed method: " + type + ":" + p_method + " target ID: " + itos(p_id)); statistics(); - ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings."); - ERR_FAIL_V(ERR_OUT_OF_MEMORY); + ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings."); } Message *msg = memnew_placement(&buffer[buffer_end], Message); msg->args = p_argcount; - msg->instance_ID = p_id; + msg->instance_id = p_id; msg->target = p_method; msg->type = TYPE_CALL; if (p_show_error) @@ -103,13 +102,12 @@ Error MessageQueue::push_set(ObjectID p_id, const StringName &p_prop, const Vari type = ObjectDB::get_instance(p_id)->get_class(); print_line("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id)); statistics(); - ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings."); - ERR_FAIL_V(ERR_OUT_OF_MEMORY); + ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings."); } Message *msg = memnew_placement(&buffer[buffer_end], Message); msg->args = 1; - msg->instance_ID = p_id; + msg->instance_id = p_id; msg->target = p_prop; msg->type = TYPE_SET; @@ -131,19 +129,15 @@ Error MessageQueue::push_notification(ObjectID p_id, int p_notification) { uint8_t room_needed = sizeof(Message); if ((buffer_end + room_needed) >= buffer_size) { - String type; - if (ObjectDB::get_instance(p_id)) - type = ObjectDB::get_instance(p_id)->get_class(); print_line("Failed notification: " + itos(p_notification) + " target ID: " + itos(p_id)); statistics(); - ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings."); - ERR_FAIL_V(ERR_OUT_OF_MEMORY); + ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings."); } Message *msg = memnew_placement(&buffer[buffer_end], Message); msg->type = TYPE_NOTIFICATION; - msg->instance_ID = p_id; + msg->instance_id = p_id; //msg->target; msg->notification = p_notification; @@ -177,7 +171,7 @@ void MessageQueue::statistics() { while (read_pos < buffer_end) { Message *message = (Message *)&buffer[read_pos]; - Object *target = ObjectDB::get_instance(message->instance_ID); + Object *target = ObjectDB::get_instance(message->instance_id); if (target != NULL) { @@ -256,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_PRINTS("Error calling deferred method: " + Variant::get_call_error_text(p_target, p_func, argptrs, p_argcount, ce) + "."); } } @@ -289,7 +283,7 @@ void MessageQueue::flush() { _THREAD_SAFE_UNLOCK_ - Object *target = ObjectDB::get_instance(message->instance_ID); + Object *target = ObjectDB::get_instance(message->instance_id); if (target != NULL) { @@ -302,10 +296,6 @@ void MessageQueue::flush() { _call_function(target, message->target, args, message->args, message->type & FLAG_SHOW_ERROR); - for (int i = 0; i < message->args; i++) { - args[i].~Variant(); - } - } break; case TYPE_NOTIFICATION: { @@ -319,11 +309,17 @@ void MessageQueue::flush() { // messages don't expect a return value target->set(message->target, *arg); - arg->~Variant(); } break; } } + if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) { + Variant *args = (Variant *)(message + 1); + for (int i = 0; i < message->args; i++) { + args[i].~Variant(); + } + } + message->~Message(); _THREAD_SAFE_LOCK_ @@ -341,7 +337,7 @@ bool MessageQueue::is_flushing() const { MessageQueue::MessageQueue() { - ERR_FAIL_COND(singleton != NULL); + ERR_FAIL_COND_MSG(singleton != NULL, "MessageQueue singleton already exist."); singleton = this; flushing = false; diff --git a/core/message_queue.h b/core/message_queue.h index 2515eb4a98..026d17ad3f 100644 --- a/core/message_queue.h +++ b/core/message_queue.h @@ -54,7 +54,7 @@ class MessageQueue { struct Message { - ObjectID instance_ID; + ObjectID instance_id; StringName target; int16_t type; union { diff --git a/core/method_bind.h b/core/method_bind.h index 5ea8adb7e0..7bb75e778f 100644 --- a/core/method_bind.h +++ b/core/method_bind.h @@ -38,10 +38,6 @@ #include <stdio.h> -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - #ifdef DEBUG_ENABLED #define DEBUG_METHODS_ENABLED #endif @@ -273,6 +269,8 @@ public: void set_argument_names(const Vector<StringName> &p_names); //set by class, db, can't be inferred otherwise Vector<StringName> get_argument_names() const; + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const = 0; + #endif void set_hint_flags(uint32_t p_hint) { hint_flags = p_hint; } uint32_t get_hint_flags() const { return hint_flags | (is_const() ? METHOD_FLAG_CONST : 0) | (is_vararg() ? METHOD_FLAG_VARARG : 0); } @@ -329,6 +327,10 @@ public: return _gen_argument_type_info(p_arg).type; } + virtual GodotTypeInfo::Metadata get_argument_meta(int) const { + return GodotTypeInfo::METADATA_NONE; + } + #else virtual Variant::Type _gen_argument_type(int p_arg) const { diff --git a/core/node_path.cpp b/core/node_path.cpp index 07ff765516..970ed100fe 100644 --- a/core/node_path.cpp +++ b/core/node_path.cpp @@ -269,7 +269,7 @@ NodePath NodePath::rel_path_to(const NodePath &p_np) const { NodePath NodePath::get_as_property_path() const { - if (!data->path.size()) { + if (!data || !data->path.size()) { return *this; } else { Vector<StringName> new_path = data->subpath; @@ -357,7 +357,7 @@ NodePath::NodePath(const String &p_path) { String path = p_path; Vector<StringName> subpath; - int absolute = (path[0] == '/') ? 1 : 0; + bool absolute = (path[0] == '/'); bool last_is_slash = true; bool has_slashes = false; int slices = 0; @@ -375,8 +375,7 @@ NodePath::NodePath(const String &p_path) { if (str == "") { if (path[i] == 0) continue; // Allow end-of-path : - ERR_EXPLAIN("Invalid NodePath: " + p_path); - ERR_FAIL(); + ERR_FAIL_MSG("Invalid NodePath '" + p_path + "'."); } subpath.push_back(str); @@ -387,7 +386,7 @@ NodePath::NodePath(const String &p_path) { path = path.substr(0, subpath_pos); } - for (int i = absolute; i < path.length(); i++) { + for (int i = (int)absolute; i < path.length(); i++) { if (path[i] == '/') { @@ -407,7 +406,7 @@ NodePath::NodePath(const String &p_path) { data = memnew(Data); data->refcount.init(); - data->absolute = absolute ? true : false; + data->absolute = absolute; data->has_slashes = has_slashes; data->subpath = subpath; data->hash_cache_valid = false; @@ -416,10 +415,10 @@ NodePath::NodePath(const String &p_path) { return; data->path.resize(slices); last_is_slash = true; - int from = absolute; + int from = (int)absolute; int slice = 0; - for (int i = absolute; i < path.length() + 1; i++) { + for (int i = (int)absolute; i < path.length() + 1; i++) { if (path[i] == '/' || path[i] == 0) { diff --git a/core/node_path.h b/core/node_path.h index 24725123d6..1b21c4ef1c 100644 --- a/core/node_path.h +++ b/core/node_path.h @@ -34,10 +34,6 @@ #include "core/string_name.h" #include "core/ustring.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - class NodePath { struct Data { diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h index e52d36a859..1a466e57f4 100644 --- a/core/oa_hash_map.h +++ b/core/oa_hash_map.h @@ -37,10 +37,11 @@ #include "core/os/memory.h" /** - * A HashMap implementation that uses open addressing with robinhood hashing. - * Robinhood hashing swaps out entries that have a smaller probing distance + * A HashMap implementation that uses open addressing with Robin Hood hashing. + * Robin Hood hashing swaps out entries that have a smaller probing distance * than the to-be-inserted entry, that evens out the average probing distance - * and enables faster lookups. + * and enables faster lookups. Backward shift deletion is employed to further + * improve the performance and to avoid infinite loops in rare cases. * * The entries are stored inplace, so huge keys or values might fill cache lines * a lot faster. @@ -60,26 +61,20 @@ private: uint32_t num_elements; static const uint32_t EMPTY_HASH = 0; - static const uint32_t DELETED_HASH_BIT = 1 << 31; - _FORCE_INLINE_ uint32_t _hash(const TKey &p_key) { + _FORCE_INLINE_ uint32_t _hash(const TKey &p_key) const { uint32_t hash = Hasher::hash(p_key); if (hash == EMPTY_HASH) { hash = EMPTY_HASH + 1; - } else if (hash & DELETED_HASH_BIT) { - hash &= ~DELETED_HASH_BIT; } return hash; } - _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash) { - p_hash = p_hash & ~DELETED_HASH_BIT; // we don't care if it was deleted or not - + _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash) const { uint32_t original_pos = p_hash % capacity; - - return p_pos - original_pos; + return (p_pos - original_pos + capacity) % capacity; } _FORCE_INLINE_ void _construct(uint32_t p_pos, uint32_t p_hash, const TKey &p_key, const TValue &p_value) { @@ -90,7 +85,7 @@ private: num_elements++; } - bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) { + bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const { uint32_t hash = _hash(p_key); uint32_t pos = hash % capacity; uint32_t distance = 0; @@ -133,14 +128,6 @@ private: // not an empty slot, let's check the probing length of the existing one uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos]); if (existing_probe_len < distance) { - - if (hashes[pos] & DELETED_HASH_BIT) { - // we found a place where we can fit in! - _construct(pos, hash, key, value); - - return; - } - SWAP(hash, hashes[pos]); SWAP(key, keys[pos]); SWAP(value, values[pos]); @@ -151,17 +138,17 @@ private: distance++; } } - void _resize_and_rehash() { + + void _resize_and_rehash(uint32_t p_new_capacity) { + + uint32_t old_capacity = capacity; + capacity = p_new_capacity; TKey *old_keys = keys; TValue *old_values = values; uint32_t *old_hashes = hashes; - uint32_t old_capacity = capacity; - - capacity = old_capacity * 2; num_elements = 0; - keys = memnew_arr(TKey, capacity); values = memnew_arr(TValue, capacity); hashes = memnew_arr(uint32_t, capacity); @@ -174,9 +161,6 @@ private: if (old_hashes[i] == EMPTY_HASH) { continue; } - if (old_hashes[i] & DELETED_HASH_BIT) { - continue; - } _insert_with_hash(old_hashes[i], old_keys[i], old_values[i]); } @@ -186,13 +170,37 @@ private: memdelete_arr(old_hashes); } + void _resize_and_rehash() { + _resize_and_rehash(capacity * 2); + } + public: _FORCE_INLINE_ uint32_t get_capacity() const { return capacity; } _FORCE_INLINE_ uint32_t get_num_elements() const { return num_elements; } + bool empty() const { + return num_elements == 0; + } + + void clear() { + + for (uint32_t i = 0; i < capacity; i++) { + + if (hashes[i] == EMPTY_HASH) { + continue; + } + + hashes[i] = EMPTY_HASH; + values[i].~TValue(); + keys[i].~TKey(); + } + + num_elements = 0; + } + void insert(const TKey &p_key, const TValue &p_value) { - if ((float)num_elements / (float)capacity > 0.9) { + if (num_elements + 1 > 0.9 * capacity) { _resize_and_rehash(); } @@ -219,7 +227,7 @@ public: * if r_data is not NULL then the value will be written to the object * it points to. */ - bool lookup(const TKey &p_key, TValue &r_data) { + bool lookup(const TKey &p_key, TValue &r_data) const { uint32_t pos = 0; bool exists = _lookup_pos(p_key, pos); @@ -232,7 +240,7 @@ public: return false; } - _FORCE_INLINE_ bool has(const TKey &p_key) { + _FORCE_INLINE_ bool has(const TKey &p_key) const { uint32_t _pos = 0; return _lookup_pos(p_key, _pos); } @@ -245,12 +253,33 @@ public: return; } - hashes[pos] |= DELETED_HASH_BIT; + uint32_t next_pos = (pos + 1) % capacity; + while (hashes[next_pos] != EMPTY_HASH && + _get_probe_length(next_pos, hashes[next_pos]) != 0) { + SWAP(hashes[next_pos], hashes[pos]); + SWAP(keys[next_pos], keys[pos]); + SWAP(values[next_pos], values[pos]); + pos = next_pos; + next_pos = (pos + 1) % capacity; + } + + hashes[pos] = EMPTY_HASH; values[pos].~TValue(); keys[pos].~TKey(); + num_elements--; } + /** + * reserves space for a number of elements, useful to avoid many resizes and rehashes + * if adding a known (possibly large) number of elements at once, must be larger than old + * capacity. + **/ + void reserve(uint32_t p_new_capacity) { + ERR_FAIL_COND(p_new_capacity < capacity); + _resize_and_rehash(p_new_capacity); + } + struct Iterator { bool valid; @@ -289,9 +318,6 @@ public: if (hashes[i] == EMPTY_HASH) { continue; } - if (hashes[i] & DELETED_HASH_BIT) { - continue; - } it.valid = true; it.key = &keys[i]; @@ -302,6 +328,9 @@ public: return it; } + OAHashMap(const OAHashMap &) = delete; // Delete the copy constructor so we don't get unexpected copies and dangling pointers. + OAHashMap &operator=(const OAHashMap &) = delete; // Same for assignment operator. + OAHashMap(uint32_t p_initial_capacity = 64) { capacity = p_initial_capacity; @@ -312,7 +341,7 @@ public: hashes = memnew_arr(uint32_t, p_initial_capacity); for (uint32_t i = 0; i < p_initial_capacity; i++) { - hashes[i] = 0; + hashes[i] = EMPTY_HASH; } } diff --git a/core/object.cpp b/core/object.cpp index 8b693f039c..ed3ae4f25d 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -436,7 +436,7 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid } else if (p_name == CoreStringNames::get_singleton()->_meta) { //set_meta(p_name,p_value); - metadata = p_value; + metadata = p_value.duplicate(); if (r_valid) *r_valid = true; return; @@ -474,7 +474,6 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid if (r_valid) *r_valid = false; - return; } Variant Object::get(const StringName &p_name, bool *r_valid) const { @@ -608,18 +607,16 @@ Variant Object::get_indexed(const Vector<StringName> &p_names, bool *r_valid) co } bool valid = false; - Variant current_value = get(p_names[0]); + Variant current_value = get(p_names[0], &valid); for (int i = 1; i < p_names.size(); i++) { current_value = current_value.get_named(p_names[i], &valid); - if (!valid) { - if (r_valid) - *r_valid = false; - return Variant(); - } + if (!valid) + break; } if (r_valid) - *r_valid = true; + *r_valid = valid; + return current_value; } @@ -712,20 +709,17 @@ static void _test_call_error(const StringName &p_func, const Variant::CallError break; case Variant::CallError::CALL_ERROR_INVALID_ARGUMENT: { - ERR_EXPLAIN("Error Calling Function: " + String(p_func) + " - Invalid type for argument " + itos(error.argument) + ", expected " + Variant::get_type_name(error.expected)); - ERR_FAIL(); + ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Invalid type for argument " + itos(error.argument) + ", expected " + Variant::get_type_name(error.expected) + "."); break; } case Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: { - ERR_EXPLAIN("Error Calling Function: " + String(p_func) + " - Too many arguments, expected " + itos(error.argument)); - ERR_FAIL(); + ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Too many arguments, expected " + itos(error.argument) + "."); break; } case Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: { - ERR_EXPLAIN("Error Calling Function: " + String(p_func) + " - Too few arguments, expected " + itos(error.argument)); - ERR_FAIL(); + ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Too few arguments, expected " + itos(error.argument) + "."); break; } case Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL: @@ -742,17 +736,9 @@ void Object::call_multilevel(const StringName &p_method, const Variant **p_args, if (p_method == CoreStringNames::get_singleton()->_free) { #ifdef DEBUG_ENABLED - if (Object::cast_to<Reference>(this)) { - ERR_EXPLAIN("Can't 'free' a reference."); - ERR_FAIL(); - return; - } + ERR_FAIL_COND_MSG(Object::cast_to<Reference>(this), "Can't 'free' a reference."); - if (_lock_index.get() > 1) { - ERR_EXPLAIN("Object is locked and can't be freed."); - ERR_FAIL(); - return; - } + ERR_FAIL_COND_MSG(_lock_index.get() > 1, "Object is locked and can't be freed."); #endif //must be here, must be before everything, @@ -812,11 +798,7 @@ bool Object::has_method(const StringName &p_method) const { MethodBind *method = ClassDB::get_method(get_class_name(), p_method); - if (method) { - return true; - } - - return false; + return method != NULL; } Variant Object::getvar(const Variant &p_key, bool *r_valid) const { @@ -844,8 +826,7 @@ Variant Object::callv(const StringName &p_method, const Array &p_args) { Variant::CallError ce; Variant ret = call(p_method, argptrs, p_args.size(), ce); if (ce.error != Variant::CallError::CALL_OK) { - ERR_EXPLAIN("Error calling method from 'callv': " + Variant::get_call_error_text(this, p_method, argptrs, p_args.size(), ce)); - ERR_FAIL_V(Variant()); + ERR_FAIL_V_MSG(Variant(), "Error calling method from 'callv': " + Variant::get_call_error_text(this, p_method, argptrs, p_args.size(), ce) + "."); } return ret; } @@ -897,15 +878,13 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a if (Object::cast_to<Reference>(this)) { r_error.argument = 0; r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; - ERR_EXPLAIN("Can't 'free' a reference."); - ERR_FAIL_V(Variant()); + ERR_FAIL_V_MSG(Variant(), "Can't 'free' a reference."); } if (_lock_index.get() > 1) { r_error.argument = 0; r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; - ERR_EXPLAIN("Object is locked and can't be freed."); - ERR_FAIL_V(Variant()); + ERR_FAIL_V_MSG(Variant(), "Object is locked and can't be freed."); } #endif @@ -956,6 +935,16 @@ void Object::notification(int p_notification, bool p_reversed) { } } +String Object::to_string() { + if (script_instance) { + bool valid; + String ret = script_instance->to_string(&valid); + if (valid) + return ret; + } + return "[" + get_class() + ":" + itos(get_instance_id()) + "]"; +} + void Object::_changed_callback(Object *p_changed, const char *p_prop) { } @@ -1059,6 +1048,10 @@ Variant Object::get_meta(const String &p_name) const { return metadata[p_name]; } +void Object::remove_meta(const String &p_name) { + metadata.erase(p_name); +} + Array Object::_get_property_list_bind() const { List<PropertyInfo> lpi; @@ -1107,9 +1100,9 @@ void Object::get_meta_list(List<String> *p_list) const { void Object::add_user_signal(const MethodInfo &p_signal) { - ERR_FAIL_COND(p_signal.name == ""); - ERR_FAIL_COND(ClassDB::has_signal(get_class_name(), p_signal.name)); - ERR_FAIL_COND(signal_map.has(p_signal.name)); + ERR_FAIL_COND_MSG(p_signal.name == "", "Signal name cannot be empty."); + ERR_FAIL_COND_MSG(ClassDB::has_signal(get_class_name(), p_signal.name), "User signal's name conflicts with a built-in signal of '" + get_class_name() + "'."); + ERR_FAIL_COND_MSG(signal_map.has(p_signal.name), "Trying to add already existing signal '" + p_signal.name + "'."); Signal s; s.user = p_signal; signal_map[p_signal.name] = s; @@ -1167,10 +1160,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int #ifdef DEBUG_ENABLED bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_name); //check in script - if (!signal_is_valid && !script.is_null() && !Ref<Script>(script)->has_script_signal(p_name)) { - ERR_EXPLAIN("Can't emit non-existing signal " + String("\"") + p_name + "\"."); - ERR_FAIL_V(ERR_UNAVAILABLE); - } + ERR_FAIL_COND_V_MSG(!signal_is_valid && !script.is_null() && !Ref<Script>(script)->has_script_signal(p_name), ERR_UNAVAILABLE, "Can't emit non-existing signal " + String("\"") + p_name + "\"."); #endif //not connected? just return return ERR_UNAVAILABLE; @@ -1235,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_PRINTS("Error calling method from signal '" + String(p_name) + "': " + Variant::get_call_error_text(target, c.method, args, argc, ce) + "."); err = ERR_METHOD_NOT_FOUND; } } @@ -1366,7 +1356,10 @@ Array Object::_get_incoming_connections() const { void Object::get_signal_list(List<MethodInfo> *p_signals) const { if (!script.is_null()) { - Ref<Script>(script)->get_script_signal_list(p_signals); + Ref<Script> scr = script; + if (scr.is_valid()) { + scr->get_script_signal_list(p_signals); + } } ClassDB::get_signal_list(get_class_name(), p_signals); @@ -1407,8 +1400,9 @@ void Object::get_signal_connection_list(const StringName &p_signal, List<Connect p_connections->push_back(s->slot_map.getv(i).conn); } -bool Object::has_persistent_signal_connections() const { +int Object::get_persistent_signal_connection_count() const { + int count = 0; const StringName *S = NULL; while ((S = signal_map.next(S))) { @@ -1416,13 +1410,13 @@ bool Object::has_persistent_signal_connections() const { const Signal *s = &signal_map[*S]; for (int i = 0; i < s->slot_map.size(); i++) { - - if (s->slot_map.getv(i).conn.flags & CONNECT_PERSIST) - return true; + if (s->slot_map.getv(i).conn.flags & CONNECT_PERSIST) { + count += 1; + } } } - return false; + return count; } void Object::get_signals_connected_to_this(List<Connection> *p_connections) const { @@ -1455,10 +1449,8 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str #endif } - if (!signal_is_valid) { - ERR_EXPLAIN("In Object of type '" + String(get_class()) + "': Attempt to connect nonexistent signal '" + p_signal + "' to method '" + p_to_object->get_class() + "." + p_to_method + "'"); - ERR_FAIL_COND_V(!signal_is_valid, ERR_INVALID_PARAMETER); - } + ERR_FAIL_COND_V_MSG(!signal_is_valid, ERR_INVALID_PARAMETER, "In Object of type '" + String(get_class()) + "': Attempt to connect nonexistent signal '" + p_signal + "' to method '" + p_to_object->get_class() + "." + p_to_method + "'."); + signal_map[p_signal] = Signal(); s = &signal_map[p_signal]; } @@ -1469,8 +1461,7 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str s->slot_map[target].reference_count++; return OK; } else { - ERR_EXPLAIN("Signal '" + p_signal + "' is already connected to given method '" + p_to_method + "' in that object."); - ERR_FAIL_COND_V(s->slot_map.has(target), ERR_INVALID_PARAMETER); + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Signal '" + p_signal + "' is already connected to given method '" + p_to_method + "' in that object."); } } @@ -1506,8 +1497,7 @@ bool Object::is_connected(const StringName &p_signal, Object *p_to_object, const if (!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal)) return false; - ERR_EXPLAIN("Nonexistent signal: " + p_signal); - ERR_FAIL_COND_V(!s, false); + ERR_FAIL_V_MSG(false, "Nonexistent signal: " + p_signal + "."); } Signal::Target target(p_to_object->get_instance_id(), p_to_method); @@ -1525,21 +1515,13 @@ void Object::_disconnect(const StringName &p_signal, Object *p_to_object, const ERR_FAIL_NULL(p_to_object); Signal *s = signal_map.getptr(p_signal); - if (!s) { - ERR_EXPLAIN("Nonexistent signal: " + p_signal); - ERR_FAIL_COND(!s); - } - if (s->lock > 0) { - ERR_EXPLAIN("Attempt to disconnect signal '" + p_signal + "' while emitting (locks: " + itos(s->lock) + ")"); - ERR_FAIL_COND(s->lock > 0); - } + ERR_FAIL_COND_MSG(!s, "Nonexistent signal: " + p_signal + "."); + + ERR_FAIL_COND_MSG(s->lock > 0, "Attempt to disconnect signal '" + p_signal + "' while emitting (locks: " + itos(s->lock) + ")."); Signal::Target target(p_to_object->get_instance_id(), p_to_method); - if (!s->slot_map.has(target)) { - ERR_EXPLAIN("Disconnecting nonexistent signal '" + p_signal + "', slot: " + itos(target._id) + ":" + target.method); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(!s->slot_map.has(target), "Disconnecting nonexistent signal '" + p_signal + "', slot: " + itos(target._id) + ":" + target.method + "."); Signal::Slot *slot = &s->slot_map[target]; @@ -1637,7 +1619,8 @@ void Object::_clear_internal_resource_paths(const Variant &p_var) { _clear_internal_resource_paths(d[E->get()]); } } break; - default: {} + default: { + } } } @@ -1673,7 +1656,7 @@ void Object::clear_internal_resource_paths() { void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("get_class"), &Object::get_class); - ClassDB::bind_method(D_METHOD("is_class", "type"), &Object::is_class); + ClassDB::bind_method(D_METHOD("is_class", "class"), &Object::is_class); ClassDB::bind_method(D_METHOD("set", "property", "value"), &Object::_set_bind); ClassDB::bind_method(D_METHOD("get", "property"), &Object::_get_bind); ClassDB::bind_method(D_METHOD("set_indexed", "property", "value"), &Object::_set_indexed_bind); @@ -1681,24 +1664,20 @@ void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("get_property_list"), &Object::_get_property_list_bind); ClassDB::bind_method(D_METHOD("get_method_list"), &Object::_get_method_list_bind); ClassDB::bind_method(D_METHOD("notification", "what", "reversed"), &Object::notification, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("to_string"), &Object::to_string); ClassDB::bind_method(D_METHOD("get_instance_id"), &Object::get_instance_id); ClassDB::bind_method(D_METHOD("set_script", "script"), &Object::set_script); ClassDB::bind_method(D_METHOD("get_script"), &Object::get_script); ClassDB::bind_method(D_METHOD("set_meta", "name", "value"), &Object::set_meta); + ClassDB::bind_method(D_METHOD("remove_meta", "name"), &Object::remove_meta); ClassDB::bind_method(D_METHOD("get_meta", "name"), &Object::get_meta); ClassDB::bind_method(D_METHOD("has_meta", "name"), &Object::has_meta); ClassDB::bind_method(D_METHOD("get_meta_list"), &Object::_get_meta_list_bind); - //todo reimplement this per language so all 5 arguments can be called - - //ClassDB::bind_method(D_METHOD("call","method","arg1","arg2","arg3","arg4"),&Object::_call_bind,DEFVAL(Variant()),DEFVAL(Variant()),DEFVAL(Variant()),DEFVAL(Variant())); - //ClassDB::bind_method(D_METHOD("call_deferred","method","arg1","arg2","arg3","arg4"),&Object::_call_deferred_bind,DEFVAL(Variant()),DEFVAL(Variant()),DEFVAL(Variant()),DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("add_user_signal", "signal", "arguments"), &Object::_add_user_signal, DEFVAL(Array())); ClassDB::bind_method(D_METHOD("has_user_signal", "signal"), &Object::_has_user_signal); - //ClassDB::bind_method(D_METHOD("emit_signal","signal","arguments"),&Object::_emit_signal,DEFVAL(Array())); { MethodInfo mi; @@ -1767,6 +1746,7 @@ void Object::_bind_methods() { #endif BIND_VMETHOD(MethodInfo("_init")); + BIND_VMETHOD(MethodInfo(Variant::STRING, "_to_string")); BIND_CONSTANT(NOTIFICATION_POSTINITIALIZE); BIND_CONSTANT(NOTIFICATION_PREDELETE); @@ -1938,8 +1918,8 @@ Object::Object() { _class_ptr = NULL; _block_signals = false; _predelete_ok = 0; - _instance_ID = 0; - _instance_ID = ObjectDB::add_instance(this); + _instance_id = 0; + _instance_id = ObjectDB::add_instance(this); _can_translate = true; _is_queued_for_deletion = false; instance_binding_count = 0; @@ -1968,10 +1948,7 @@ Object::~Object() { Signal *s = &signal_map[*S]; - if (s->lock) { - ERR_EXPLAIN("Attempt to delete an object in the middle of a signal emission from it"); - ERR_CONTINUE(s->lock > 0); - } + ERR_CONTINUE_MSG(s->lock > 0, "Attempt to delete an object in the middle of a signal emission from it."); //brute force disconnect for performance int slot_count = s->slot_map.size(); @@ -1993,7 +1970,7 @@ Object::~Object() { } ObjectDB::remove_instance(this); - _instance_ID = 0; + _instance_id = 0; _predelete_ok = 2; if (!ScriptServer::are_languages_finished()) { @@ -2041,10 +2018,10 @@ void ObjectDB::remove_instance(Object *p_object) { rw_lock->write_unlock(); } -Object *ObjectDB::get_instance(ObjectID p_instance_ID) { +Object *ObjectDB::get_instance(ObjectID p_instance_id) { rw_lock->read_lock(); - Object **obj = instances.getptr(p_instance_ID); + Object **obj = instances.getptr(p_instance_id); rw_lock->read_unlock(); if (!obj) diff --git a/core/object.h b/core/object.h index e8a6178052..ac8620757c 100644 --- a/core/object.h +++ b/core/object.h @@ -58,7 +58,7 @@ enum PropertyHint { PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc" PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout") PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer) - PROPERTY_HINT_SPRITE_FRAME, + PROPERTY_HINT_SPRITE_FRAME, // FIXME: Obsolete: drop whenever we can break compat. Keeping now for GDNative compat. PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer) PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags) PROPERTY_HINT_LAYERS_2D_RENDER, @@ -88,6 +88,7 @@ enum PropertyHint { PROPERTY_HINT_PROPERTY_OF_SCRIPT, ///< a property of a script & base PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send PROPERTY_HINT_NODE_PATH_VALID_TYPES, + PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog PROPERTY_HINT_MAX, // When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit }; @@ -103,7 +104,8 @@ enum PropertyUsageFlags { PROPERTY_USAGE_INTERNATIONALIZED = 64, //hint for internationalized strings PROPERTY_USAGE_GROUP = 128, //used for grouping props in the editor PROPERTY_USAGE_CATEGORY = 256, - //those below are deprecated thanks to ClassDB's now class value cache + // FIXME: Drop in 4.0, possibly reorder other flags? + // Those below are deprecated thanks to ClassDB's now class value cache //PROPERTY_USAGE_STORE_IF_NONZERO = 512, //only store if nonzero //PROPERTY_USAGE_STORE_IF_NONONE = 1024, //only store if false PROPERTY_USAGE_NO_INSTANCE_STATE = 2048, @@ -120,6 +122,7 @@ enum PropertyUsageFlags { PROPERTY_USAGE_HIGH_END_GFX = 1 << 22, PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 23, PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 24, + PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 25, // Used in inspector to increment property when keyed in animation player PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK, PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED, @@ -129,6 +132,7 @@ enum PropertyUsageFlags { #define ADD_SIGNAL(m_signal) ClassDB::add_signal(get_class_static(), m_signal) #define ADD_PROPERTY(m_property, m_setter, m_getter) ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter)) #define ADD_PROPERTYI(m_property, m_setter, m_getter, m_index) ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter), m_index) +#define ADD_PROPERTY_DEFAULT(m_property, m_default) ClassDB::set_property_default_value(get_class_static(), m_property, m_default) #define ADD_GROUP(m_name, m_prefix) ClassDB::add_property_group(get_class_static(), m_name, m_prefix) struct PropertyInfo { @@ -177,6 +181,15 @@ struct PropertyInfo { usage(PROPERTY_USAGE_DEFAULT) { } + bool operator==(const PropertyInfo &p_info) const { + return ((type == p_info.type) && + (name == p_info.name) && + (class_name == p_info.class_name) && + (hint == p_info.hint) && + (hint_string == p_info.hint_string) && + (usage == p_info.usage)); + } + bool operator<(const PropertyInfo &p_info) const { return name < p_info.name; } @@ -464,7 +477,7 @@ private: bool _block_signals; int _predelete_ok; Set<Object *> change_receptors; - ObjectID _instance_ID; + ObjectID _instance_id; bool _predelete(); void _postinitialize(); bool _can_translate; @@ -576,7 +589,7 @@ public: bool _is_gpl_reversed() const { return false; } - _FORCE_INLINE_ ObjectID get_instance_id() const { return _instance_ID; } + _FORCE_INLINE_ ObjectID get_instance_id() const { return _instance_id; } // this is used for editors void add_change_receptor(Object *p_receptor); @@ -658,6 +671,7 @@ public: void call_multilevel(const StringName &p_name, VARIANT_ARG_LIST); // C++ helper void notification(int p_notification, bool p_reversed = false); + String to_string(); //used mainly by script, get and set all INCLUDING string virtual Variant getvar(const Variant &p_key, bool *r_valid = NULL) const; @@ -672,6 +686,7 @@ public: bool has_meta(const String &p_name) const; void set_meta(const String &p_name, const Variant &p_value); + void remove_meta(const String &p_name); Variant get_meta(const String &p_name) const; void get_meta_list(List<String> *p_list) const; @@ -692,7 +707,7 @@ public: void get_signal_list(List<MethodInfo> *p_signals) const; void get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const; void get_all_signal_connections(List<Connection> *p_connections) const; - bool has_persistent_signal_connections() const; + int get_persistent_signal_connection_count() const; void get_signals_connected_to_this(List<Connection> *p_connections) const; Error connect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, const Vector<Variant> &p_binds = Vector<Variant>(), uint32_t p_flags = 0); @@ -774,13 +789,18 @@ class ObjectDB { public: typedef void (*DebugFunc)(Object *p_obj); - static Object *get_instance(ObjectID p_instance_ID); + static Object *get_instance(ObjectID p_instance_id); static void debug_objects(DebugFunc p_func); static int get_object_count(); _FORCE_INLINE_ static bool instance_validate(Object *p_ptr) { + rw_lock->read_lock(); + + bool exists = instance_checks.has(p_ptr); + + rw_lock->read_unlock(); - return instance_checks.has(p_ptr); + return exists; } }; diff --git a/core/ordered_hash_map.h b/core/ordered_hash_map.h index 09d43d6797..2c18de92be 100644 --- a/core/ordered_hash_map.h +++ b/core/ordered_hash_map.h @@ -274,6 +274,10 @@ public: inline bool empty() const { return list.empty(); } inline int size() const { return list.size(); } + const void *id() const { + return list.id(); + } + void clear() { map.clear(); list.clear(); diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp index 2c1c655175..e7496055ec 100644 --- a/core/os/dir_access.cpp +++ b/core/os/dir_access.cpp @@ -43,8 +43,6 @@ String DirAccess::_get_root_path() const { case ACCESS_USERDATA: return OS::get_singleton()->get_user_data_dir(); default: return ""; } - - return ""; } String DirAccess::_get_root_string() const { @@ -54,8 +52,6 @@ String DirAccess::_get_root_string() const { case ACCESS_USERDATA: return "user://"; default: return ""; } - - return ""; } int DirAccess::get_current_drive() { @@ -183,14 +179,6 @@ Error DirAccess::make_dir_recursive(String p_dir) { return OK; } -String DirAccess::get_next(bool *p_is_dir) { - - String next = get_next(); - if (p_is_dir) - *p_is_dir = current_is_dir(); - return next; -} - String DirAccess::fix_path(String p_path) const { switch (_access_type) { @@ -256,7 +244,7 @@ DirAccess *DirAccess::open(const String &p_path, Error *r_error) { DirAccess *da = create_for_path(p_path); - ERR_FAIL_COND_V(!da, NULL); + ERR_FAIL_COND_V_MSG(!da, NULL, "Cannot create DirAccess for path '" + p_path + "'."); Error err = da->change_dir(p_path); if (r_error) *r_error = err; @@ -330,7 +318,7 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) { if (err == OK && p_chmod_flags != -1) { fdst->close(); - err = fdst->_chmod(p_to, p_chmod_flags); + err = FileAccess::set_unix_permissions(p_to, p_chmod_flags); // If running on a platform with no chmod support (i.e., Windows), don't fail if (err == ERR_UNAVAILABLE) err = OK; @@ -373,12 +361,12 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag if (current_is_dir()) dirs.push_back(n); else { - String rel_path = n; + const String &rel_path = n; if (!n.is_rel_path()) { list_dir_end(); return ERR_BUG; } - Error err = copy(get_current_dir() + "/" + n, p_to + rel_path, p_chmod_flags); + Error err = copy(get_current_dir().plus_file(n), p_to + rel_path, p_chmod_flags); if (err) { list_dir_end(); return err; @@ -396,39 +384,36 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag String target_dir = p_to + rel_path; if (!p_target_da->dir_exists(target_dir)) { Error err = p_target_da->make_dir(target_dir); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + target_dir + "'."); } Error err = change_dir(E->get()); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + E->get() + "'."); + err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags); if (err) { change_dir(".."); - ERR_PRINT("Failed to copy recursively"); - return err; + ERR_FAIL_V_MSG(err, "Failed to copy recursively."); } err = change_dir(".."); - if (err) { - ERR_PRINT("Failed to go back"); - return err; - } + ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to go back."); } return OK; } Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags) { - ERR_FAIL_COND_V(!dir_exists(p_from), ERR_FILE_NOT_FOUND); + ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist."); DirAccess *target_da = DirAccess::create_for_path(p_to); - ERR_FAIL_COND_V(!target_da, ERR_CANT_CREATE); + ERR_FAIL_COND_V_MSG(!target_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_to + "'."); if (!target_da->dir_exists(p_to)) { Error err = target_da->make_dir_recursive(p_to); if (err) { memdelete(target_da); } - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + p_to + "'."); } if (!p_to.ends_with("/")) { diff --git a/core/os/dir_access.h b/core/os/dir_access.h index bde19bd5ae..d3eb1e13f6 100644 --- a/core/os/dir_access.h +++ b/core/os/dir_access.h @@ -34,10 +34,6 @@ #include "core/typedefs.h" #include "core/ustring.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - //@ TODO, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies class DirAccess { public: @@ -71,7 +67,6 @@ protected: public: virtual Error list_dir_begin() = 0; ///< This starts dir listing - virtual String get_next(bool *p_is_dir); // compatibility virtual String get_next() = 0; virtual bool current_is_dir() const = 0; virtual bool current_is_hidden() const = 0; @@ -98,6 +93,18 @@ public: virtual Error rename(String p_from, String p_to) = 0; virtual Error remove(String p_name) = 0; + // Meant for editor code when we want to quickly remove a file without custom + // handling (e.g. removing a cache file). + static void remove_file_or_error(String p_path) { + DirAccess *da = create(ACCESS_FILESYSTEM); + if (da->file_exists(p_path)) { + if (da->remove(p_path) != OK) { + ERR_FAIL_MSG("Cannot remove file or directory: " + p_path); + } + } + memdelete(da); + } + virtual String get_filesystem_type() const = 0; static String get_full_path(const String &p_path, AccessType p_access); static DirAccess *create_for_path(const String &p_path); diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index 39d9f45bd7..738e597730 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -30,14 +30,12 @@ #include "file_access.h" +#include "core/crypto/crypto_core.h" #include "core/io/file_access_pack.h" #include "core/io/marshalls.h" #include "core/os/os.h" #include "core/project_settings.h" -#include "thirdparty/misc/md5.h" -#include "thirdparty/misc/sha256.h" - FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = { 0, 0 }; FileAccess::FileCloseFailNotify FileAccess::close_fail_notify = NULL; @@ -500,13 +498,36 @@ uint64_t FileAccess::get_modified_time(const String &p_file) { return 0; FileAccess *fa = create_for_path(p_file); - ERR_FAIL_COND_V(!fa, 0); + ERR_FAIL_COND_V_MSG(!fa, 0, "Cannot create FileAccess for path '" + p_file + "'."); uint64_t mt = fa->_get_modified_time(p_file); memdelete(fa); return mt; } +uint32_t FileAccess::get_unix_permissions(const String &p_file) { + + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && PackedData::get_singleton()->has_path(p_file)) + return 0; + + FileAccess *fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(!fa, 0, "Cannot create FileAccess for path '" + p_file + "'."); + + uint32_t mt = fa->_get_unix_permissions(p_file); + memdelete(fa); + return mt; +} + +Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissions) { + + FileAccess *fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'."); + + Error err = fa->_set_unix_permissions(p_file, p_permissions); + memdelete(fa); + return err; +} + void FileAccess::store_string(const String &p_string) { if (p_string.length() == 0) @@ -571,10 +592,15 @@ void FileAccess::store_buffer(const uint8_t *p_src, int p_length) { store_8(p_src[i]); } -Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path) { +Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_error) { - FileAccess *f = FileAccess::open(p_path, READ); - ERR_FAIL_COND_V(!f, Vector<uint8_t>()); + FileAccess *f = FileAccess::open(p_path, READ, r_error); + if (!f) { + if (r_error) { // if error requested, do not throw error + return Vector<uint8_t>(); + } + ERR_FAIL_V_MSG(Vector<uint8_t>(), "Can't open file from path '" + String(p_path) + "'."); + } Vector<uint8_t> data; data.resize(f->get_len()); f->get_buffer(data.ptrw(), data.size()); @@ -582,14 +608,33 @@ Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path) { return data; } +String FileAccess::get_file_as_string(const String &p_path, Error *r_error) { + + Error err; + Vector<uint8_t> array = get_file_as_array(p_path, &err); + if (r_error) { + *r_error = err; + } + if (err != OK) { + if (r_error) { + return String(); + } + ERR_FAIL_V_MSG(String(), "Can't get file as string from path '" + String(p_path) + "'."); + } + + String ret; + ret.parse_utf8((const char *)array.ptr(), array.size()); + return ret; +} + String FileAccess::get_md5(const String &p_file) { FileAccess *f = FileAccess::open(p_file, READ); if (!f) return String(); - MD5_CTX md5; - MD5Init(&md5); + CryptoCore::MD5Context ctx; + ctx.start(); unsigned char step[32768]; @@ -598,24 +643,24 @@ String FileAccess::get_md5(const String &p_file) { int br = f->get_buffer(step, 32768); if (br > 0) { - MD5Update(&md5, step, br); + ctx.update(step, br); } if (br < 4096) break; } - MD5Final(&md5); - - String ret = String::md5(md5.digest); + unsigned char hash[16]; + ctx.finish(hash); memdelete(f); - return ret; + + return String::md5(hash); } String FileAccess::get_multiple_md5(const Vector<String> &p_file) { - MD5_CTX md5; - MD5Init(&md5); + CryptoCore::MD5Context ctx; + ctx.start(); for (int i = 0; i < p_file.size(); i++) { FileAccess *f = FileAccess::open(p_file[i], READ); @@ -628,7 +673,7 @@ String FileAccess::get_multiple_md5(const Vector<String> &p_file) { int br = f->get_buffer(step, 32768); if (br > 0) { - MD5Update(&md5, step, br); + ctx.update(step, br); } if (br < 4096) break; @@ -636,11 +681,10 @@ String FileAccess::get_multiple_md5(const Vector<String> &p_file) { memdelete(f); } - MD5Final(&md5); - - String ret = String::md5(md5.digest); + unsigned char hash[16]; + ctx.finish(hash); - return ret; + return String::md5(hash); } String FileAccess::get_sha256(const String &p_file) { @@ -649,8 +693,8 @@ String FileAccess::get_sha256(const String &p_file) { if (!f) return String(); - sha256_context sha256; - sha256_init(&sha256); + CryptoCore::SHA256Context ctx; + ctx.start(); unsigned char step[32768]; @@ -659,15 +703,14 @@ String FileAccess::get_sha256(const String &p_file) { int br = f->get_buffer(step, 32768); if (br > 0) { - sha256_hash(&sha256, step, br); + ctx.update(step, br); } if (br < 4096) break; } unsigned char hash[32]; - - sha256_done(&sha256, hash); + ctx.finish(hash); memdelete(f); return String::hex_encode_buffer(hash, 32); diff --git a/core/os/file_access.h b/core/os/file_access.h index c65b75369c..4930eae35a 100644 --- a/core/os/file_access.h +++ b/core/os/file_access.h @@ -56,6 +56,9 @@ public: bool endian_swap; bool real_is_double; + virtual uint32_t _get_unix_permissions(const String &p_file) = 0; + virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) = 0; + protected: String fix_path(const String &p_path) const; virtual Error _open(const String &p_path, int p_mode_flags) = 0; ///< open a file @@ -148,14 +151,14 @@ public: virtual Error reopen(const String &p_path, int p_mode_flags); ///< does not change the AccessType - virtual Error _chmod(const String &p_path, int p_mod) { return ERR_UNAVAILABLE; } - static FileAccess *create(AccessType p_access); /// Create a file access (for the current platform) this is the only portable way of accessing files. static FileAccess *create_for_path(const String &p_path); static FileAccess *open(const String &p_path, int p_mode_flags, Error *r_error = NULL); /// Create a file access (for the current platform) this is the only portable way of accessing files. static CreateFunc get_create_func(AccessType p_access); static bool exists(const String &p_name); ///< return true if a file exists static uint64_t get_modified_time(const String &p_file); + static uint32_t get_unix_permissions(const String &p_file); + static Error set_unix_permissions(const String &p_file, uint32_t p_permissions); static void set_backup_save(bool p_enable) { backup_save = p_enable; }; static bool is_backup_save_enabled() { return backup_save; }; @@ -164,7 +167,8 @@ public: static String get_sha256(const String &p_file); static String get_multiple_md5(const Vector<String> &p_file); - static Vector<uint8_t> get_file_as_array(const String &p_path); + static Vector<uint8_t> get_file_as_array(const String &p_path, Error *r_error = NULL); + static String get_file_as_string(const String &p_path, Error *r_error = NULL); template <class T> static void make_default(AccessType p_access) { diff --git a/core/os/input.cpp b/core/os/input.cpp index caa9fb1493..51cb41b184 100644 --- a/core/os/input.cpp +++ b/core/os/input.cpp @@ -34,6 +34,10 @@ #include "core/os/os.h" #include "core/project_settings.h" +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif + Input *Input::singleton = NULL; Input *Input::get_singleton() { @@ -76,6 +80,7 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string); ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0)); ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration); + ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms"), &Input::vibrate_handheld, DEFVAL(500)); ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity); ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer); ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer); @@ -89,6 +94,7 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("action_press", "action", "strength"), &Input::action_press, DEFVAL(1.f)); ClassDB::bind_method(D_METHOD("action_release", "action"), &Input::action_release); ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Input::set_default_cursor_shape, DEFVAL(CURSOR_ARROW)); + ClassDB::bind_method(D_METHOD("get_current_cursor_shape"), &Input::get_current_cursor_shape); ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event); ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &Input::set_use_accumulated_input); @@ -122,6 +128,8 @@ void Input::_bind_methods() { void Input::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { #ifdef TOOLS_ENABLED + const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\""; + String pf = p_function; if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || pf == "is_action_just_pressed" || pf == "is_action_just_released" || pf == "get_action_strength")) { @@ -135,7 +143,7 @@ void Input::get_argument_options(const StringName &p_function, int p_idx, List<S continue; String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length()); - r_options->push_back("\"" + name + "\""); + r_options->push_back(quote_style + name + quote_style); } } #endif diff --git a/core/os/input.h b/core/os/input.h index c8b80b28d0..a12ded176b 100644 --- a/core/os/input.h +++ b/core/os/input.h @@ -100,6 +100,7 @@ public: virtual uint64_t get_joy_vibration_timestamp(int p_device) = 0; virtual void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0) = 0; virtual void stop_joy_vibration(int p_device) = 0; + virtual void vibrate_handheld(int p_duration_ms = 500) = 0; virtual Point2 get_mouse_position() const = 0; virtual Point2 get_last_mouse_speed() const = 0; @@ -121,10 +122,10 @@ public: virtual bool is_emulating_touch_from_mouse() const = 0; virtual bool is_emulating_mouse_from_touch() const = 0; - virtual CursorShape get_default_cursor_shape() = 0; + virtual CursorShape get_default_cursor_shape() const = 0; virtual void set_default_cursor_shape(CursorShape p_shape) = 0; + virtual CursorShape get_current_cursor_shape() const = 0; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) = 0; - virtual void set_mouse_in_window(bool p_in_window) = 0; virtual String get_joy_button_string(int p_button) = 0; virtual String get_joy_axis_string(int p_axis) = 0; diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index 25a5c2afeb..f09a904953 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -49,11 +49,11 @@ bool InputEvent::is_action(const StringName &p_action) const { return InputMap::get_singleton()->event_is_action(Ref<InputEvent>((InputEvent *)this), p_action); } -bool InputEvent::is_action_pressed(const StringName &p_action) const { +bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo) const { bool pressed; bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed); - return valid && pressed && !is_echo(); + return valid && pressed && (p_allow_echo || !is_echo()); } bool InputEvent::is_action_released(const StringName &p_action) const { @@ -112,7 +112,7 @@ void InputEvent::_bind_methods() { ClassDB::bind_method(D_METHOD("get_device"), &InputEvent::get_device); ClassDB::bind_method(D_METHOD("is_action", "action"), &InputEvent::is_action); - ClassDB::bind_method(D_METHOD("is_action_pressed", "action"), &InputEvent::is_action_pressed); + ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "allow_echo"), &InputEvent::is_action_pressed, DEFVAL(false)); ClassDB::bind_method(D_METHOD("is_action_released", "action"), &InputEvent::is_action_released); ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &InputEvent::get_action_strength); @@ -314,7 +314,7 @@ bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool *p_pressed if (p_pressed != NULL) *p_pressed = key->is_pressed(); if (p_strength != NULL) - *p_strength = (*p_pressed) ? 1.0f : 0.0f; + *p_strength = (p_pressed != NULL && *p_pressed) ? 1.0f : 0.0f; } return match; } @@ -450,7 +450,7 @@ bool InputEventMouseButton::is_doubleclick() const { Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { - Vector2 g = p_xform.xform(get_global_position()); + Vector2 g = get_global_position(); Vector2 l = p_xform.xform(get_position() + p_local_ofs); Ref<InputEventMouseButton> mb; @@ -483,7 +483,7 @@ bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event, bool *p if (p_pressed != NULL) *p_pressed = mb->is_pressed(); if (p_strength != NULL) - *p_strength = (*p_pressed) ? 1.0f : 0.0f; + *p_strength = (p_pressed != NULL && *p_pressed) ? 1.0f : 0.0f; } return match; @@ -557,10 +557,31 @@ InputEventMouseButton::InputEventMouseButton() { //////////////////////////////////////////// +void InputEventMouseMotion::set_tilt(const Vector2 &p_tilt) { + + tilt = p_tilt; +} + +Vector2 InputEventMouseMotion::get_tilt() const { + + return tilt; +} + +void InputEventMouseMotion::set_pressure(float p_pressure) { + + pressure = p_pressure; +} + +float InputEventMouseMotion::get_pressure() const { + + return pressure; +} + void InputEventMouseMotion::set_relative(const Vector2 &p_relative) { relative = p_relative; } + Vector2 InputEventMouseMotion::get_relative() const { return relative; @@ -570,6 +591,7 @@ void InputEventMouseMotion::set_speed(const Vector2 &p_speed) { speed = p_speed; } + Vector2 InputEventMouseMotion::get_speed() const { return speed; @@ -577,7 +599,7 @@ Vector2 InputEventMouseMotion::get_speed() const { Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { - Vector2 g = p_xform.xform(get_global_position()); + Vector2 g = get_global_position(); Vector2 l = p_xform.xform(get_position() + p_local_ofs); Vector2 r = p_xform.basis_xform(get_relative()); Vector2 s = p_xform.basis_xform(get_speed()); @@ -590,6 +612,8 @@ Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, co mm->set_modifiers_from_event(this); mm->set_position(l); + mm->set_pressure(get_pressure()); + mm->set_tilt(get_tilt()); mm->set_global_position(g); mm->set_button_mask(get_button_mask()); @@ -665,17 +689,27 @@ bool InputEventMouseMotion::accumulate(const Ref<InputEvent> &p_event) { void InputEventMouseMotion::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_tilt", "tilt"), &InputEventMouseMotion::set_tilt); + ClassDB::bind_method(D_METHOD("get_tilt"), &InputEventMouseMotion::get_tilt); + + ClassDB::bind_method(D_METHOD("set_pressure", "pressure"), &InputEventMouseMotion::set_pressure); + ClassDB::bind_method(D_METHOD("get_pressure"), &InputEventMouseMotion::get_pressure); + ClassDB::bind_method(D_METHOD("set_relative", "relative"), &InputEventMouseMotion::set_relative); ClassDB::bind_method(D_METHOD("get_relative"), &InputEventMouseMotion::get_relative); ClassDB::bind_method(D_METHOD("set_speed", "speed"), &InputEventMouseMotion::set_speed); ClassDB::bind_method(D_METHOD("get_speed"), &InputEventMouseMotion::get_speed); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "tilt"), "set_tilt", "get_tilt"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pressure"), "set_pressure", "get_pressure"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "relative"), "set_relative", "get_relative"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "speed"), "set_speed", "get_speed"); } InputEventMouseMotion::InputEventMouseMotion() { + + pressure = 0; } //////////////////////////////////////// @@ -717,8 +751,17 @@ bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool * bool pressed = same_direction ? Math::abs(jm->get_axis_value()) >= p_deadzone : false; if (p_pressed != NULL) *p_pressed = pressed; - if (p_strength != NULL) - *p_strength = pressed ? CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, Math::abs(jm->get_axis_value())), 0.0f, 1.0f) : 0.0f; + if (p_strength != NULL) { + if (pressed) { + if (p_deadzone == 1.0f) { + *p_strength = 1.0f; + } else { + *p_strength = CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, Math::abs(jm->get_axis_value())), 0.0f, 1.0f); + } + } else { + *p_strength = 0.0f; + } + } } return match; } @@ -786,7 +829,7 @@ bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool * if (p_pressed != NULL) *p_pressed = jb->is_pressed(); if (p_strength != NULL) - *p_strength = (*p_pressed) ? 1.0f : 0.0f; + *p_strength = (p_pressed != NULL && *p_pressed) ? 1.0f : 0.0f; } return match; @@ -1001,6 +1044,14 @@ bool InputEventAction::is_pressed() const { return pressed; } +void InputEventAction::set_strength(float p_strength) { + strength = CLAMP(p_strength, 0.0f, 1.0f); +} + +float InputEventAction::get_strength() const { + return strength; +} + bool InputEventAction::shortcut_match(const Ref<InputEvent> &p_event) const { if (p_event.is_null()) return false; @@ -1024,7 +1075,7 @@ bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool *p_pres if (p_pressed != NULL) *p_pressed = act->pressed; if (p_strength != NULL) - *p_strength = (*p_pressed) ? 1.0f : 0.0f; + *p_strength = (p_pressed != NULL && *p_pressed) ? 1.0f : 0.0f; } return match; } @@ -1042,14 +1093,19 @@ void InputEventAction::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &InputEventAction::set_pressed); //ClassDB::bind_method(D_METHOD("is_pressed"), &InputEventAction::is_pressed); + ClassDB::bind_method(D_METHOD("set_strength", "strength"), &InputEventAction::set_strength); + ClassDB::bind_method(D_METHOD("get_strength"), &InputEventAction::get_strength); + // ClassDB::bind_method(D_METHOD("is_action", "name"), &InputEventAction::is_action); ADD_PROPERTY(PropertyInfo(Variant::STRING, "action"), "set_action", "get_action"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "strength", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_strength", "get_strength"); } InputEventAction::InputEventAction() { pressed = false; + strength = 1.0f; } ///////////////////////////// diff --git a/core/os/input_event.h b/core/os/input_event.h index ba01516519..a4db618bfe 100644 --- a/core/os/input_event.h +++ b/core/os/input_event.h @@ -38,10 +38,6 @@ #include "core/ustring.h" /** - @author Juan Linietsky <reduzio@gmail.com> -*/ - -/** * Input Event classes. These are used in the main loop. * The events are pretty obvious. */ @@ -117,6 +113,16 @@ enum JoystickList { JOY_WII_MINUS = JOY_BUTTON_10, JOY_WII_PLUS = JOY_BUTTON_11, + JOY_VR_GRIP = JOY_BUTTON_2, + JOY_VR_PAD = JOY_BUTTON_14, + JOY_VR_TRIGGER = JOY_BUTTON_15, + + JOY_OCULUS_AX = JOY_BUTTON_7, + JOY_OCULUS_BY = JOY_BUTTON_1, + JOY_OCULUS_MENU = JOY_BUTTON_3, + + JOY_OPENVR_MENU = JOY_BUTTON_1, + // end of history JOY_AXIS_0 = 0, @@ -139,6 +145,12 @@ enum JoystickList { JOY_ANALOG_L2 = JOY_AXIS_6, JOY_ANALOG_R2 = JOY_AXIS_7, + + JOY_VR_ANALOG_TRIGGER = JOY_AXIS_2, + JOY_VR_ANALOG_GRIP = JOY_AXIS_4, + + JOY_OPENVR_TOUCHPADX = JOY_AXIS_0, + JOY_OPENVR_TOUCHPADY = JOY_AXIS_1, }; enum MidiMessageList { @@ -157,7 +169,7 @@ enum MidiMessageList { */ class InputEvent : public Resource { - GDCLASS(InputEvent, Resource) + GDCLASS(InputEvent, Resource); int device; @@ -172,7 +184,7 @@ public: int get_device() const; bool is_action(const StringName &p_action) const; - bool is_action_pressed(const StringName &p_action) const; + bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false) const; bool is_action_released(const StringName &p_action) const; float get_action_strength(const StringName &p_action) const; @@ -194,7 +206,7 @@ public: }; class InputEventWithModifiers : public InputEvent { - GDCLASS(InputEventWithModifiers, InputEvent) + GDCLASS(InputEventWithModifiers, InputEvent); bool shift; bool alt; @@ -240,7 +252,7 @@ public: class InputEventKey : public InputEventWithModifiers { - GDCLASS(InputEventKey, InputEventWithModifiers) + GDCLASS(InputEventKey, InputEventWithModifiers); bool pressed; /// otherwise release @@ -279,7 +291,7 @@ public: class InputEventMouse : public InputEventWithModifiers { - GDCLASS(InputEventMouse, InputEventWithModifiers) + GDCLASS(InputEventMouse, InputEventWithModifiers); int button_mask; @@ -304,7 +316,7 @@ public: class InputEventMouseButton : public InputEventMouse { - GDCLASS(InputEventMouseButton, InputEventMouse) + GDCLASS(InputEventMouseButton, InputEventMouse); float factor; int button_index; @@ -338,7 +350,10 @@ public: class InputEventMouseMotion : public InputEventMouse { - GDCLASS(InputEventMouseMotion, InputEventMouse) + GDCLASS(InputEventMouseMotion, InputEventMouse); + + Vector2 tilt; + float pressure; Vector2 relative; Vector2 speed; @@ -346,6 +361,12 @@ protected: static void _bind_methods(); public: + void set_tilt(const Vector2 &p_tilt); + Vector2 get_tilt() const; + + void set_pressure(float p_pressure); + float get_pressure() const; + void set_relative(const Vector2 &p_relative); Vector2 get_relative() const; @@ -362,7 +383,7 @@ public: class InputEventJoypadMotion : public InputEvent { - GDCLASS(InputEventJoypadMotion, InputEvent) + GDCLASS(InputEventJoypadMotion, InputEvent); int axis; ///< Joypad axis float axis_value; ///< -1 to 1 @@ -387,7 +408,7 @@ public: }; class InputEventJoypadButton : public InputEvent { - GDCLASS(InputEventJoypadButton, InputEvent) + GDCLASS(InputEventJoypadButton, InputEvent); int button_index; bool pressed; @@ -415,7 +436,7 @@ public: }; class InputEventScreenTouch : public InputEvent { - GDCLASS(InputEventScreenTouch, InputEvent) + GDCLASS(InputEventScreenTouch, InputEvent); int index; Vector2 pos; bool pressed; @@ -441,7 +462,7 @@ public: class InputEventScreenDrag : public InputEvent { - GDCLASS(InputEventScreenDrag, InputEvent) + GDCLASS(InputEventScreenDrag, InputEvent); int index; Vector2 pos; Vector2 relative; @@ -471,10 +492,11 @@ public: class InputEventAction : public InputEvent { - GDCLASS(InputEventAction, InputEvent) + GDCLASS(InputEventAction, InputEvent); StringName action; bool pressed; + float strength; protected: static void _bind_methods(); @@ -486,6 +508,9 @@ public: void set_pressed(bool p_pressed); virtual bool is_pressed() const; + void set_strength(float p_strength); + float get_strength() const; + virtual bool is_action(const StringName &p_action) const; virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; @@ -499,7 +524,7 @@ public: class InputEventGesture : public InputEventWithModifiers { - GDCLASS(InputEventGesture, InputEventWithModifiers) + GDCLASS(InputEventGesture, InputEventWithModifiers); Vector2 pos; @@ -513,7 +538,7 @@ public: class InputEventMagnifyGesture : public InputEventGesture { - GDCLASS(InputEventMagnifyGesture, InputEventGesture) + GDCLASS(InputEventMagnifyGesture, InputEventGesture); real_t factor; protected: @@ -531,7 +556,7 @@ public: class InputEventPanGesture : public InputEventGesture { - GDCLASS(InputEventPanGesture, InputEventGesture) + GDCLASS(InputEventPanGesture, InputEventGesture); Vector2 delta; protected: @@ -548,7 +573,7 @@ public: }; class InputEventMIDI : public InputEvent { - GDCLASS(InputEventMIDI, InputEvent) + GDCLASS(InputEventMIDI, InputEvent); int channel; int message; diff --git a/core/os/keyboard.h b/core/os/keyboard.h index 58a0807579..5c8a2e90e9 100644 --- a/core/os/keyboard.h +++ b/core/os/keyboard.h @@ -33,10 +33,6 @@ #include "core/ustring.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - /* Special Key: diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp index 895ce14ae9..146a301995 100644 --- a/core/os/main_loop.cpp +++ b/core/os/main_loop.cpp @@ -44,11 +44,13 @@ void MainLoop::_bind_methods() { BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); BIND_VMETHOD(MethodInfo("_input_text", PropertyInfo(Variant::STRING, "text"))); BIND_VMETHOD(MethodInfo("_initialize")); - BIND_VMETHOD(MethodInfo("_iteration", PropertyInfo(Variant::REAL, "delta"))); - BIND_VMETHOD(MethodInfo("_idle", PropertyInfo(Variant::REAL, "delta"))); - BIND_VMETHOD(MethodInfo("_drop_files", PropertyInfo(Variant::POOL_STRING_ARRAY, "files"), PropertyInfo(Variant::INT, "screen"))); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "_iteration", PropertyInfo(Variant::REAL, "delta"))); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "_idle", PropertyInfo(Variant::REAL, "delta"))); + BIND_VMETHOD(MethodInfo("_drop_files", PropertyInfo(Variant::POOL_STRING_ARRAY, "files"), PropertyInfo(Variant::INT, "from_screen"))); BIND_VMETHOD(MethodInfo("_finalize")); + BIND_VMETHOD(MethodInfo("_global_menu_action", PropertyInfo(Variant::NIL, "id"), PropertyInfo(Variant::NIL, "meta"))); + BIND_CONSTANT(NOTIFICATION_WM_MOUSE_ENTER); BIND_CONSTANT(NOTIFICATION_WM_MOUSE_EXIT); BIND_CONSTANT(NOTIFICATION_WM_FOCUS_IN); @@ -61,6 +63,10 @@ void MainLoop::_bind_methods() { BIND_CONSTANT(NOTIFICATION_WM_ABOUT); BIND_CONSTANT(NOTIFICATION_CRASH); BIND_CONSTANT(NOTIFICATION_OS_IME_UPDATE); + BIND_CONSTANT(NOTIFICATION_APP_RESUMED); + BIND_CONSTANT(NOTIFICATION_APP_PAUSED); + + ADD_SIGNAL(MethodInfo("on_request_permissions_result", PropertyInfo(Variant::STRING, "permission"), PropertyInfo(Variant::BOOL, "granted"))); }; void MainLoop::set_init_script(const Ref<Script> &p_init_script) { @@ -115,6 +121,12 @@ void MainLoop::drop_files(const Vector<String> &p_files, int p_from_screen) { get_script_instance()->call("_drop_files", p_files, p_from_screen); } +void MainLoop::global_menu_action(const Variant &p_id, const Variant &p_meta) { + + if (get_script_instance()) + get_script_instance()->call("_global_menu_action", p_id, p_meta); +} + void MainLoop::finish() { if (get_script_instance()) { diff --git a/core/os/main_loop.h b/core/os/main_loop.h index ad734d3fc8..aca920efcb 100644 --- a/core/os/main_loop.h +++ b/core/os/main_loop.h @@ -35,10 +35,6 @@ #include "core/reference.h" #include "core/script_language.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - class MainLoop : public Object { GDCLASS(MainLoop, Object); @@ -64,6 +60,8 @@ public: NOTIFICATION_WM_ABOUT = 1011, NOTIFICATION_CRASH = 1012, NOTIFICATION_OS_IME_UPDATE = 1013, + NOTIFICATION_APP_RESUMED = 1014, + NOTIFICATION_APP_PAUSED = 1015, }; virtual void input_event(const Ref<InputEvent> &p_event); @@ -75,6 +73,7 @@ public: virtual void finish(); virtual void drop_files(const Vector<String> &p_files, int p_from_screen = 0); + virtual void global_menu_action(const Variant &p_id, const Variant &p_meta); void set_init_script(const Ref<Script> &p_init_script); diff --git a/core/os/memory.cpp b/core/os/memory.cpp index d1de51f3db..f4ed1d6e27 100644 --- a/core/os/memory.cpp +++ b/core/os/memory.cpp @@ -50,20 +50,17 @@ void *operator new(size_t p_size, void *(*p_allocfunc)(size_t p_size)) { #ifdef _MSC_VER void operator delete(void *p_mem, const char *p_description) { - ERR_EXPLAINC("Call to placement delete should not happen."); - CRASH_NOW(); + CRASH_NOW_MSG("Call to placement delete should not happen."); } void operator delete(void *p_mem, void *(*p_allocfunc)(size_t p_size)) { - ERR_EXPLAINC("Call to placement delete should not happen."); - CRASH_NOW(); + CRASH_NOW_MSG("Call to placement delete should not happen."); } void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_description) { - ERR_EXPLAINC("Call to placement delete should not happen."); - CRASH_NOW(); + CRASH_NOW_MSG("Call to placement delete should not happen."); } #endif diff --git a/core/os/memory.h b/core/os/memory.h index f3ca9fc614..a68a359546 100644 --- a/core/os/memory.h +++ b/core/os/memory.h @@ -31,14 +31,11 @@ #ifndef MEMORY_H #define MEMORY_H +#include "core/error_macros.h" #include "core/safe_refcount.h" #include <stddef.h> -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - #ifndef PAD_ALIGN #define PAD_ALIGN 16 //must always be greater than this at much #endif @@ -66,7 +63,7 @@ public: class DefaultAllocator { public: _FORCE_INLINE_ static void *alloc(size_t p_memory) { return Memory::alloc_static(p_memory, false); } - _FORCE_INLINE_ static void free(void *p_ptr) { return Memory::free_static(p_ptr, false); } + _FORCE_INLINE_ static void free(void *p_ptr) { Memory::free_static(p_ptr, false); } }; void *operator new(size_t p_size, const char *p_description); ///< operator new that takes a description and uses MemoryStaticPool diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp index 7cb7ae130f..81871d6c4f 100644 --- a/core/os/midi_driver.cpp +++ b/core/os/midi_driver.cpp @@ -33,6 +33,7 @@ #include "core/os/os.h" #include "main/input_default.h" +uint8_t MIDIDriver::last_received_message = 0x00; MIDIDriver *MIDIDriver::singleton = NULL; MIDIDriver *MIDIDriver::get_singleton() { @@ -48,33 +49,42 @@ void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_ Ref<InputEventMIDI> event; event.instance(); + uint32_t param_position = 1; if (length >= 1) { - event->set_channel(data[0] & 0xF); - event->set_message(data[0] >> 4); + if ((data[0] & 0x80) == 0x00) { + // running status + event->set_channel(last_received_message & 0xF); + event->set_message(last_received_message >> 4); + param_position = 0; + } else { + event->set_channel(data[0] & 0xF); + event->set_message(data[0] >> 4); + param_position = 1; + last_received_message = data[0]; + } } switch (event->get_message()) { case MIDI_MESSAGE_AFTERTOUCH: - if (length >= 3) { - event->set_pitch(data[1]); - event->set_pressure(data[2]); + if (length >= 2 + param_position) { + event->set_pitch(data[param_position]); + event->set_pressure(data[param_position + 1]); } break; case MIDI_MESSAGE_CONTROL_CHANGE: - if (length >= 3) { - event->set_controller_number(data[1]); - event->set_controller_value(data[2]); + if (length >= 2 + param_position) { + event->set_controller_number(data[param_position]); + event->set_controller_value(data[param_position + 1]); } break; case MIDI_MESSAGE_NOTE_ON: case MIDI_MESSAGE_NOTE_OFF: - case MIDI_MESSAGE_PITCH_BEND: - if (length >= 3) { - event->set_pitch(data[1]); - event->set_velocity(data[2]); + if (length >= 2 + param_position) { + event->set_pitch(data[param_position]); + event->set_velocity(data[param_position + 1]); if (event->get_message() == MIDI_MESSAGE_NOTE_ON && event->get_velocity() == 0) { // https://www.midi.org/forum/228-writing-midi-software-send-note-off,-or-zero-velocity-note-on @@ -83,15 +93,21 @@ void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_ } break; + case MIDI_MESSAGE_PITCH_BEND: + if (length >= 2 + param_position) { + event->set_pitch((data[param_position + 1] << 7) | data[param_position]); + } + break; + case MIDI_MESSAGE_PROGRAM_CHANGE: - if (length >= 2) { - event->set_instrument(data[1]); + if (length >= 1 + param_position) { + event->set_instrument(data[param_position]); } break; case MIDI_MESSAGE_CHANNEL_PRESSURE: - if (length >= 2) { - event->set_pressure(data[1]); + if (length >= 1 + param_position) { + event->set_pressure(data[param_position]); } break; } diff --git a/core/os/midi_driver.h b/core/os/midi_driver.h index e0e5e2be67..33f49d19f5 100644 --- a/core/os/midi_driver.h +++ b/core/os/midi_driver.h @@ -41,6 +41,7 @@ class MIDIDriver { static MIDIDriver *singleton; + static uint8_t last_received_message; public: static MIDIDriver *get_singleton(); diff --git a/core/os/os.cpp b/core/os/os.cpp index 03e63f636e..2d61417b4f 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -51,6 +51,35 @@ uint32_t OS::get_ticks_msec() const { return get_ticks_usec() / 1000; } +String OS::get_iso_date_time(bool local) const { + OS::Date date = get_date(local); + OS::Time time = get_time(local); + + String timezone; + if (!local) { + TimeZoneInfo zone = get_time_zone_info(); + if (zone.bias >= 0) { + timezone = "+"; + } + timezone = timezone + itos(zone.bias / 60).pad_zeros(2) + itos(zone.bias % 60).pad_zeros(2); + } else { + timezone = "Z"; + } + + return itos(date.year).pad_zeros(2) + + "-" + + itos(date.month).pad_zeros(2) + + "-" + + itos(date.day).pad_zeros(2) + + "T" + + itos(time.hour).pad_zeros(2) + + ":" + + itos(time.min).pad_zeros(2) + + ":" + + itos(time.sec).pad_zeros(2) + + timezone; +} + uint64_t OS::get_splash_tick_msec() const { return _msec_splash; } @@ -157,32 +186,14 @@ int OS::get_process_id() const { return -1; }; -bool OS::is_stdout_verbose() const { +void OS::vibrate_handheld(int p_duration_ms) { - return _verbose_stdout; + WARN_PRINTS("vibrate_handheld() only works with Android and iOS"); } -void OS::set_last_error(const char *p_error) { - - GLOBAL_LOCK_FUNCTION - if (p_error == NULL) - p_error = "Unknown Error"; - - if (last_error) - memfree(last_error); - last_error = NULL; - int len = 0; - while (p_error[len++]) - ; - - last_error = (char *)memalloc(len); - for (int i = 0; i < len; i++) - last_error[i] = p_error[i]; -} +bool OS::is_stdout_verbose() const { -const char *OS::get_last_error() const { - GLOBAL_LOCK_FUNCTION - return last_error ? last_error : ""; + return _verbose_stdout; } void OS::dump_memory_to_file(const char *p_file) { @@ -220,6 +231,16 @@ int OS::get_virtual_keyboard_height() const { return 0; } +void OS::set_cursor_shape(CursorShape p_shape) { +} + +OS::CursorShape OS::get_cursor_shape() const { + return CURSOR_ARROW; +} + +void OS::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { +} + void OS::print_all_resources(String p_to_file) { ERR_FAIL_COND(p_to_file != "" && _OSPRF); @@ -229,7 +250,7 @@ void OS::print_all_resources(String p_to_file) { _OSPRF = FileAccess::open(p_to_file, FileAccess::WRITE, &err); if (err != OK) { _OSPRF = NULL; - ERR_FAIL_COND(err != OK); + ERR_FAIL_MSG("Can't print all resources to file: " + String(p_to_file) + "."); } } @@ -253,14 +274,6 @@ void OS::dump_resources_to_file(const char *p_file) { ResourceCache::dump(p_file); } -void OS::clear_last_error() { - - GLOBAL_LOCK_FUNCTION - if (last_error) - memfree(last_error); - last_error = NULL; -} - void OS::set_no_window_mode(bool p_enable) { _no_window = p_enable; @@ -447,14 +460,14 @@ void OS::_ensure_user_data_dir() { da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); Error err = da->make_dir_recursive(dd); - if (err != OK) { - ERR_EXPLAIN("Error attempting to create data dir: " + dd); - } - ERR_FAIL_COND(err != OK); + ERR_FAIL_COND_MSG(err != OK, "Error attempting to create data dir: " + dd + "."); memdelete(da); } +void OS::set_native_icon(const String &p_filename) { +} + void OS::set_icon(const Ref<Image> &p_icon) { } @@ -559,6 +572,14 @@ bool OS::is_vsync_enabled() const { return _use_vsync; } +void OS::set_vsync_via_compositor(bool p_enable) { + _vsync_via_compositor = p_enable; +} + +bool OS::is_vsync_via_compositor_enabled() const { + return _vsync_via_compositor; +} + OS::PowerState OS::get_power_state() { return POWERSTATE_UNKNOWN; } @@ -678,7 +699,7 @@ int OS::get_audio_driver_count() const { const char *OS::get_audio_driver_name(int p_driver) const { AudioDriver *driver = AudioDriverManager::get_driver(p_driver); - ERR_FAIL_COND_V(!driver, ""); + ERR_FAIL_COND_V_MSG(!driver, "", "Cannot get audio driver at index '" + itos(p_driver) + "'."); return AudioDriverManager::get_driver(p_driver)->get_name(); } @@ -720,7 +741,6 @@ OS::OS() { void *volatile stack_bottom; restart_on_exit = false; - last_error = NULL; singleton = this; _keep_screen_on = true; // set default value to true, because this had been true before godot 2.0. low_processor_usage_mode = false; diff --git a/core/os/os.h b/core/os/os.h index d02d5a2c84..26db629d7c 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -41,9 +41,7 @@ #include <stdarg.h> -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ +class Mutex; class OS { @@ -62,6 +60,7 @@ class OS { bool _allow_hidpi; bool _allow_layered; bool _use_vsync; + bool _vsync_via_compositor; char *last_error; @@ -102,10 +101,10 @@ public: bool maximized; bool always_on_top; bool use_vsync; - bool layered_splash; + bool vsync_via_compositor; bool layered; float get_aspect() const { return (float)width / (float)height; } - VideoMode(int p_width = 1024, int p_height = 600, bool p_fullscreen = false, bool p_resizable = true, bool p_borderless_window = false, bool p_maximized = false, bool p_always_on_top = false, bool p_use_vsync = false) { + VideoMode(int p_width = 1024, int p_height = 600, bool p_fullscreen = false, bool p_resizable = true, bool p_borderless_window = false, bool p_maximized = false, bool p_always_on_top = false, bool p_use_vsync = false, bool p_vsync_via_compositor = false) { width = p_width; height = p_height; fullscreen = p_fullscreen; @@ -114,8 +113,8 @@ public: maximized = p_maximized; always_on_top = p_always_on_top; use_vsync = p_use_vsync; + vsync_via_compositor = p_vsync_via_compositor; layered = false; - layered_splash = false; } }; @@ -147,6 +146,11 @@ public: static OS *get_singleton(); + virtual void global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta){}; + virtual void global_menu_add_separator(const String &p_menu){}; + virtual void global_menu_remove_item(const String &p_menu, int p_idx){}; + virtual void global_menu_clear(const String &p_menu){}; + void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type = Logger::ERR_ERROR); void print(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; @@ -154,10 +158,6 @@ public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!") = 0; virtual String get_stdin_string(bool p_block = true) = 0; - virtual void set_last_error(const char *p_error); - virtual const char *get_last_error() const; - virtual void clear_last_error(); - enum MouseMode { MOUSE_MODE_VISIBLE, MOUSE_MODE_HIDDEN, @@ -205,8 +205,12 @@ public: virtual int get_screen_dpi(int p_screen = -1) const { return 72; } virtual Point2 get_window_position() const { return Vector2(); } virtual void set_window_position(const Point2 &p_position) {} + virtual Size2 get_max_window_size() const { return Size2(); }; + virtual Size2 get_min_window_size() const { return Size2(); }; virtual Size2 get_window_size() const = 0; virtual Size2 get_real_window_size() const { return get_window_size(); } + virtual void set_min_window_size(const Size2 p_size) {} + virtual void set_max_window_size(const Size2 p_size) {} virtual void set_window_size(const Size2 p_size) {} virtual void set_window_fullscreen(bool p_enabled) {} virtual bool is_window_fullscreen() const { return true; } @@ -218,6 +222,8 @@ public: virtual bool is_window_maximized() const { return true; } virtual void set_window_always_on_top(bool p_enabled) {} virtual bool is_window_always_on_top() const { return false; } + virtual void set_console_visible(bool p_enabled) {} + virtual bool is_console_visible() const { return false; } virtual void request_attention() {} virtual void center_window(); @@ -260,9 +266,10 @@ public: virtual int get_low_processor_usage_mode_sleep_usec() const; virtual String get_executable_path() const; - virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false) = 0; + virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL) = 0; virtual Error kill(const ProcessID &p_pid) = 0; virtual int get_process_id() const; + virtual void vibrate_handheld(int p_duration_ms = 500); virtual Error shell_open(String p_uri); virtual Error set_cwd(const String &p_cwd); @@ -271,7 +278,7 @@ public: virtual String get_environment(const String &p_var) const = 0; virtual bool set_environment(const String &p_var, const String &p_value) const = 0; - virtual String get_name() = 0; + virtual String get_name() const = 0; virtual List<String> get_cmdline_args() const { return _cmdline; } virtual String get_model_name() const; @@ -330,6 +337,7 @@ public: virtual Date get_date(bool local = false) const = 0; virtual Time get_time(bool local = false) const = 0; virtual TimeZoneInfo get_time_zone_info() const = 0; + virtual String get_iso_date_time(bool local = false) const; virtual uint64_t get_unix_time() const; virtual uint64_t get_system_time_secs() const; virtual uint64_t get_system_time_msecs() const; @@ -377,8 +385,9 @@ public: // returns height of the currently shown virtual keyboard (0 if keyboard is hidden) virtual int get_virtual_keyboard_height() const; - virtual void set_cursor_shape(CursorShape p_shape) = 0; - virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) = 0; + virtual void set_cursor_shape(CursorShape p_shape); + virtual CursorShape get_cursor_shape() const; + virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); virtual bool get_swap_ok_cancel() { return false; } virtual void dump_memory_to_file(const char *p_file); @@ -448,6 +457,7 @@ public: virtual void make_rendering_thread(); virtual void swap_buffers(); + virtual void set_native_icon(const String &p_filename); virtual void set_icon(const Ref<Image> &p_icon); virtual int get_exit_code() const; @@ -502,6 +512,9 @@ public: //real, actual overridable function to switch vsync, which needs to be called from graphics thread if needed virtual void _set_use_vsync(bool p_enable) {} + 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(); @@ -519,6 +532,8 @@ public: List<String> get_restart_on_exit_arguments() const; virtual bool request_permission(const String &p_name) { return true; } + virtual bool request_permissions() { return true; } + virtual Vector<String> get_granted_permissions() const { return Vector<String>(); } virtual void process_and_drop_events() {} OS(); diff --git a/core/os/semaphore.h b/core/os/semaphore.h index ccbba0dacd..a0862dce84 100644 --- a/core/os/semaphore.h +++ b/core/os/semaphore.h @@ -33,10 +33,6 @@ #include "core/error_list.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - class Semaphore { protected: static Semaphore *(*create_func)(); diff --git a/core/os/thread.h b/core/os/thread.h index e7a6e8cb1f..169280a208 100644 --- a/core/os/thread.h +++ b/core/os/thread.h @@ -34,10 +34,6 @@ #include "core/typedefs.h" #include "core/ustring.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - typedef void (*ThreadCreateCallback)(void *p_userdata); class Thread { diff --git a/core/packed_data_container.cpp b/core/packed_data_container.cpp index 99bed829c1..7afaf7f0f1 100644 --- a/core/packed_data_container.cpp +++ b/core/packed_data_container.cpp @@ -119,7 +119,7 @@ Variant PackedDataContainer::_get_at_ofs(uint32_t p_ofs, const uint8_t *p_buf, b if (rerr != OK) { err = true; - ERR_FAIL_COND_V(err != OK, Variant()); + ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to decode Variant."); } return v; } @@ -137,6 +137,7 @@ uint32_t PackedDataContainer::_type_at_ofs(uint32_t p_ofs) const { int PackedDataContainer::_size(uint32_t p_ofs) const { PoolVector<uint8_t>::Read rd = data.read(); + ERR_FAIL_COND_V(!rd.ptr(), 0); const uint8_t *r = &rd[p_ofs]; uint32_t type = decode_uint32(r); @@ -224,7 +225,8 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd string_cache[s] = tmpdata.size(); - }; //fallthrough + FALLTHROUGH; + }; case Variant::NIL: case Variant::BOOL: case Variant::INT: @@ -319,7 +321,8 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd } break; - default: {} + default: { + } } return OK; diff --git a/core/pool_allocator.cpp b/core/pool_allocator.cpp index a283d8db1d..6c1f2756f2 100644 --- a/core/pool_allocator.cpp +++ b/core/pool_allocator.cpp @@ -204,10 +204,8 @@ PoolAllocator::ID PoolAllocator::alloc(int p_size) { /* Then search again */ if (!find_hole(&new_entry_indices_pos, size_to_alloc)) { - mt_unlock(); - ERR_PRINT("memory can't be compacted further"); - return POOL_ALLOCATOR_INVALID_ID; + ERR_FAIL_V_MSG(POOL_ALLOCATOR_INVALID_ID, "Memory can't be compacted further."); } } @@ -217,7 +215,7 @@ PoolAllocator::ID PoolAllocator::alloc(int p_size) { if (!found_free_entry) { mt_unlock(); - ERR_FAIL_COND_V(!found_free_entry, POOL_ALLOCATOR_INVALID_ID); + ERR_FAIL_V_MSG(POOL_ALLOCATOR_INVALID_ID, "No free entry found in PoolAllocator."); } /* move all entry indices up, make room for this one */ @@ -500,9 +498,7 @@ void *PoolAllocator::get(ID p_mem) { if (!needs_locking) { Entry *e = get_entry(p_mem); - if (!e) { - ERR_FAIL_COND_V(!e, NULL); - }; + ERR_FAIL_COND_V(!e, NULL); return &pool[e->pos]; } @@ -540,6 +536,10 @@ void PoolAllocator::unlock(ID p_mem) { return; mt_lock(); Entry *e = get_entry(p_mem); + if (!e) { + mt_unlock(); + ERR_FAIL_COND(!e); + } if (e->lock == 0) { mt_unlock(); ERR_PRINT("e->lock == 0"); @@ -612,7 +612,7 @@ PoolAllocator::PoolAllocator(void *p_mem, int p_size, int p_align, bool p_needs_ PoolAllocator::PoolAllocator(int p_align, int p_size, bool p_needs_locking, int p_max_entries) { ERR_FAIL_COND(p_align < 1); - mem_ptr = Memory::alloc_static(p_size + p_align, "PoolAllocator()"); + mem_ptr = Memory::alloc_static(p_size + p_align, true); uint8_t *mem8 = (uint8_t *)mem_ptr; uint64_t ofs = (uint64_t)mem8; if (ofs % p_align) diff --git a/core/pool_vector.cpp b/core/pool_vector.cpp index b9d2316315..50ea898bef 100644 --- a/core/pool_vector.cpp +++ b/core/pool_vector.cpp @@ -66,6 +66,5 @@ void MemoryPool::cleanup() { memdelete_arr(allocs); memdelete(alloc_mutex); - ERR_EXPLAINC("There are still MemoryPool allocs in use at exit!"); - ERR_FAIL_COND(allocs_used > 0); + ERR_FAIL_COND_MSG(allocs_used > 0, "There are still MemoryPool allocs in use at exit!"); } diff --git a/core/pool_vector.h b/core/pool_vector.h index 102a620f17..a698e72e18 100644 --- a/core/pool_vector.h +++ b/core/pool_vector.h @@ -77,10 +77,6 @@ struct MemoryPool { static void cleanup(); }; -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - template <class T> class PoolVector { @@ -102,8 +98,7 @@ class PoolVector { MemoryPool::alloc_mutex->lock(); if (MemoryPool::allocs_used == MemoryPool::alloc_count) { MemoryPool::alloc_mutex->unlock(); - ERR_EXPLAINC("All memory pool allocations are in use, can't COW."); - ERR_FAIL(); + ERR_FAIL_MSG("All memory pool allocations are in use, can't COW."); } MemoryPool::Alloc *old_alloc = alloc; @@ -301,6 +296,10 @@ public: virtual ~Access() { _unref(); } + + void release() { + _unref(); + } }; class Read : public Access { @@ -386,6 +385,7 @@ public: } inline int size() const; + inline bool empty() const; T get(int p_index) const; void set(int p_index, const T &p_val); void push_back(const T &p_val); @@ -411,8 +411,8 @@ public: p_to = size() + p_to; } - CRASH_BAD_INDEX(p_from, size()); - CRASH_BAD_INDEX(p_to, size()); + ERR_FAIL_INDEX_V(p_from, size(), PoolVector<T>()); + ERR_FAIL_INDEX_V(p_to, size(), PoolVector<T>()); PoolVector<T> slice; int span = 1 + p_to - p_from; @@ -454,7 +454,7 @@ public: bool is_locked() const { return alloc && alloc->lock > 0; } - inline const T operator[](int p_index) const; + inline T operator[](int p_index) const; Error resize(int p_size); @@ -476,6 +476,12 @@ int PoolVector<T>::size() const { } template <class T> +bool PoolVector<T>::empty() const { + + return alloc ? alloc->size == 0 : true; +} + +template <class T> T PoolVector<T>::get(int p_index) const { return operator[](p_index); @@ -484,9 +490,7 @@ T PoolVector<T>::get(int p_index) const { template <class T> void PoolVector<T>::set(int p_index, const T &p_val) { - if (p_index < 0 || p_index >= size()) { - ERR_FAIL_COND(p_index < 0 || p_index >= size()); - } + ERR_FAIL_INDEX(p_index, size()); Write w = write(); w[p_index] = p_val; @@ -500,7 +504,7 @@ void PoolVector<T>::push_back(const T &p_val) { } template <class T> -const T PoolVector<T>::operator[](int p_index) const { +T PoolVector<T>::operator[](int p_index) const { CRASH_BAD_INDEX(p_index, size()); @@ -511,6 +515,8 @@ const T PoolVector<T>::operator[](int p_index) const { template <class T> Error PoolVector<T>::resize(int p_size) { + ERR_FAIL_COND_V_MSG(p_size < 0, ERR_INVALID_PARAMETER, "Size of PoolVector cannot be negative."); + if (alloc == NULL) { if (p_size == 0) @@ -520,8 +526,7 @@ Error PoolVector<T>::resize(int p_size) { MemoryPool::alloc_mutex->lock(); if (MemoryPool::allocs_used == MemoryPool::alloc_count) { MemoryPool::alloc_mutex->unlock(); - ERR_EXPLAINC("All memory pool allocations are in use."); - ERR_FAIL_V(ERR_OUT_OF_MEMORY); + ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "All memory pool allocations are in use."); } //take one from the free list @@ -538,7 +543,7 @@ Error PoolVector<T>::resize(int p_size) { } else { - ERR_FAIL_COND_V(alloc->lock > 0, ERR_LOCKED); //can't resize if locked! + ERR_FAIL_COND_V_MSG(alloc->lock > 0, ERR_LOCKED, "Can't resize PoolVector if locked."); //can't resize if locked! } size_t new_size = sizeof(T) * p_size; diff --git a/core/print_string.cpp b/core/print_string.cpp index d91d49f53b..3271744af3 100644 --- a/core/print_string.cpp +++ b/core/print_string.cpp @@ -68,8 +68,8 @@ void remove_print_handler(PrintHandlerList *p_handler) { } //OS::get_singleton()->print("print handler list is %p\n",print_handler_list); - ERR_FAIL_COND(l == NULL); _global_unlock(); + ERR_FAIL_COND(l == NULL); } void print_line(String p_string) { diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 8c9a3dbcd4..067578e354 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -75,11 +75,23 @@ String ProjectSettings::localize_path(const String &p_path) const { memdelete(dir); - if (!cwd.begins_with(resource_path)) { + // Ensure that we end with a '/'. + // This is important to ensure that we do not wrongly localize the resource path + // in an absolute path that just happens to contain this string but points to a + // different folder (e.g. "/my/project" as resource_path would be contained in + // "/my/project_data", even though the latter is not part of res://. + // `plus_file("")` is an easy way to ensure we have a trailing '/'. + const String res_path = resource_path.plus_file(""); + + // DirAccess::get_current_dir() is not guaranteed to return a path that with a trailing '/', + // so we must make sure we have it as well in order to compare with 'res_path'. + cwd = cwd.plus_file(""); + + if (!cwd.begins_with(res_path)) { return p_path; }; - return cwd.replace_first(resource_path, "res:/"); + return cwd.replace_first(res_path, "res://"); } else { memdelete(dir); @@ -101,12 +113,12 @@ String ProjectSettings::localize_path(const String &p_path) const { void ProjectSettings::set_initial_value(const String &p_name, const Variant &p_value) { - ERR_FAIL_COND(!props.has(p_name)); + ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); props[p_name].initial = p_value; } void ProjectSettings::set_restart_if_changed(const String &p_name, bool p_restart) { - ERR_FAIL_COND(!props.has(p_name)); + ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); props[p_name].restart_if_changed = p_restart; } @@ -252,12 +264,12 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const { } } -bool ProjectSettings::_load_resource_pack(const String &p_pack) { +bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_files) { if (PackedData::get_singleton()->is_disabled()) return false; - bool ok = PackedData::get_singleton()->add_pack(p_pack) == OK; + bool ok = PackedData::get_singleton()->add_pack(p_pack, p_replace_files) == OK; if (!ok) return false; @@ -324,7 +336,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b if (p_main_pack != "") { bool ok = _load_resource_pack(p_main_pack); - ERR_FAIL_COND_V(!ok, ERR_CANT_OPEN); + ERR_FAIL_COND_V_MSG(!ok, ERR_CANT_OPEN, "Cannot open resource pack '" + p_main_pack + "'."); Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); if (err == OK) { @@ -335,17 +347,17 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b return err; } - // Attempt with exec_name.pck - // (This is the usual case when distributing a Godot game.) - - // Based on the OS, it can be the exec path + '.pck' (Linux w/o extension, macOS in .app bundle) - // or the exec path's basename + '.pck' (Windows). - // We need to test both possibilities as extensions for Linux binaries are optional - // (so both 'mygame.bin' and 'mygame' should be able to find 'mygame.pck'). - String exec_path = OS::get_singleton()->get_executable_path(); if (exec_path != "") { + // Attempt with exec_name.pck + // (This is the usual case when distributing a Godot game.) + + // Based on the OS, it can be the exec path + '.pck' (Linux w/o extension, macOS in .app bundle) + // or the exec path's basename + '.pck' (Windows). + // We need to test both possibilities as extensions for Linux binaries are optional + // (so both 'mygame.bin' and 'mygame' should be able to find 'mygame.pck'). + bool found = false; String exec_dir = exec_path.get_base_dir(); @@ -367,6 +379,14 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b } } + // Attempt with PCK bundled into executable + + if (!found) { + if (_load_resource_pack(exec_path)) { + found = true; + } + } + // If we opened our package, try and load our project if (found) { Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); @@ -401,7 +421,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b // or, if requested (`p_upwards`) in parent directories. DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - ERR_FAIL_COND_V(!d, ERR_CANT_CREATE); + ERR_FAIL_COND_V_MSG(!d, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_path + "'."); d->change_dir(p_path); String current_dir = d->get_current_dir(); @@ -467,7 +487,7 @@ void ProjectSettings::set_registering_order(bool p_enable) { registering_order = p_enable; } -Error ProjectSettings::_load_settings_binary(const String p_path) { +Error ProjectSettings::_load_settings_binary(const String &p_path) { Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); @@ -480,8 +500,7 @@ Error ProjectSettings::_load_settings_binary(const String p_path) { if (hdr[0] != 'E' || hdr[1] != 'C' || hdr[2] != 'F' || hdr[3] != 'G') { memdelete(f); - ERR_EXPLAIN("Corrupted header in binary project.binary (not ECFG)"); - ERR_FAIL_V(ERR_FILE_CORRUPT;) + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Corrupted header in binary project.binary (not ECFG)."); } uint32_t count = f->get_32(); @@ -501,16 +520,17 @@ Error ProjectSettings::_load_settings_binary(const String p_path) { d.resize(vlen); f->get_buffer(d.ptrw(), vlen); Variant value; - err = decode_variant(value, d.ptr(), d.size(), NULL, false); - ERR_EXPLAIN("Error decoding property: " + key); - ERR_CONTINUE(err != OK); + err = decode_variant(value, d.ptr(), d.size(), NULL, true); + ERR_CONTINUE_MSG(err != OK, "Error decoding property: " + key + "."); set(key, value); } + f->close(); + memdelete(f); return OK; } -Error ProjectSettings::_load_settings_text(const String p_path) { +Error ProjectSettings::_load_settings_text(const String &p_path) { Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); @@ -557,8 +577,7 @@ Error ProjectSettings::_load_settings_text(const String p_path) { config_version = value; if (config_version > CONFIG_VERSION) { memdelete(f); - ERR_EXPLAIN(vformat("Can't open project at '%s', its `config_version` (%d) is from a more recent and incompatible version of the engine. Expected config version: %d.", p_path, config_version, CONFIG_VERSION)); - ERR_FAIL_COND_V(config_version > CONFIG_VERSION, ERR_FILE_CANT_OPEN); + ERR_FAIL_V_MSG(ERR_FILE_CANT_OPEN, vformat("Can't open project at '%s', its `config_version` (%d) is from a more recent and incompatible version of the engine. Expected config version: %d.", p_path, config_version, CONFIG_VERSION)); } } else { if (section == String()) { @@ -571,13 +590,9 @@ Error ProjectSettings::_load_settings_text(const String p_path) { section = next_tag.name; } } - - memdelete(f); - - return OK; } -Error ProjectSettings::_load_settings_text_or_binary(const String p_text_path, const String p_bin_path) { +Error ProjectSettings::_load_settings_text_or_binary(const String &p_text_path, const String &p_bin_path) { // Attempt first to load the text-based project.godot file Error err_text = _load_settings_text(p_text_path); @@ -596,19 +611,19 @@ Error ProjectSettings::_load_settings_text_or_binary(const String p_text_path, c int ProjectSettings::get_order(const String &p_name) const { - ERR_FAIL_COND_V(!props.has(p_name), -1); + ERR_FAIL_COND_V_MSG(!props.has(p_name), -1, "Request for nonexistent project setting: " + p_name + "."); return props[p_name].order; } void ProjectSettings::set_order(const String &p_name, int p_order) { - ERR_FAIL_COND(!props.has(p_name)); + ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); props[p_name].order = p_order; } void ProjectSettings::set_builtin_order(const String &p_name) { - ERR_FAIL_COND(!props.has(p_name)); + ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); if (props[p_name].order >= NO_BUILTIN_ORDER_BASE) { props[p_name].order = last_builtin_order++; } @@ -616,7 +631,7 @@ void ProjectSettings::set_builtin_order(const String &p_name) { void ProjectSettings::clear(const String &p_name) { - ERR_FAIL_COND(!props.has(p_name)); + ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); props.erase(p_name); } @@ -629,11 +644,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str Error err; FileAccess *file = FileAccess::open(p_file, FileAccess::WRITE, &err); - if (err != OK) { - - ERR_EXPLAIN("Couldn't save project.binary at " + p_file); - ERR_FAIL_COND_V(err, err) - } + ERR_FAIL_COND_V_MSG(err != OK, err, "Couldn't save project.binary at " + p_file + "."); uint8_t hdr[4] = { 'E', 'C', 'F', 'G' }; file->store_buffer(hdr, 4); @@ -694,18 +705,18 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str file->store_string(key); int len; - err = encode_variant(value, NULL, len, false); + err = encode_variant(value, NULL, len, true); if (err != OK) memdelete(file); - ERR_FAIL_COND_V(err != OK, ERR_INVALID_DATA); + ERR_FAIL_COND_V_MSG(err != OK, ERR_INVALID_DATA, "Error when trying to encode Variant."); Vector<uint8_t> buff; buff.resize(len); - err = encode_variant(value, buff.ptrw(), len, false); + err = encode_variant(value, buff.ptrw(), len, true); if (err != OK) memdelete(file); - ERR_FAIL_COND_V(err != OK, ERR_INVALID_DATA); + ERR_FAIL_COND_V_MSG(err != OK, ERR_INVALID_DATA, "Error when trying to encode Variant."); file->store_32(len); file->store_buffer(buff.ptr(), buff.size()); } @@ -722,10 +733,7 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const Map<Strin Error err; FileAccess *file = FileAccess::open(p_file, FileAccess::WRITE, &err); - if (err) { - ERR_EXPLAIN("Couldn't save project.godot - " + p_file); - ERR_FAIL_COND_V(err, err) - } + ERR_FAIL_COND_V_MSG(err != OK, err, "Couldn't save project.godot - " + p_file + "."); file->store_line("; Engine configuration file."); file->store_line("; It's best edited using the editor UI and not directly,"); @@ -781,7 +789,7 @@ Error ProjectSettings::_save_custom_bnd(const String &p_file) { // add other par Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_custom, const Vector<String> &p_custom_features, bool p_merge_with_current) { - ERR_FAIL_COND_V(p_path == "", ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(p_path == "", ERR_INVALID_PARAMETER, "Project settings save path cannot be empty."); Set<_VCSort> vclist; @@ -856,11 +864,8 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust return _save_settings_binary(p_path, props, p_custom, custom_features); else { - ERR_EXPLAIN("Unknown config file format: " + p_path); - ERR_FAIL_V(ERR_FILE_UNRECOGNIZED); + ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unknown config file format: " + p_path + "."); } - - return OK; } Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed) { @@ -976,7 +981,7 @@ void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("localize_path", "path"), &ProjectSettings::localize_path); ClassDB::bind_method(D_METHOD("globalize_path", "path"), &ProjectSettings::globalize_path); ClassDB::bind_method(D_METHOD("save"), &ProjectSettings::save); - ClassDB::bind_method(D_METHOD("load_resource_pack", "pack"), &ProjectSettings::_load_resource_pack); + ClassDB::bind_method(D_METHOD("load_resource_pack", "pack", "replace_files"), &ProjectSettings::_load_resource_pack, DEFVAL(true)); ClassDB::bind_method(D_METHOD("property_can_revert", "name"), &ProjectSettings::property_can_revert); ClassDB::bind_method(D_METHOD("property_get_revert", "name"), &ProjectSettings::property_get_revert); @@ -997,6 +1002,8 @@ ProjectSettings::ProjectSettings() { Ref<InputEventJoypadButton> joyb; GLOBAL_DEF("application/config/name", ""); + GLOBAL_DEF("application/config/description", ""); + custom_prop_info["application/config/description"] = PropertyInfo(Variant::STRING, "application/config/description", PROPERTY_HINT_MULTILINE_TEXT); GLOBAL_DEF("application/run/main_scene", ""); custom_prop_info["application/run/main_scene"] = PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "*.tscn,*.scn,*.res"); GLOBAL_DEF("application/run/disable_stdout", false); @@ -1004,6 +1011,20 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("application/config/use_custom_user_dir", false); GLOBAL_DEF("application/config/custom_user_dir_name", ""); GLOBAL_DEF("application/config/project_settings_override", ""); + GLOBAL_DEF("audio/default_bus_layout", "res://default_bus_layout.tres"); + custom_prop_info["audio/default_bus_layout"] = PropertyInfo(Variant::STRING, "audio/default_bus_layout", PROPERTY_HINT_FILE, "*.tres"); + + PoolStringArray extensions = PoolStringArray(); + extensions.push_back("gd"); + if (Engine::get_singleton()->has_singleton("GodotSharp")) + extensions.push_back("cs"); + extensions.push_back("shader"); + + GLOBAL_DEF("editor/search_in_file_extensions", extensions); + custom_prop_info["editor/search_in_file_extensions"] = PropertyInfo(Variant::POOL_STRING_ARRAY, "editor/search_in_file_extensions"); + + GLOBAL_DEF("editor/script_templates_search_path", "res://script_templates"); + custom_prop_info["editor/script_templates_search_path"] = PropertyInfo(Variant::STRING, "editor/script_templates_search_path", PROPERTY_HINT_DIR); action = Dictionary(); action["deadzone"] = Variant(0.5f); diff --git a/core/project_settings.h b/core/project_settings.h index 0ff18ab3f5..b32470361b 100644 --- a/core/project_settings.h +++ b/core/project_settings.h @@ -35,10 +35,6 @@ #include "core/os/thread_safe.h" #include "core/set.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - class ProjectSettings : public Object { GDCLASS(ProjectSettings, Object); @@ -97,9 +93,9 @@ protected: static ProjectSettings *singleton; - Error _load_settings_text(const String p_path); - Error _load_settings_binary(const String p_path); - Error _load_settings_text_or_binary(const String p_text_path, const String p_bin_path); + Error _load_settings_text(const String &p_path); + Error _load_settings_binary(const String &p_path); + Error _load_settings_text_or_binary(const String &p_text_path, const String &p_bin_path); Error _save_settings_text(const String &p_file, const Map<String, List<String> > &props, const CustomMap &p_custom = CustomMap(), const String &p_custom_features = String()); Error _save_settings_binary(const String &p_file, const Map<String, List<String> > &props, const CustomMap &p_custom = CustomMap(), const String &p_custom_features = String()); @@ -108,7 +104,7 @@ protected: void _convert_to_last_version(int p_from_version); - bool _load_resource_pack(const String &p_pack); + bool _load_resource_pack(const String &p_pack, bool p_replace_files = true); void _add_property_info_bind(const Dictionary &p_info); diff --git a/core/ref_ptr.cpp b/core/ref_ptr.cpp index 961f143e5c..6da73ca41a 100644 --- a/core/ref_ptr.cpp +++ b/core/ref_ptr.cpp @@ -49,6 +49,14 @@ bool RefPtr::operator==(const RefPtr &p_other) const { 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; +} + RefPtr::RefPtr(const RefPtr &p_other) { memnew_placement(&data[0], Ref<Reference>); diff --git a/core/ref_ptr.h b/core/ref_ptr.h index f745ababa1..320cf35609 100644 --- a/core/ref_ptr.h +++ b/core/ref_ptr.h @@ -50,6 +50,7 @@ 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; } diff --git a/core/reference.cpp b/core/reference.cpp index 7b5145184a..92bbdacd5d 100644 --- a/core/reference.cpp +++ b/core/reference.cpp @@ -36,12 +36,7 @@ bool Reference::init_ref() { if (reference()) { - // this may fail in the scenario of two threads assigning the pointer for the FIRST TIME - // at the same time, which is never likely to happen (would be crazy to do) - // so don't do it. - - if (refcount_init.get() > 0) { - refcount_init.unref(); + if (!is_referenced() && refcount_init.unref()) { unreference(); // first referencing is already 1, so compensate for the ref above } @@ -64,13 +59,15 @@ int Reference::reference_get_count() const { } bool Reference::reference() { - bool success = refcount.ref(); - if (success && refcount.get() <= 2 /* higher is not relevant */) { + uint32_t rc_val = refcount.refval(); + bool success = rc_val != 0; + + if (success && rc_val <= 2 /* higher is not relevant */) { if (get_script_instance()) { get_script_instance()->refcount_incremented(); } - if (instance_binding_count > 0) { + if (instance_binding_count > 0 && !ScriptServer::are_languages_finished()) { for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) { if (_script_instance_bindings[i]) { ScriptServer::get_language(i)->refcount_incremented_instance_binding(this); @@ -84,14 +81,15 @@ bool Reference::reference() { bool Reference::unreference() { - bool die = refcount.unref(); + uint32_t rc_val = refcount.unrefval(); + bool die = rc_val == 0; - if (refcount.get() <= 1 /* higher is not relevant */) { + if (rc_val <= 1 /* higher is not relevant */) { if (get_script_instance()) { bool script_ret = get_script_instance()->refcount_decremented(); die = die && script_ret; } - if (instance_binding_count > 0) { + if (instance_binding_count > 0 && !ScriptServer::are_languages_finished()) { for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) { if (_script_instance_bindings[i]) { bool script_ret = ScriptServer::get_language(i)->refcount_decremented_instance_binding(this); diff --git a/core/reference.h b/core/reference.h index f3fcc922c7..b8d00a94ad 100644 --- a/core/reference.h +++ b/core/reference.h @@ -36,9 +36,6 @@ #include "core/ref_ptr.h" #include "core/safe_refcount.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ class Reference : public Object { GDCLASS(Reference, Object); @@ -50,7 +47,7 @@ protected: static void _bind_methods(); public: - _FORCE_INLINE_ bool is_referenced() const { return refcount_init.get() < 1; } + _FORCE_INLINE_ bool is_referenced() const { return refcount_init.get() != 1; } bool init_ref(); bool reference(); // returns false if refcount is at zero and didn't get increased bool unreference(); @@ -197,6 +194,19 @@ public: r.reference = NULL; } + template <class T_Other> + void reference_ptr(T_Other *p_ptr) { + if (reference == p_ptr) { + return; + } + unref(); + + T *r = Object::cast_to<T>(p_ptr); + if (r) { + ref_pointer(r); + } + } + Ref(const Ref &p_from) { reference = NULL; @@ -362,7 +372,8 @@ struct PtrToArg<const RefPtr &> { template <class T> struct GetTypeInfo<Ref<T> > { - enum { VARIANT_TYPE = Variant::OBJECT }; + static const Variant::Type VARIANT_TYPE = Variant::OBJECT; + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; static inline PropertyInfo get_class_info() { return PropertyInfo(Variant::OBJECT, String(), PROPERTY_HINT_RESOURCE_TYPE, T::get_class_static()); @@ -371,7 +382,8 @@ struct GetTypeInfo<Ref<T> > { template <class T> struct GetTypeInfo<const Ref<T> &> { - enum { VARIANT_TYPE = Variant::OBJECT }; + static const Variant::Type VARIANT_TYPE = Variant::OBJECT; + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; static inline PropertyInfo get_class_info() { return PropertyInfo(Variant::OBJECT, String(), PROPERTY_HINT_RESOURCE_TYPE, T::get_class_static()); diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 97c96b4018..efc77bde48 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -34,6 +34,8 @@ #include "core/class_db.h" #include "core/compressed_translation.h" #include "core/core_string_names.h" +#include "core/crypto/crypto.h" +#include "core/crypto/hashing_context.h" #include "core/engine.h" #include "core/func_ref.h" #include "core/input_map.h" @@ -70,6 +72,8 @@ static Ref<ResourceFormatLoaderBinary> resource_loader_binary; static Ref<ResourceFormatImporter> resource_format_importer; static Ref<ResourceFormatLoaderImage> resource_format_image; static Ref<TranslationLoaderPO> resource_format_po; +static Ref<ResourceFormatSaverCrypto> resource_format_saver_crypto; +static Ref<ResourceFormatLoaderCrypto> resource_format_loader_crypto; static _ResourceLoader *_resource_loader = NULL; static _ResourceSaver *_resource_saver = NULL; @@ -151,7 +155,19 @@ void register_core_types() { ClassDB::register_class<StreamPeerTCP>(); ClassDB::register_class<TCP_Server>(); ClassDB::register_class<PacketPeerUDP>(); + + // Crypto + ClassDB::register_class<HashingContext>(); + ClassDB::register_custom_instance_class<X509Certificate>(); + ClassDB::register_custom_instance_class<CryptoKey>(); + ClassDB::register_custom_instance_class<Crypto>(); ClassDB::register_custom_instance_class<StreamPeerSSL>(); + + resource_format_saver_crypto.instance(); + ResourceSaver::add_resource_format_saver(resource_format_saver_crypto); + resource_format_loader_crypto.instance(); + ResourceLoader::add_resource_format_loader(resource_format_loader_crypto); + ClassDB::register_virtual_class<IP>(); ClassDB::register_virtual_class<PacketPeer>(); ClassDB::register_class<PacketPeerStream>(); @@ -184,11 +200,14 @@ void register_core_types() { ClassDB::register_class<PackedDataContainer>(); ClassDB::register_virtual_class<PackedDataContainerRef>(); ClassDB::register_class<AStar>(); + ClassDB::register_class<AStar2D>(); ClassDB::register_class<EncodedObjectAsID>(); ClassDB::register_class<RandomNumberGenerator>(); ClassDB::register_class<JSONParseResult>(); + ClassDB::register_virtual_class<ResourceImporter>(); + ip = IP::create(); _geometry = memnew(_Geometry); @@ -204,8 +223,13 @@ void register_core_types() { void register_core_settings() { //since in register core types, globals may not e present + GLOBAL_DEF("network/limits/tcp/connect_timeout_seconds", (30)); + ProjectSettings::get_singleton()->set_custom_property_info("network/limits/tcp/connect_timeout_seconds", PropertyInfo(Variant::INT, "network/limits/tcp/connect_timeout_seconds", PROPERTY_HINT_RANGE, "1,1800,1")); GLOBAL_DEF_RST("network/limits/packet_peer_stream/max_buffer_po2", (16)); ProjectSettings::get_singleton()->set_custom_property_info("network/limits/packet_peer_stream/max_buffer_po2", PropertyInfo(Variant::INT, "network/limits/packet_peer_stream/max_buffer_po2", PROPERTY_HINT_RANGE, "0,64,1,or_greater")); + + GLOBAL_DEF("network/ssl/certificates", ""); + ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt")); } void register_core_singletons() { @@ -267,11 +291,17 @@ void unregister_core_types() { ResourceLoader::remove_resource_format_loader(resource_format_po); resource_format_po.unref(); + ResourceSaver::remove_resource_format_saver(resource_format_saver_crypto); + resource_format_saver_crypto.unref(); + ResourceLoader::remove_resource_format_loader(resource_format_loader_crypto); + resource_format_loader_crypto.unref(); + if (ip) memdelete(ip); ResourceLoader::finalize(); + ClassDB::cleanup_defaults(); ObjectDB::cleanup(); unregister_variant_methods(); diff --git a/core/register_core_types.h b/core/register_core_types.h index b5a6aa985b..2d397b55f9 100644 --- a/core/register_core_types.h +++ b/core/register_core_types.h @@ -31,10 +31,6 @@ #ifndef REGISTER_CORE_TYPES_H #define REGISTER_CORE_TYPES_H -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - void register_core_types(); void register_core_settings(); void register_core_singletons(); diff --git a/core/resource.cpp b/core/resource.cpp index 74c93cd790..e0a40b6f3c 100644 --- a/core/resource.cpp +++ b/core/resource.cpp @@ -75,8 +75,7 @@ void Resource::set_path(const String &p_path, bool p_take_over) { bool exists = ResourceCache::resources.has(p_path); ResourceCache::lock->read_unlock(); - ERR_EXPLAIN("Another resource is loaded from path: " + p_path + " (possible cyclic resource inclusion)"); - ERR_FAIL_COND(exists); + ERR_FAIL_COND_MSG(exists, "Another resource is loaded from path '" + p_path + "' (possible cyclic resource inclusion)."); } } path_cache = p_path; @@ -277,8 +276,7 @@ void Resource::notify_change_to_owners() { for (Set<ObjectID>::Element *E = owners.front(); E; E = E->next()) { Object *obj = ObjectDB::get_instance(E->get()); - ERR_EXPLAIN("Object was deleted, while still owning a resource"); - ERR_CONTINUE(!obj); //wtf + ERR_CONTINUE_MSG(!obj, "Object was deleted, while still owning a resource."); //wtf //TODO store string obj->call("resource_changed", RES(this)); } @@ -363,6 +361,47 @@ bool Resource::is_translation_remapped() const { return remapped_list.in_list(); } +#ifdef TOOLS_ENABLED +//helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored +void Resource::set_id_for_path(const String &p_path, int p_id) { + if (p_id == -1) { + if (ResourceCache::path_cache_lock) { + ResourceCache::path_cache_lock->write_lock(); + } + ResourceCache::resource_path_cache[p_path].erase(get_path()); + if (ResourceCache::path_cache_lock) { + ResourceCache::path_cache_lock->write_unlock(); + } + } else { + if (ResourceCache::path_cache_lock) { + ResourceCache::path_cache_lock->write_lock(); + } + ResourceCache::resource_path_cache[p_path][get_path()] = p_id; + if (ResourceCache::path_cache_lock) { + ResourceCache::path_cache_lock->write_unlock(); + } + } +} + +int Resource::get_id_for_path(const String &p_path) const { + if (ResourceCache::path_cache_lock) { + ResourceCache::path_cache_lock->read_lock(); + } + if (ResourceCache::resource_path_cache[p_path].has(get_path())) { + int result = ResourceCache::resource_path_cache[p_path][get_path()]; + if (ResourceCache::path_cache_lock) { + ResourceCache::path_cache_lock->read_unlock(); + } + return result; + } else { + if (ResourceCache::path_cache_lock) { + ResourceCache::path_cache_lock->read_unlock(); + } + return -1; + } +} +#endif + void Resource::_bind_methods() { ClassDB::bind_method(D_METHOD("set_path", "path"), &Resource::_set_path); @@ -407,17 +446,26 @@ Resource::~Resource() { ResourceCache::lock->write_unlock(); } if (owners.size()) { - WARN_PRINT("Resource is still owned"); + WARN_PRINT("Resource is still owned."); } } HashMap<String, Resource *> ResourceCache::resources; +#ifdef TOOLS_ENABLED +HashMap<String, HashMap<String, int> > ResourceCache::resource_path_cache; +#endif RWLock *ResourceCache::lock = NULL; +#ifdef TOOLS_ENABLED +RWLock *ResourceCache::path_cache_lock = NULL; +#endif void ResourceCache::setup() { lock = RWLock::create(); +#ifdef TOOLS_ENABLED + path_cache_lock = RWLock::create(); +#endif } void ResourceCache::clear() { @@ -491,7 +539,7 @@ void ResourceCache::dump(const char *p_file, bool p_short) { FileAccess *f = NULL; if (p_file) { f = FileAccess::open(p_file, FileAccess::WRITE); - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "Cannot create file at path '" + String(p_file) + "'."); } const String *K = NULL; diff --git a/core/resource.h b/core/resource.h index a4d9e998ac..3e1fe07137 100644 --- a/core/resource.h +++ b/core/resource.h @@ -38,10 +38,6 @@ #include "core/safe_refcount.h" #include "core/self_list.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - #define RES_BASE_EXTENSION(m_ext) \ public: \ static void register_custom_data_to_otdb() { ClassDB::add_resource_base_extension(m_ext, get_class_static()); } \ @@ -137,6 +133,12 @@ public: virtual RID get_rid() const; // some resources may offer conversion to RID +#ifdef TOOLS_ENABLED + //helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored + void set_id_for_path(const String &p_path, int p_id); + int get_id_for_path(const String &p_path) const; +#endif + Resource(); ~Resource(); }; @@ -148,6 +150,10 @@ class ResourceCache { friend class ResourceLoader; //need the lock static RWLock *lock; static HashMap<String, Resource *> resources; +#ifdef TOOLS_ENABLED + static HashMap<String, HashMap<String, int> > resource_path_cache; // each tscn has a set of resource paths and IDs + static RWLock *path_cache_lock; +#endif // TOOLS_ENABLED friend void unregister_core_types(); static void clear(); friend void register_core_types(); diff --git a/core/rid.h b/core/rid.h index c7a71a03a0..381eee645b 100644 --- a/core/rid.h +++ b/core/rid.h @@ -37,10 +37,6 @@ #include "core/set.h" #include "core/typedefs.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - class RID_OwnerBase; class RID_Data { diff --git a/core/safe_refcount.h b/core/safe_refcount.h index f6b8f80271..47161eed57 100644 --- a/core/safe_refcount.h +++ b/core/safe_refcount.h @@ -177,23 +177,24 @@ struct SafeRefCount { public: // destroy() is called when weak_count_ drops to zero. - _ALWAYS_INLINE_ bool ref() { //true on success + _ALWAYS_INLINE_ bool ref() { // true on success return atomic_conditional_increment(&count) != 0; } - _ALWAYS_INLINE_ uint32_t refval() { //true on success + _ALWAYS_INLINE_ uint32_t refval() { // none-zero on success return atomic_conditional_increment(&count); } _ALWAYS_INLINE_ bool unref() { // true if must be disposed of - if (atomic_decrement(&count) == 0) { - return true; - } + return atomic_decrement(&count) == 0; + } + + _ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of - return false; + return atomic_decrement(&count); } _ALWAYS_INLINE_ uint32_t get() const { // nothrow diff --git a/core/script_debugger_local.cpp b/core/script_debugger_local.cpp index ac4dafcf59..e61f9f7158 100644 --- a/core/script_debugger_local.cpp +++ b/core/script_debugger_local.cpp @@ -33,7 +33,7 @@ #include "core/os/os.h" #include "scene/main/scene_tree.h" -void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue) { +void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint) { if (!target_function.empty()) { String current_function = p_script->debug_get_stack_level_function(0); diff --git a/core/script_debugger_local.h b/core/script_debugger_local.h index 19151d4cb0..b3aed5e358 100644 --- a/core/script_debugger_local.h +++ b/core/script_debugger_local.h @@ -48,7 +48,7 @@ class ScriptDebuggerLocal : public ScriptDebugger { void print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix); public: - void debug(ScriptLanguage *p_script, bool p_can_continue); + void debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint); virtual void send_message(const String &p_message, const Array &p_args); virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info); diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp deleted file mode 100644 index e7ff7a3aef..0000000000 --- a/core/script_debugger_remote.cpp +++ /dev/null @@ -1,1104 +0,0 @@ -/*************************************************************************/ -/* script_debugger_remote.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "script_debugger_remote.h" - -#include "core/engine.h" -#include "core/io/ip.h" -#include "core/io/marshalls.h" -#include "core/os/input.h" -#include "core/os/os.h" -#include "core/project_settings.h" -#include "scene/main/node.h" -#include "scene/resources/packed_scene.h" - -void ScriptDebuggerRemote::_send_video_memory() { - - List<ResourceUsage> usage; - if (resource_usage_func) - resource_usage_func(&usage); - - usage.sort(); - - packet_peer_stream->put_var("message:video_mem"); - packet_peer_stream->put_var(usage.size() * 4); - - for (List<ResourceUsage>::Element *E = usage.front(); E; E = E->next()) { - - packet_peer_stream->put_var(E->get().path); - packet_peer_stream->put_var(E->get().type); - packet_peer_stream->put_var(E->get().format); - packet_peer_stream->put_var(E->get().vram); - } -} - -Error ScriptDebuggerRemote::connect_to_host(const String &p_host, uint16_t p_port) { - - IP_Address ip; - if (p_host.is_valid_ip_address()) - ip = p_host; - else - ip = IP::get_singleton()->resolve_hostname(p_host); - - int port = p_port; - - const int tries = 6; - int waits[tries] = { 1, 10, 100, 1000, 1000, 1000 }; - - tcp_client->connect_to_host(ip, port); - - for (int i = 0; i < tries; i++) { - - if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) { - print_verbose("Remote Debugger: Connected!"); - break; - } else { - - const int ms = waits[i]; - OS::get_singleton()->delay_usec(ms * 1000); - print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec."); - }; - }; - - if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) { - - ERR_PRINTS("Remote Debugger: Unable to connect. Status: " + String::num(tcp_client->get_status())); - return FAILED; - }; - - packet_peer_stream->set_stream_peer(tcp_client); - - return OK; -} - -void ScriptDebuggerRemote::_put_variable(const String &p_name, const Variant &p_variable) { - - packet_peer_stream->put_var(p_name); - - Variant var = p_variable; - if (p_variable.get_type() == Variant::OBJECT && !ObjectDB::instance_validate(p_variable)) { - var = Variant(); - } - - int len = 0; - Error err = encode_variant(var, NULL, len, true); - if (err != OK) - ERR_PRINT("Failed to encode variant"); - - if (len > packet_peer_stream->get_output_buffer_max_size()) { //limit to max size - packet_peer_stream->put_var(Variant()); - } else { - packet_peer_stream->put_var(var); - } -} - -void ScriptDebuggerRemote::_save_node(ObjectID id, const String &p_path) { - - Node *node = Object::cast_to<Node>(ObjectDB::get_instance(id)); - ERR_FAIL_COND(!node); - - Ref<PackedScene> ps = memnew(PackedScene); - ps->pack(node); - ResourceSaver::save(p_path, ps); -} - -void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue) { - - //this function is called when there is a debugger break (bug on script) - //or when execution is paused from editor - - if (!tcp_client->is_connected_to_host()) { - ERR_EXPLAIN("Script Debugger failed to connect, but being used anyway."); - ERR_FAIL(); - } - - packet_peer_stream->put_var("debug_enter"); - packet_peer_stream->put_var(2); - packet_peer_stream->put_var(p_can_continue); - packet_peer_stream->put_var(p_script->debug_get_error()); - - skip_profile_frame = true; // to avoid super long frame time for the frame - - Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode(); - if (mouse_mode != Input::MOUSE_MODE_VISIBLE) - Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); - - while (true) { - - _get_output(); - - if (packet_peer_stream->get_available_packet_count() > 0) { - - Variant var; - Error err = packet_peer_stream->get_var(var); - ERR_CONTINUE(err != OK); - ERR_CONTINUE(var.get_type() != Variant::ARRAY); - - Array cmd = var; - - ERR_CONTINUE(cmd.size() == 0); - ERR_CONTINUE(cmd[0].get_type() != Variant::STRING); - - String command = cmd[0]; - - if (command == "get_stack_dump") { - - packet_peer_stream->put_var("stack_dump"); - int slc = p_script->debug_get_stack_level_count(); - packet_peer_stream->put_var(slc); - - for (int i = 0; i < slc; i++) { - - Dictionary d; - d["file"] = p_script->debug_get_stack_level_source(i); - d["line"] = p_script->debug_get_stack_level_line(i); - d["function"] = p_script->debug_get_stack_level_function(i); - //d["id"]=p_script->debug_get_stack_level_ - d["id"] = 0; - - packet_peer_stream->put_var(d); - } - - } else if (command == "get_stack_frame_vars") { - - cmd.remove(0); - ERR_CONTINUE(cmd.size() != 1); - int lv = cmd[0]; - - List<String> members; - List<Variant> member_vals; - if (ScriptInstance *inst = p_script->debug_get_stack_level_instance(lv)) { - members.push_back("self"); - member_vals.push_back(inst->get_owner()); - } - p_script->debug_get_stack_level_members(lv, &members, &member_vals); - ERR_CONTINUE(members.size() != member_vals.size()); - - List<String> locals; - List<Variant> local_vals; - p_script->debug_get_stack_level_locals(lv, &locals, &local_vals); - ERR_CONTINUE(locals.size() != local_vals.size()); - - List<String> globals; - List<Variant> globals_vals; - p_script->debug_get_globals(&globals, &globals_vals); - ERR_CONTINUE(globals.size() != globals_vals.size()); - - packet_peer_stream->put_var("stack_frame_vars"); - packet_peer_stream->put_var(3 + (locals.size() + members.size() + globals.size()) * 2); - - { //locals - packet_peer_stream->put_var(locals.size()); - - List<String>::Element *E = locals.front(); - List<Variant>::Element *F = local_vals.front(); - - while (E) { - _put_variable(E->get(), F->get()); - - E = E->next(); - F = F->next(); - } - } - - { //members - packet_peer_stream->put_var(members.size()); - - List<String>::Element *E = members.front(); - List<Variant>::Element *F = member_vals.front(); - - while (E) { - - _put_variable(E->get(), F->get()); - - E = E->next(); - F = F->next(); - } - } - - { //globals - packet_peer_stream->put_var(globals.size()); - - List<String>::Element *E = globals.front(); - List<Variant>::Element *F = globals_vals.front(); - - while (E) { - _put_variable(E->get(), F->get()); - - E = E->next(); - F = F->next(); - } - } - - } else if (command == "step") { - - set_depth(-1); - set_lines_left(1); - break; - } else if (command == "next") { - - set_depth(0); - set_lines_left(1); - break; - - } else if (command == "continue") { - - set_depth(-1); - set_lines_left(-1); - OS::get_singleton()->move_window_to_foreground(); - break; - } else if (command == "break") { - ERR_PRINT("Got break when already broke!"); - break; - } else if (command == "request_scene_tree") { - - if (request_scene_tree) - request_scene_tree(request_scene_tree_ud); - - } else if (command == "request_video_mem") { - - _send_video_memory(); - } else if (command == "inspect_object") { - - ObjectID id = cmd[1]; - _send_object_id(id); - } else if (command == "set_object_property") { - - _set_object_property(cmd[1], cmd[2], cmd[3]); - - } else if (command == "reload_scripts") { - reload_all_scripts = true; - } else if (command == "breakpoint") { - - bool set = cmd[3]; - if (set) - insert_breakpoint(cmd[2], cmd[1]); - else - remove_breakpoint(cmd[2], cmd[1]); - - } else if (command == "save_node") { - _save_node(cmd[1], cmd[2]); - } else { - _parse_live_edit(cmd); - } - - } else { - OS::get_singleton()->delay_usec(10000); - OS::get_singleton()->process_and_drop_events(); - } - } - - packet_peer_stream->put_var("debug_exit"); - packet_peer_stream->put_var(0); - - if (mouse_mode != Input::MOUSE_MODE_VISIBLE) - Input::get_singleton()->set_mouse_mode(mouse_mode); -} - -void ScriptDebuggerRemote::_get_output() { - - mutex->lock(); - if (output_strings.size()) { - - locking = true; - packet_peer_stream->put_var("output"); - packet_peer_stream->put_var(output_strings.size()); - - while (output_strings.size()) { - - packet_peer_stream->put_var(output_strings.front()->get()); - output_strings.pop_front(); - } - locking = false; - } - - if (n_messages_dropped > 0) { - Message msg; - msg.message = "Too many messages! " + String::num_int64(n_messages_dropped) + " messages were dropped."; - messages.push_back(msg); - n_messages_dropped = 0; - } - - while (messages.size()) { - locking = true; - packet_peer_stream->put_var("message:" + messages.front()->get().message); - packet_peer_stream->put_var(messages.front()->get().data.size()); - for (int i = 0; i < messages.front()->get().data.size(); i++) { - packet_peer_stream->put_var(messages.front()->get().data[i]); - } - messages.pop_front(); - locking = false; - } - - if (n_errors_dropped > 0) { - OutputError oe; - oe.error = "TOO_MANY_ERRORS"; - oe.error_descr = "Too many errors! " + String::num_int64(n_errors_dropped) + " errors were dropped."; - oe.warning = false; - uint64_t time = OS::get_singleton()->get_ticks_msec(); - oe.hr = time / 3600000; - oe.min = (time / 60000) % 60; - oe.sec = (time / 1000) % 60; - oe.msec = time % 1000; - errors.push_back(oe); - n_errors_dropped = 0; - } - - while (errors.size()) { - locking = true; - packet_peer_stream->put_var("error"); - OutputError oe = errors.front()->get(); - - packet_peer_stream->put_var(oe.callstack.size() + 2); - - Array error_data; - - error_data.push_back(oe.hr); - error_data.push_back(oe.min); - error_data.push_back(oe.sec); - error_data.push_back(oe.msec); - error_data.push_back(oe.source_func); - error_data.push_back(oe.source_file); - error_data.push_back(oe.source_line); - error_data.push_back(oe.error); - error_data.push_back(oe.error_descr); - error_data.push_back(oe.warning); - packet_peer_stream->put_var(error_data); - packet_peer_stream->put_var(oe.callstack.size()); - for (int i = 0; i < oe.callstack.size(); i++) { - packet_peer_stream->put_var(oe.callstack[i]); - } - - errors.pop_front(); - locking = false; - } - mutex->unlock(); -} - -void ScriptDebuggerRemote::line_poll() { - - //the purpose of this is just processing events every now and then when the script might get too busy - //otherwise bugs like infinite loops can't be caught - if (poll_every % 2048 == 0) - _poll_events(); - poll_every++; -} - -void ScriptDebuggerRemote::_err_handler(void *ud, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type) { - - if (p_type == ERR_HANDLER_SCRIPT) - return; //ignore script errors, those go through debugger - - Vector<ScriptLanguage::StackInfo> si; - - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - si = ScriptServer::get_language(i)->debug_get_current_stack_info(); - if (si.size()) - break; - } - - ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)ud; - sdr->send_error(p_func, p_file, p_line, p_err, p_descr, p_type, si); -} - -bool ScriptDebuggerRemote::_parse_live_edit(const Array &p_command) { - - String cmdstr = p_command[0]; - if (!live_edit_funcs || !cmdstr.begins_with("live_")) - return false; - - //print_line(Variant(cmd).get_construct_string()); - if (cmdstr == "live_set_root") { - - if (!live_edit_funcs->root_func) - return true; - //print_line("root: "+Variant(cmd).get_construct_string()); - live_edit_funcs->root_func(live_edit_funcs->udata, p_command[1], p_command[2]); - - } else if (cmdstr == "live_node_path") { - - if (!live_edit_funcs->node_path_func) - return true; - //print_line("path: "+Variant(cmd).get_construct_string()); - - live_edit_funcs->node_path_func(live_edit_funcs->udata, p_command[1], p_command[2]); - - } else if (cmdstr == "live_res_path") { - - if (!live_edit_funcs->res_path_func) - return true; - live_edit_funcs->res_path_func(live_edit_funcs->udata, p_command[1], p_command[2]); - - } else if (cmdstr == "live_node_prop_res") { - if (!live_edit_funcs->node_set_res_func) - return true; - - live_edit_funcs->node_set_res_func(live_edit_funcs->udata, p_command[1], p_command[2], p_command[3]); - - } else if (cmdstr == "live_node_prop") { - - if (!live_edit_funcs->node_set_func) - return true; - live_edit_funcs->node_set_func(live_edit_funcs->udata, p_command[1], p_command[2], p_command[3]); - - } else if (cmdstr == "live_res_prop_res") { - - if (!live_edit_funcs->res_set_res_func) - return true; - live_edit_funcs->res_set_res_func(live_edit_funcs->udata, p_command[1], p_command[2], p_command[3]); - - } else if (cmdstr == "live_res_prop") { - - if (!live_edit_funcs->res_set_func) - return true; - live_edit_funcs->res_set_func(live_edit_funcs->udata, p_command[1], p_command[2], p_command[3]); - - } else if (cmdstr == "live_node_call") { - - if (!live_edit_funcs->node_call_func) - return true; - live_edit_funcs->node_call_func(live_edit_funcs->udata, p_command[1], p_command[2], p_command[3], p_command[4], p_command[5], p_command[6], p_command[7]); - - } else if (cmdstr == "live_res_call") { - - if (!live_edit_funcs->res_call_func) - return true; - live_edit_funcs->res_call_func(live_edit_funcs->udata, p_command[1], p_command[2], p_command[3], p_command[4], p_command[5], p_command[6], p_command[7]); - - } else if (cmdstr == "live_create_node") { - - live_edit_funcs->tree_create_node_func(live_edit_funcs->udata, p_command[1], p_command[2], p_command[3]); - - } else if (cmdstr == "live_instance_node") { - - live_edit_funcs->tree_instance_node_func(live_edit_funcs->udata, p_command[1], p_command[2], p_command[3]); - - } else if (cmdstr == "live_remove_node") { - - live_edit_funcs->tree_remove_node_func(live_edit_funcs->udata, p_command[1]); - - } else if (cmdstr == "live_remove_and_keep_node") { - - live_edit_funcs->tree_remove_and_keep_node_func(live_edit_funcs->udata, p_command[1], p_command[2]); - } else if (cmdstr == "live_restore_node") { - - live_edit_funcs->tree_restore_node_func(live_edit_funcs->udata, p_command[1], p_command[2], p_command[3]); - - } else if (cmdstr == "live_duplicate_node") { - - live_edit_funcs->tree_duplicate_node_func(live_edit_funcs->udata, p_command[1], p_command[2]); - } else if (cmdstr == "live_reparent_node") { - - live_edit_funcs->tree_reparent_node_func(live_edit_funcs->udata, p_command[1], p_command[2], p_command[3], p_command[4]); - - } else { - - return false; - } - - return true; -} - -void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) { - - Object *obj = ObjectDB::get_instance(p_id); - if (!obj) - return; - - typedef Pair<PropertyInfo, Variant> PropertyDesc; - List<PropertyDesc> properties; - - if (ScriptInstance *si = obj->get_script_instance()) { - if (!si->get_script().is_null()) { - - typedef Map<const Script *, Set<StringName> > ScriptMemberMap; - typedef Map<const Script *, Map<StringName, Variant> > ScriptConstantsMap; - - ScriptMemberMap members; - members[si->get_script().ptr()] = Set<StringName>(); - si->get_script()->get_members(&(members[si->get_script().ptr()])); - - ScriptConstantsMap constants; - constants[si->get_script().ptr()] = Map<StringName, Variant>(); - si->get_script()->get_constants(&(constants[si->get_script().ptr()])); - - Ref<Script> base = si->get_script()->get_base_script(); - while (base.is_valid()) { - - members[base.ptr()] = Set<StringName>(); - base->get_members(&(members[base.ptr()])); - - constants[base.ptr()] = Map<StringName, Variant>(); - base->get_constants(&(constants[base.ptr()])); - - base = base->get_base_script(); - } - - for (ScriptMemberMap::Element *sm = members.front(); sm; sm = sm->next()) { - for (Set<StringName>::Element *E = sm->get().front(); E; E = E->next()) { - Variant m; - if (si->get(E->get(), m)) { - String script_path = sm->key() == si->get_script().ptr() ? "" : sm->key()->get_path().get_file() + "/"; - PropertyInfo pi(m.get_type(), "Members/" + script_path + E->get()); - properties.push_back(PropertyDesc(pi, m)); - } - } - } - - for (ScriptConstantsMap::Element *sc = constants.front(); sc; sc = sc->next()) { - for (Map<StringName, Variant>::Element *E = sc->get().front(); E; E = E->next()) { - String script_path = sc->key() == si->get_script().ptr() ? "" : sc->key()->get_path().get_file() + "/"; - if (E->value().get_type() == Variant::OBJECT) { - Variant id = ((Object *)E->value())->get_instance_id(); - PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object"); - properties.push_back(PropertyDesc(pi, id)); - } else { - PropertyInfo pi(E->value().get_type(), "Constants/" + script_path + E->key()); - properties.push_back(PropertyDesc(pi, E->value())); - } - } - } - } - } - if (Node *node = Object::cast_to<Node>(obj)) { - PropertyInfo pi(Variant::NODE_PATH, String("Node/path")); - properties.push_front(PropertyDesc(pi, node->get_path())); - } else if (Resource *res = Object::cast_to<Resource>(obj)) { - if (Script *s = Object::cast_to<Script>(res)) { - Map<StringName, Variant> constants; - s->get_constants(&constants); - for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { - if (E->value().get_type() == Variant::OBJECT) { - Variant id = ((Object *)E->value())->get_instance_id(); - PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object"); - properties.push_front(PropertyDesc(pi, E->value())); - } else { - PropertyInfo pi(E->value().get_type(), String("Constants/") + E->key()); - properties.push_front(PropertyDesc(pi, E->value())); - } - } - } - } - - List<PropertyInfo> pinfo; - obj->get_property_list(&pinfo, true); - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - if (E->get().usage & (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CATEGORY)) { - properties.push_back(PropertyDesc(E->get(), obj->get(E->get().name))); - } - } - - Array send_props; - for (int i = 0; i < properties.size(); i++) { - const PropertyInfo &pi = properties[i].first; - Variant &var = properties[i].second; - - WeakRef *ref = Object::cast_to<WeakRef>(var); - if (ref) { - var = ref->get_ref(); - } - - RES res = var; - - Array prop; - prop.push_back(pi.name); - prop.push_back(pi.type); - - //only send information that can be sent.. - int len = 0; //test how big is this to encode - encode_variant(var, NULL, len); - if (len > packet_peer_stream->get_output_buffer_max_size()) { //limit to max size - prop.push_back(PROPERTY_HINT_OBJECT_TOO_BIG); - prop.push_back(""); - prop.push_back(pi.usage); - prop.push_back(Variant()); - } else { - prop.push_back(pi.hint); - prop.push_back(pi.hint_string); - prop.push_back(pi.usage); - - if (!res.is_null()) { - var = res->get_path(); - } - - prop.push_back(var); - } - send_props.push_back(prop); - } - - packet_peer_stream->put_var("message:inspect_object"); - packet_peer_stream->put_var(3); - packet_peer_stream->put_var(p_id); - packet_peer_stream->put_var(obj->get_class()); - packet_peer_stream->put_var(send_props); -} - -void ScriptDebuggerRemote::_set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value) { - - Object *obj = ObjectDB::get_instance(p_id); - if (!obj) - return; - - String prop_name = p_property; - if (p_property.begins_with("Members/")) { - Vector<String> ss = p_property.split("/"); - prop_name = ss[ss.size() - 1]; - } - - obj->set(prop_name, p_value); -} - -void ScriptDebuggerRemote::_poll_events() { - - //this si called from ::idle_poll, happens only when running the game, - //does not get called while on debug break - - while (packet_peer_stream->get_available_packet_count() > 0) { - - _get_output(); - - //send over output_strings - - Variant var; - Error err = packet_peer_stream->get_var(var); - - ERR_CONTINUE(err != OK); - ERR_CONTINUE(var.get_type() != Variant::ARRAY); - - Array cmd = var; - - ERR_CONTINUE(cmd.size() == 0); - ERR_CONTINUE(cmd[0].get_type() != Variant::STRING); - - String command = cmd[0]; - //cmd.remove(0); - - if (command == "break") { - - if (get_break_language()) - debug(get_break_language()); - } else if (command == "request_scene_tree") { - - if (request_scene_tree) - request_scene_tree(request_scene_tree_ud); - } else if (command == "request_video_mem") { - - _send_video_memory(); - } else if (command == "inspect_object") { - - ObjectID id = cmd[1]; - _send_object_id(id); - } else if (command == "set_object_property") { - - _set_object_property(cmd[1], cmd[2], cmd[3]); - - } else if (command == "start_profiling") { - - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ScriptServer::get_language(i)->profiling_start(); - } - - max_frame_functions = cmd[1]; - profiler_function_signature_map.clear(); - profiling = true; - frame_time = 0; - idle_time = 0; - physics_time = 0; - physics_frame_time = 0; - - print_line("PROFILING ALRIGHT!"); - - } else if (command == "stop_profiling") { - - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ScriptServer::get_language(i)->profiling_stop(); - } - profiling = false; - _send_profiling_data(false); - print_line("PROFILING END!"); - } else if (command == "reload_scripts") { - reload_all_scripts = true; - } else if (command == "breakpoint") { - - bool set = cmd[3]; - if (set) - insert_breakpoint(cmd[2], cmd[1]); - else - remove_breakpoint(cmd[2], cmd[1]); - } else { - _parse_live_edit(cmd); - } - } -} - -void ScriptDebuggerRemote::_send_profiling_data(bool p_for_frame) { - - int ofs = 0; - - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - if (p_for_frame) - ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&profile_info.write[ofs], profile_info.size() - ofs); - else - ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&profile_info.write[ofs], profile_info.size() - ofs); - } - - for (int i = 0; i < ofs; i++) { - profile_info_ptrs.write[i] = &profile_info.write[i]; - } - - SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa; - sa.sort(profile_info_ptrs.ptrw(), ofs); - - int to_send = MIN(ofs, max_frame_functions); - - //check signatures first - uint64_t total_script_time = 0; - - for (int i = 0; i < to_send; i++) { - - if (!profiler_function_signature_map.has(profile_info_ptrs[i]->signature)) { - - int idx = profiler_function_signature_map.size(); - packet_peer_stream->put_var("profile_sig"); - packet_peer_stream->put_var(2); - packet_peer_stream->put_var(profile_info_ptrs[i]->signature); - packet_peer_stream->put_var(idx); - - profiler_function_signature_map[profile_info_ptrs[i]->signature] = idx; - } - - total_script_time += profile_info_ptrs[i]->self_time; - } - - //send frames then - - if (p_for_frame) { - packet_peer_stream->put_var("profile_frame"); - packet_peer_stream->put_var(8 + profile_frame_data.size() * 2 + to_send * 4); - } else { - packet_peer_stream->put_var("profile_total"); - packet_peer_stream->put_var(8 + to_send * 4); - } - - packet_peer_stream->put_var(Engine::get_singleton()->get_frames_drawn()); //total frame time - packet_peer_stream->put_var(frame_time); //total frame time - packet_peer_stream->put_var(idle_time); //idle frame time - packet_peer_stream->put_var(physics_time); //fixed frame time - packet_peer_stream->put_var(physics_frame_time); //fixed frame time - - packet_peer_stream->put_var(USEC_TO_SEC(total_script_time)); //total script execution time - - if (p_for_frame) { - - packet_peer_stream->put_var(profile_frame_data.size()); //how many profile framedatas to send - packet_peer_stream->put_var(to_send); //how many script functions to send - for (int i = 0; i < profile_frame_data.size(); i++) { - - packet_peer_stream->put_var(profile_frame_data[i].name); - packet_peer_stream->put_var(profile_frame_data[i].data); - } - } else { - packet_peer_stream->put_var(0); //how many script functions to send - packet_peer_stream->put_var(to_send); //how many script functions to send - } - - for (int i = 0; i < to_send; i++) { - - int sig_id = -1; - - if (profiler_function_signature_map.has(profile_info_ptrs[i]->signature)) { - sig_id = profiler_function_signature_map[profile_info_ptrs[i]->signature]; - } - - packet_peer_stream->put_var(sig_id); - packet_peer_stream->put_var(profile_info_ptrs[i]->call_count); - packet_peer_stream->put_var(profile_info_ptrs[i]->total_time / 1000000.0); - packet_peer_stream->put_var(profile_info_ptrs[i]->self_time / 1000000.0); - } - - if (p_for_frame) { - profile_frame_data.clear(); - } -} - -void ScriptDebuggerRemote::idle_poll() { - - // this function is called every frame, except when there is a debugger break (::debug() in this class) - // execution stops and remains in the ::debug function - - _get_output(); - - if (requested_quit) { - - packet_peer_stream->put_var("kill_me"); - packet_peer_stream->put_var(0); - requested_quit = false; - } - - if (performance) { - - uint64_t pt = OS::get_singleton()->get_ticks_msec(); - if (pt - last_perf_time > 1000) { - - last_perf_time = pt; - int max = performance->get("MONITOR_MAX"); - Array arr; - arr.resize(max); - for (int i = 0; i < max; i++) { - arr[i] = performance->call("get_monitor", i); - } - packet_peer_stream->put_var("performance"); - packet_peer_stream->put_var(1); - packet_peer_stream->put_var(arr); - } - } - - if (profiling) { - - if (skip_profile_frame) { - skip_profile_frame = false; - } else { - //send profiling info normally - _send_profiling_data(true); - } - } - - if (reload_all_scripts) { - - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ScriptServer::get_language(i)->reload_all_scripts(); - } - reload_all_scripts = false; - } - - _poll_events(); -} - -void ScriptDebuggerRemote::send_message(const String &p_message, const Array &p_args) { - - mutex->lock(); - if (!locking && tcp_client->is_connected_to_host()) { - - if (messages.size() >= max_messages_per_frame) { - n_messages_dropped++; - } else { - Message msg; - msg.message = p_message; - msg.data = p_args; - messages.push_back(msg); - } - } - mutex->unlock(); -} - -void ScriptDebuggerRemote::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) { - - OutputError oe; - oe.error = p_err; - oe.error_descr = p_descr; - oe.source_file = p_file; - oe.source_line = p_line; - oe.source_func = p_func; - oe.warning = p_type == ERR_HANDLER_WARNING; - uint64_t time = OS::get_singleton()->get_ticks_msec(); - oe.hr = time / 3600000; - oe.min = (time / 60000) % 60; - oe.sec = (time / 1000) % 60; - oe.msec = time % 1000; - Array cstack; - - cstack.resize(p_stack_info.size() * 3); - for (int i = 0; i < p_stack_info.size(); i++) { - cstack[i * 3 + 0] = p_stack_info[i].file; - cstack[i * 3 + 1] = p_stack_info[i].func; - cstack[i * 3 + 2] = p_stack_info[i].line; - } - - oe.callstack = cstack; - - mutex->lock(); - - if (!locking && tcp_client->is_connected_to_host()) { - - if (errors.size() >= max_errors_per_frame) { - n_errors_dropped++; - } else { - errors.push_back(oe); - } - } - - mutex->unlock(); -} - -void ScriptDebuggerRemote::_print_handler(void *p_this, const String &p_string, bool p_error) { - - ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)p_this; - - uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000; - sdr->msec_count += ticks - sdr->last_msec; - sdr->last_msec = ticks; - - if (sdr->msec_count > 1000) { - sdr->char_count = 0; - sdr->msec_count = 0; - } - - String s = p_string; - int allowed_chars = MIN(MAX(sdr->max_cps - sdr->char_count, 0), s.length()); - - if (allowed_chars == 0) - return; - - if (allowed_chars < s.length()) { - s = s.substr(0, allowed_chars); - } - - sdr->char_count += allowed_chars; - bool overflowed = sdr->char_count >= sdr->max_cps; - - sdr->mutex->lock(); - if (!sdr->locking && sdr->tcp_client->is_connected_to_host()) { - - if (overflowed) - s += "[...]"; - - sdr->output_strings.push_back(s); - - if (overflowed) { - sdr->output_strings.push_back("[output overflow, print less text!]"); - } - } - sdr->mutex->unlock(); -} - -void ScriptDebuggerRemote::request_quit() { - - requested_quit = true; -} - -void ScriptDebuggerRemote::set_request_scene_tree_message_func(RequestSceneTreeMessageFunc p_func, void *p_udata) { - - request_scene_tree = p_func; - request_scene_tree_ud = p_udata; -} - -void ScriptDebuggerRemote::set_live_edit_funcs(LiveEditFuncs *p_funcs) { - - live_edit_funcs = p_funcs; -} - -bool ScriptDebuggerRemote::is_profiling() const { - - return profiling; -} -void ScriptDebuggerRemote::add_profiling_frame_data(const StringName &p_name, const Array &p_data) { - - int idx = -1; - for (int i = 0; i < profile_frame_data.size(); i++) { - if (profile_frame_data[i].name == p_name) { - idx = i; - break; - } - } - - FrameData fd; - fd.name = p_name; - fd.data = p_data; - - if (idx == -1) { - profile_frame_data.push_back(fd); - } else { - profile_frame_data.write[idx] = fd; - } -} - -void ScriptDebuggerRemote::profiling_start() { - //ignores this, uses it via connection -} - -void ScriptDebuggerRemote::profiling_end() { - //ignores this, uses it via connection -} - -void ScriptDebuggerRemote::profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { - - frame_time = p_frame_time; - idle_time = p_idle_time; - physics_time = p_physics_time; - physics_frame_time = p_physics_frame_time; -} - -ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_func = NULL; - -ScriptDebuggerRemote::ScriptDebuggerRemote() : - profiling(false), - max_frame_functions(16), - skip_profile_frame(false), - reload_all_scripts(false), - tcp_client(Ref<StreamPeerTCP>(memnew(StreamPeerTCP))), - packet_peer_stream(Ref<PacketPeerStream>(memnew(PacketPeerStream))), - last_perf_time(0), - performance(Engine::get_singleton()->get_singleton_object("Performance")), - requested_quit(false), - mutex(Mutex::create()), - max_messages_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_messages_per_frame")), - n_messages_dropped(0), - max_errors_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_errors_per_frame")), - n_errors_dropped(0), - max_cps(GLOBAL_GET("network/limits/debugger_stdout/max_chars_per_second")), - char_count(0), - last_msec(0), - msec_count(0), - locking(false), - poll_every(0), - request_scene_tree(NULL), - live_edit_funcs(NULL) { - - packet_peer_stream->set_stream_peer(tcp_client); - packet_peer_stream->set_output_buffer_max_size(1024 * 1024 * 8); //8mb should be way more than enough - - phl.printfunc = _print_handler; - phl.userdata = this; - add_print_handler(&phl); - - eh.errfunc = _err_handler; - eh.userdata = this; - add_error_handler(&eh); - - profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions")); - profile_info_ptrs.resize(profile_info.size()); -} - -ScriptDebuggerRemote::~ScriptDebuggerRemote() { - - remove_print_handler(&phl); - remove_error_handler(&eh); - memdelete(mutex); -} diff --git a/core/script_debugger_remote.h b/core/script_debugger_remote.h deleted file mode 100644 index 1fc9d7c7f1..0000000000 --- a/core/script_debugger_remote.h +++ /dev/null @@ -1,179 +0,0 @@ -/*************************************************************************/ -/* script_debugger_remote.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SCRIPT_DEBUGGER_REMOTE_H -#define SCRIPT_DEBUGGER_REMOTE_H - -#include "core/io/packet_peer.h" -#include "core/io/stream_peer_tcp.h" -#include "core/list.h" -#include "core/os/os.h" -#include "core/script_language.h" - -class ScriptDebuggerRemote : public ScriptDebugger { - - struct Message { - - String message; - Array data; - }; - - struct ProfileInfoSort { - - bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const { - return A->total_time < B->total_time; - } - }; - - Vector<ScriptLanguage::ProfilingInfo> profile_info; - Vector<ScriptLanguage::ProfilingInfo *> profile_info_ptrs; - - Map<StringName, int> profiler_function_signature_map; - float frame_time, idle_time, physics_time, physics_frame_time; - - bool profiling; - int max_frame_functions; - bool skip_profile_frame; - bool reload_all_scripts; - - Ref<StreamPeerTCP> tcp_client; - Ref<PacketPeerStream> packet_peer_stream; - - uint64_t last_perf_time; - Object *performance; - bool requested_quit; - Mutex *mutex; - - struct OutputError { - - int hr; - int min; - int sec; - int msec; - String source_file; - String source_func; - int source_line; - String error; - String error_descr; - bool warning; - Array callstack; - }; - - List<String> output_strings; - List<Message> messages; - int max_messages_per_frame; - int n_messages_dropped; - List<OutputError> errors; - int max_errors_per_frame; - int n_errors_dropped; - - int max_cps; - int char_count; - uint64_t last_msec; - uint64_t msec_count; - - bool locking; //hack to avoid a deadloop - static void _print_handler(void *p_this, const String &p_string, bool p_error); - - PrintHandlerList phl; - - void _get_output(); - void _poll_events(); - uint32_t poll_every; - - bool _parse_live_edit(const Array &p_command); - - RequestSceneTreeMessageFunc request_scene_tree; - void *request_scene_tree_ud; - - void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value); - - void _send_object_id(ObjectID p_id); - void _send_video_memory(); - LiveEditFuncs *live_edit_funcs; - - ErrorHandlerList eh; - static void _err_handler(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type); - - void _send_profiling_data(bool p_for_frame); - - struct FrameData { - - StringName name; - Array data; - }; - - Vector<FrameData> profile_frame_data; - - void _put_variable(const String &p_name, const Variant &p_variable); - - void _save_node(ObjectID id, const String &p_path); - -public: - struct ResourceUsage { - - String path; - String format; - String type; - RID id; - int vram; - bool operator<(const ResourceUsage &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; } - }; - - typedef void (*ResourceUsageFunc)(List<ResourceUsage> *); - - static ResourceUsageFunc resource_usage_func; - - Error connect_to_host(const String &p_host, uint16_t p_port); - virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true); - virtual void idle_poll(); - virtual void line_poll(); - - virtual bool is_remote() const { return true; } - virtual void request_quit(); - - virtual void send_message(const String &p_message, const Array &p_args); - virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info); - - virtual void set_request_scene_tree_message_func(RequestSceneTreeMessageFunc p_func, void *p_udata); - virtual void set_live_edit_funcs(LiveEditFuncs *p_funcs); - - virtual bool is_profiling() const; - virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data); - - virtual void profiling_start(); - virtual void profiling_end(); - virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time); - - ScriptDebuggerRemote(); - ~ScriptDebuggerRemote(); -}; - -#endif // SCRIPT_DEBUGGER_REMOTE_H diff --git a/core/script_language.cpp b/core/script_language.cpp index 4a6f904f9d..cbe4681eca 100644 --- a/core/script_language.cpp +++ b/core/script_language.cpp @@ -30,6 +30,7 @@ #include "script_language.h" +#include "core/core_string_names.h" #include "core/project_settings.h" ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES]; @@ -49,6 +50,52 @@ void Script::_notification(int p_what) { } } +Variant Script::_get_property_default_value(const StringName &p_property) { + Variant ret; + get_property_default_value(p_property, ret); + return ret; +} + +Array Script::_get_script_property_list() { + Array ret; + List<PropertyInfo> list; + get_script_property_list(&list); + for (List<PropertyInfo>::Element *E = list.front(); E; E = E->next()) { + ret.append(E->get().operator Dictionary()); + } + return ret; +} + +Array Script::_get_script_method_list() { + Array ret; + List<MethodInfo> list; + get_script_method_list(&list); + for (List<MethodInfo>::Element *E = list.front(); E; E = E->next()) { + ret.append(E->get().operator Dictionary()); + } + return ret; +} + +Array Script::_get_script_signal_list() { + Array ret; + List<MethodInfo> list; + get_script_signal_list(&list); + for (List<MethodInfo>::Element *E = list.front(); E; E = E->next()) { + ret.append(E->get().operator Dictionary()); + } + return ret; +} + +Dictionary Script::_get_script_constant_map() { + Dictionary ret; + Map<StringName, Variant> map; + get_constants(&map); + for (Map<StringName, Variant>::Element *E = map.front(); E; E = E->next()) { + ret[E->key()] = E->value(); + } + return ret; +} + void Script::_bind_methods() { ClassDB::bind_method(D_METHOD("can_instance"), &Script::can_instance); @@ -63,6 +110,12 @@ void Script::_bind_methods() { ClassDB::bind_method(D_METHOD("has_script_signal", "signal_name"), &Script::has_script_signal); + ClassDB::bind_method(D_METHOD("get_script_property_list"), &Script::_get_script_property_list); + ClassDB::bind_method(D_METHOD("get_script_method_list"), &Script::_get_script_method_list); + ClassDB::bind_method(D_METHOD("get_script_signal_list"), &Script::_get_script_signal_list); + ClassDB::bind_method(D_METHOD("get_script_constant_map"), &Script::_get_script_constant_map); + ClassDB::bind_method(D_METHOD("get_property_default_value", "property"), &Script::_get_property_default_value); + ClassDB::bind_method(D_METHOD("is_tool"), &Script::is_tool); ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_code", PROPERTY_HINT_NONE, "", 0), "set_source_code", "get_source_code"); @@ -165,6 +218,7 @@ void ScriptServer::global_classes_clear() { } void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path) { + ERR_FAIL_COND_MSG(p_class == p_base || (global_classes.has(p_base) && get_global_class_native_base(p_base) == p_class), "Cyclic inheritance in script class."); GlobalScriptClass g; g.language = p_language; g.path = p_path; diff --git a/core/script_language.h b/core/script_language.h index 005e21e2cc..f90bb4b6c3 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -36,10 +36,6 @@ #include "core/pair.h" #include "core/resource.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - class ScriptLanguage; typedef void (*ScriptEditRequestFunction)(const String &p_path); @@ -113,6 +109,12 @@ protected: friend class PlaceHolderScriptInstance; virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {} + Variant _get_property_default_value(const StringName &p_property); + Array _get_script_property_list(); + Array _get_script_method_list(); + Array _get_script_signal_list(); + Dictionary _get_script_constant_map(); + public: virtual bool can_instance() const = 0; @@ -173,6 +175,11 @@ public: virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount); virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount); virtual void notification(int p_notification) = 0; + virtual String to_string(bool *r_valid) { + if (r_valid) + *r_valid = false; + return String(); + } //this is used by script languages that keep a reference counter of their own //you can make make Ref<> not die when it reaches zero, so deleting the reference @@ -195,6 +202,35 @@ public: virtual ~ScriptInstance(); }; +struct ScriptCodeCompletionOption { + enum Kind { + KIND_CLASS, + KIND_FUNCTION, + KIND_SIGNAL, + KIND_VARIABLE, + KIND_MEMBER, + KIND_ENUM, + KIND_CONSTANT, + KIND_NODE_PATH, + KIND_FILE_PATH, + KIND_PLAIN_TEXT, + }; + Kind kind; + String display; + String insert_text; + RES icon; + + ScriptCodeCompletionOption() { + kind = KIND_PLAIN_TEXT; + } + + ScriptCodeCompletionOption(const String &p_text, Kind p_kind) { + display = p_text; + insert_text = p_text; + kind = p_kind; + } +}; + class ScriptCodeCompletionCache { static ScriptCodeCompletionCache *singleton; @@ -245,7 +281,7 @@ public: virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return ERR_UNAVAILABLE; } virtual bool overrides_external_editor() { return false; } - virtual Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint) { return ERR_UNAVAILABLE; } + virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) { return ERR_UNAVAILABLE; } struct LookupResult { enum Type { @@ -264,7 +300,7 @@ public: int location; }; - virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_base_path, Object *p_owner, LookupResult &r_result) { return ERR_UNAVAILABLE; } + virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) { return ERR_UNAVAILABLE; } virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const = 0; virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) = 0; @@ -391,31 +427,6 @@ class ScriptDebugger { ScriptLanguage *break_lang; public: - typedef void (*RequestSceneTreeMessageFunc)(void *); - - struct LiveEditFuncs { - - void *udata; - void (*node_path_func)(void *, const NodePath &p_path, int p_id); - void (*res_path_func)(void *, const String &p_path, int p_id); - - void (*node_set_func)(void *, int p_id, const StringName &p_prop, const Variant &p_value); - void (*node_set_res_func)(void *, int p_id, const StringName &p_prop, const String &p_value); - void (*node_call_func)(void *, int p_id, const StringName &p_method, VARIANT_ARG_DECLARE); - void (*res_set_func)(void *, int p_id, const StringName &p_prop, const Variant &p_value); - void (*res_set_res_func)(void *, int p_id, const StringName &p_prop, const String &p_value); - void (*res_call_func)(void *, int p_id, const StringName &p_method, VARIANT_ARG_DECLARE); - void (*root_func)(void *, const NodePath &p_scene_path, const String &p_scene_from); - - void (*tree_create_node_func)(void *, const NodePath &p_parent, const String &p_type, const String &p_name); - void (*tree_instance_node_func)(void *, const NodePath &p_parent, const String &p_path, const String &p_name); - void (*tree_remove_node_func)(void *, const NodePath &p_at); - void (*tree_remove_and_keep_node_func)(void *, const NodePath &p_at, ObjectID p_keep_id); - void (*tree_restore_node_func)(void *, ObjectID p_id, const NodePath &p_at, int p_at_pos); - void (*tree_duplicate_node_func)(void *, const NodePath &p_at, const String &p_new_name); - void (*tree_reparent_node_func)(void *, const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos); - }; - _FORCE_INLINE_ static ScriptDebugger *get_singleton() { return singleton; } void set_lines_left(int p_left); int get_lines_left() const; @@ -431,7 +442,7 @@ public: void clear_breakpoints(); const Map<int, Set<StringName> > &get_breakpoints() const { return breakpoints; } - virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true) = 0; + virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true, bool p_is_error_breakpoint = false) = 0; virtual void idle_poll(); virtual void line_poll(); @@ -444,8 +455,7 @@ public: virtual bool is_remote() const { return false; } virtual void request_quit() {} - virtual void set_request_scene_tree_message_func(RequestSceneTreeMessageFunc p_func, void *p_udata) {} - virtual void set_live_edit_funcs(LiveEditFuncs *p_funcs) {} + virtual void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {} virtual bool is_profiling() const = 0; virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data) = 0; diff --git a/core/self_list.h b/core/self_list.h index 314d440977..07abcd1d53 100644 --- a/core/self_list.h +++ b/core/self_list.h @@ -31,6 +31,7 @@ #ifndef SELF_LIST_H #define SELF_LIST_H +#include "core/error_macros.h" #include "core/typedefs.h" template <class T> diff --git a/core/set.h b/core/set.h index 81250068af..68431c294a 100644 --- a/core/set.h +++ b/core/set.h @@ -34,12 +34,8 @@ #include "core/os/memory.h" #include "core/typedefs.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - // based on the very nice implementation of rb-trees by: -// http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html +// https://web.archive.org/web/20120507164830/http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html template <class T, class C = Comparator<T>, class A = DefaultAllocator> class Set { diff --git a/core/sort_array.h b/core/sort_array.h index 0f258aec3e..d330e0c647 100644 --- a/core/sort_array.h +++ b/core/sort_array.h @@ -31,6 +31,7 @@ #ifndef SORT_ARRAY_H #define SORT_ARRAY_H +#include "core/error_macros.h" #include "core/typedefs.h" #define ERR_BAD_COMPARE(cond) \ @@ -179,14 +180,14 @@ public: while (true) { while (compare(p_array[p_first], p_pivot)) { if (Validate) { - ERR_BAD_COMPARE(p_first == unmodified_last - 1) + ERR_BAD_COMPARE(p_first == unmodified_last - 1); } p_first++; } p_last--; while (compare(p_pivot, p_array[p_last])) { if (Validate) { - ERR_BAD_COMPARE(p_last == unmodified_first) + ERR_BAD_COMPARE(p_last == unmodified_first); } p_last--; } @@ -259,7 +260,7 @@ public: int next = p_last - 1; while (compare(p_value, p_array[next])) { if (Validate) { - ERR_BAD_COMPARE(next == 0) + ERR_BAD_COMPARE(next == 0); } p_array[p_last] = p_array[next]; p_last = next; diff --git a/core/string_builder.cpp b/core/string_builder.cpp index 35526e0d70..22eed70f8b 100644 --- a/core/string_builder.cpp +++ b/core/string_builder.cpp @@ -34,6 +34,9 @@ StringBuilder &StringBuilder::append(const String &p_string) { + if (p_string == String()) + return *this; + strings.push_back(p_string); appended_strings.push_back(-1); diff --git a/core/string_builder.h b/core/string_builder.h index 40d70e8f45..0c4985d230 100644 --- a/core/string_builder.h +++ b/core/string_builder.h @@ -70,6 +70,10 @@ public: return appended_strings.size(); } + _FORCE_INLINE_ uint32_t get_string_length() const { + return string_length; + } + String as_string() const; _FORCE_INLINE_ operator String() const { diff --git a/core/string_name.cpp b/core/string_name.cpp index 10b71ad3ac..b1a8bfb849 100644 --- a/core/string_name.cpp +++ b/core/string_name.cpp @@ -205,7 +205,6 @@ StringName::StringName(const char *p_name) { // exists lock->unlock(); return; - } else { } } @@ -253,7 +252,6 @@ StringName::StringName(const StaticCString &p_static_string) { // exists lock->unlock(); return; - } else { } } @@ -301,7 +299,6 @@ StringName::StringName(const String &p_name) { // exists lock->unlock(); return; - } else { } } diff --git a/core/string_name.h b/core/string_name.h index 0984b0181f..6dd960abd5 100644 --- a/core/string_name.h +++ b/core/string_name.h @@ -34,9 +34,6 @@ #include "core/os/mutex.h" #include "core/safe_refcount.h" #include "core/ustring.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ struct StaticCString { diff --git a/core/translation.cpp b/core/translation.cpp index 6921f1d9f1..4a662b2590 100644 --- a/core/translation.cpp +++ b/core/translation.cpp @@ -179,6 +179,7 @@ static const char *locale_list[] = { "ff_SN", // Fulah (Senegal) "fi", // Finnish "fi_FI", // Finnish (Finland) + "fil", // Filipino "fil_PH", // Filipino (Philippines) "fo_FO", // Faroese (Faroe Islands) "fr", // French @@ -227,6 +228,7 @@ static const char *locale_list[] = { "ja", // Japanese "ja_JP", // Japanese (Japan) "kab_DZ", // Kabyle (Algeria) + "ka", // Georgian "ka_GE", // Georgian (Georgia) "kk_KZ", // Kazakh (Kazakhstan) "kl_GL", // Kalaallisut (Greenland) @@ -257,10 +259,12 @@ static const char *locale_list[] = { "mg_MG", // Malagasy (Madagascar) "mh_MH", // Marshallese (Marshall Islands) "mhr_RU", // Eastern Mari (Russia) - "mi_NZ", // Maori (New Zealand) + "mi", // Māori + "mi_NZ", // Māori (New Zealand) "miq_NI", // Mískito (Nicaragua) "mk", // Macedonian "mk_MK", // Macedonian (Macedonia) + "ml", // Malayalam "ml_IN", // Malayalam (India) "mni_IN", // Manipuri (India) "mn_MN", // Mongolian (Mongolia) @@ -326,6 +330,7 @@ static const char *locale_list[] = { "sgs_LT", // Samogitian (Lithuania) "shs_CA", // Shuswap (Canada) "sid_ET", // Sidamo (Ethiopia) + "si", // Sinhala "si_LK", // Sinhala (Sri Lanka) "sk", // Slovak "sk_SK", // Slovak (Slovakia) @@ -343,6 +348,7 @@ static const char *locale_list[] = { "sq_MK", // Albanian (Macedonia) "sr", // Serbian "sr_Cyrl", // Serbian (Cyrillic) + "sr_Latn", // Serbian (Latin) "sr_ME", // Serbian (Montenegro) "sr_RS", // Serbian (Serbia) "ss_ZA", // Swati (South Africa) @@ -357,6 +363,7 @@ static const char *locale_list[] = { "ta_IN", // Tamil (India) "ta_LK", // Tamil (Sri Lanka) "tcy_IN", // Tulu (India) + "te", // Telugu "te_IN", // Telugu (India) "tg_TJ", // Tajik (Tajikistan) "the_NP", // Chitwania Tharu (Nepal) @@ -540,6 +547,7 @@ static const char *locale_names[] = { "Fulah (Senegal)", "Finnish", "Finnish (Finland)", + "Filipino", "Filipino (Philippines)", "Faroese (Faroe Islands)", "French", @@ -588,6 +596,7 @@ static const char *locale_names[] = { "Japanese", "Japanese (Japan)", "Kabyle (Algeria)", + "Georgian", "Georgian (Georgia)", "Kazakh (Kazakhstan)", "Kalaallisut (Greenland)", @@ -618,10 +627,12 @@ static const char *locale_names[] = { "Malagasy (Madagascar)", "Marshallese (Marshall Islands)", "Eastern Mari (Russia)", - "Maori (New Zealand)", + "Māori", + "Māori (New Zealand)", "Mískito (Nicaragua)", "Macedonian", "Macedonian (Macedonia)", + "Malayalam", "Malayalam (India)", "Manipuri (India)", "Mongolian (Mongolia)", @@ -687,6 +698,7 @@ static const char *locale_names[] = { "Samogitian (Lithuania)", "Shuswap (Canada)", "Sidamo (Ethiopia)", + "Sinhala", "Sinhala (Sri Lanka)", "Slovak", "Slovak (Slovakia)", @@ -704,6 +716,7 @@ static const char *locale_names[] = { "Albanian (Macedonia)", "Serbian", "Serbian (Cyrillic)", + "Serbian (Latin)", "Serbian (Montenegro)", "Serbian (Serbia)", "Swati (South Africa)", @@ -718,6 +731,7 @@ static const char *locale_names[] = { "Tamil (India)", "Tamil (Sri Lanka)", "Tulu (India)", + "Telugu", "Telugu (India)", "Tajik (Tajikistan)", "Chitwania Tharu (Nepal)", @@ -778,11 +792,6 @@ static const char *locale_renames[][2] = { { NULL, NULL } }; -static String get_trimmed_locale(const String &p_locale) { - - return p_locale.substr(0, 2); -} - /////////////////////////////////////////////// PoolVector<String> Translation::_get_messages() const { @@ -832,10 +841,9 @@ void Translation::set_locale(const String &p_locale) { String univ_locale = TranslationServer::standardize_locale(p_locale); if (!TranslationServer::is_locale_valid(univ_locale)) { - String trimmed_locale = get_trimmed_locale(univ_locale); + String trimmed_locale = TranslationServer::get_language_code(univ_locale); - ERR_EXPLAIN("Invalid locale: " + trimmed_locale); - ERR_FAIL_COND(!TranslationServer::is_locale_valid(trimmed_locale)); + ERR_FAIL_COND_MSG(!TranslationServer::is_locale_valid(trimmed_locale), "Invalid locale: " + trimmed_locale + "."); locale = trimmed_locale; } else { @@ -932,12 +940,29 @@ String TranslationServer::standardize_locale(const String &p_locale) { return univ_locale; } +String TranslationServer::get_language_code(const String &p_locale) { + + ERR_FAIL_COND_V_MSG(p_locale.length() < 2, p_locale, "Invalid locale '" + p_locale + "'."); + // Most language codes are two letters, but some are three, + // so we have to look for a regional code separator ('_' or '-') + // to extract the left part. + // For example we get 'nah_MX' as input and should return 'nah'. + int split = p_locale.find("_"); + if (split == -1) { + split = p_locale.find("-"); + } + if (split == -1) { // No separator, so the locale is already only a language code. + return p_locale; + } + return p_locale.left(split); +} + void TranslationServer::set_locale(const String &p_locale) { String univ_locale = standardize_locale(p_locale); if (!is_locale_valid(univ_locale)) { - String trimmed_locale = get_trimmed_locale(univ_locale); + String trimmed_locale = get_language_code(univ_locale); print_verbose(vformat("Unsupported locale '%s', falling back to '%s'.", p_locale, trimmed_locale)); if (!is_locale_valid(trimmed_locale)) { @@ -968,6 +993,20 @@ String TranslationServer::get_locale_name(const String &p_locale) const { return locale_name_map[p_locale]; } +Array TranslationServer::get_loaded_locales() const { + Array locales; + for (const Set<Ref<Translation> >::Element *E = translations.front(); E; E = E->next()) { + + const Ref<Translation> &t = E->get(); + ERR_FAIL_COND_V(t.is_null(), Array()); + String l = t->get_locale(); + + locales.push_back(l); + } + + return locales; +} + Vector<String> TranslationServer::get_all_locales() { Vector<String> locales; @@ -1012,77 +1051,92 @@ void TranslationServer::clear() { StringName TranslationServer::translate(const StringName &p_message) const { - //translate using locale + // Match given message against the translation catalog for the project locale. if (!enabled) return p_message; + ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid."); + + // Locale can be of the form 'll_CC', i.e. language code and regional code, + // e.g. 'en_US', 'en_GB', etc. It might also be simply 'll', e.g. 'en'. + // To find the relevant translation, we look for those with locale starting + // with the language code, and then if any is an exact match for the long + // form. If not found, we fall back to a near match (another locale with + // same language code). + + // Note: ResourceLoader::_path_remap reproduces this locale near matching + // logic, so be sure to propagate changes there when changing things here. + StringName res; + String lang = get_language_code(locale); bool near_match = false; - const CharType *lptr = &locale[0]; for (const Set<Ref<Translation> >::Element *E = translations.front(); E; E = E->next()) { - const Ref<Translation> &t = E->get(); + ERR_FAIL_COND_V(t.is_null(), p_message); String l = t->get_locale(); - if (lptr[0] != l[0] || lptr[1] != l[1]) - continue; // locale not match - //near match - bool match = (l != locale); - - if (near_match && !match) - continue; //only near-match once + bool exact_match = (l == locale); + if (!exact_match) { + if (near_match) { + continue; // Only near-match once, but keep looking for exact matches. + } + if (get_language_code(l) != lang) { + continue; // Language code does not match. + } + } StringName r = t->get_message(p_message); - - if (!r) + if (!r) { continue; - + } res = r; - if (match) + if (exact_match) { break; - else + } else { near_match = true; + } } - if (!res) { - //try again with fallback - if (fallback.length() >= 2) { - - const CharType *fptr = &fallback[0]; - near_match = false; - for (const Set<Ref<Translation> >::Element *E = translations.front(); E; E = E->next()) { - - const Ref<Translation> &t = E->get(); - String l = t->get_locale(); - if (fptr[0] != l[0] || fptr[1] != l[1]) - continue; // locale not match - - //near match - bool match = (l != fallback); - - if (near_match && !match) - continue; //only near-match once - - StringName r = t->get_message(p_message); - - if (!r) - continue; + if (!res && fallback.length() >= 2) { + // Try again with the fallback locale. + String fallback_lang = get_language_code(fallback); + near_match = false; + + for (const Set<Ref<Translation> >::Element *E = translations.front(); E; E = E->next()) { + const Ref<Translation> &t = E->get(); + ERR_FAIL_COND_V(t.is_null(), p_message); + String l = t->get_locale(); + + bool exact_match = (l == fallback); + if (!exact_match) { + if (near_match) { + continue; // Only near-match once, but keep looking for exact matches. + } + if (get_language_code(l) != fallback_lang) { + continue; // Language code does not match. + } + } - res = r; + StringName r = t->get_message(p_message); + if (!r) { + continue; + } + res = r; - if (match) - break; - else - near_match = true; + if (exact_match) { + break; + } else { + near_match = true; } } } - if (!res) + if (!res) { return p_message; + } return res; } @@ -1168,6 +1222,8 @@ void TranslationServer::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation); ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear); + + ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales); } void TranslationServer::load_translations() { diff --git a/core/translation.h b/core/translation.h index b12bad0d72..834374bda6 100644 --- a/core/translation.h +++ b/core/translation.h @@ -94,6 +94,8 @@ public: String get_locale_name(const String &p_locale) const; + Array get_loaded_locales() const; + void add_translation(const Ref<Translation> &p_translation); void remove_translation(const Ref<Translation> &p_translation); @@ -103,6 +105,7 @@ public: static Vector<String> get_all_locale_names(); static bool is_locale_valid(const String &p_locale); static String standardize_locale(const String &p_locale); + static String get_language_code(const String &p_locale); void set_tool_translation(const Ref<Translation> &p_translation); StringName tool_translate(const StringName &p_message) const; diff --git a/core/type_info.h b/core/type_info.h index c38688fea1..61ec7b2f20 100644 --- a/core/type_info.h +++ b/core/type_info.h @@ -67,43 +67,78 @@ struct TypeInherits { !TypesAreSame<B volatile const, void volatile const>::value; }; -template <class T, typename = void> -struct GetTypeInfo { - static const Variant::Type VARIANT_TYPE = Variant::NIL; - static inline PropertyInfo get_class_info() { - ERR_PRINT("GetTypeInfo fallback. Bug!"); - return PropertyInfo(); // Not "Nil", this is an error - } +namespace GodotTypeInfo { +enum Metadata { + METADATA_NONE, + METADATA_INT_IS_INT8, + METADATA_INT_IS_INT16, + METADATA_INT_IS_INT32, + METADATA_INT_IS_INT64, + METADATA_INT_IS_UINT8, + METADATA_INT_IS_UINT16, + METADATA_INT_IS_UINT32, + METADATA_INT_IS_UINT64, + METADATA_REAL_IS_FLOAT, + METADATA_REAL_IS_DOUBLE }; +} + +// If the compiler fails because it's trying to instantiate the primary 'GetTypeInfo' template +// instead of one of the specializations, it's most likely because the type 'T' is not supported. +// If 'T' is a class that inherits 'Object', make sure it can see the actual class declaration +// instead of a forward declaration. You can always forward declare 'T' in a header file, and then +// include the actual declaration of 'T' in the source file where 'GetTypeInfo<T>' is instantiated. +template <class T, typename = void> +struct GetTypeInfo; + +#define MAKE_TYPE_INFO(m_type, m_var_type) \ + template <> \ + struct GetTypeInfo<m_type> { \ + static const Variant::Type VARIANT_TYPE = m_var_type; \ + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; \ + static inline PropertyInfo get_class_info() { \ + return PropertyInfo(VARIANT_TYPE, String()); \ + } \ + }; \ + template <> \ + struct GetTypeInfo<const m_type &> { \ + static const Variant::Type VARIANT_TYPE = m_var_type; \ + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; \ + static inline PropertyInfo get_class_info() { \ + return PropertyInfo(VARIANT_TYPE, String()); \ + } \ + }; -#define MAKE_TYPE_INFO(m_type, m_var_type) \ - template <> \ - struct GetTypeInfo<m_type> { \ - static const Variant::Type VARIANT_TYPE = m_var_type; \ - static inline PropertyInfo get_class_info() { \ - return PropertyInfo(VARIANT_TYPE, String()); \ - } \ - }; \ - template <> \ - struct GetTypeInfo<const m_type &> { \ - static const Variant::Type VARIANT_TYPE = m_var_type; \ - static inline PropertyInfo get_class_info() { \ - return PropertyInfo(VARIANT_TYPE, String()); \ - } \ +#define MAKE_TYPE_INFO_WITH_META(m_type, m_var_type, m_metadata) \ + template <> \ + struct GetTypeInfo<m_type> { \ + static const Variant::Type VARIANT_TYPE = m_var_type; \ + static const GodotTypeInfo::Metadata METADATA = m_metadata; \ + static inline PropertyInfo get_class_info() { \ + return PropertyInfo(VARIANT_TYPE, String()); \ + } \ + }; \ + template <> \ + struct GetTypeInfo<const m_type &> { \ + static const Variant::Type VARIANT_TYPE = m_var_type; \ + static const GodotTypeInfo::Metadata METADATA = m_metadata; \ + static inline PropertyInfo get_class_info() { \ + return PropertyInfo(VARIANT_TYPE, String()); \ + } \ }; MAKE_TYPE_INFO(bool, Variant::BOOL) -MAKE_TYPE_INFO(uint8_t, Variant::INT) -MAKE_TYPE_INFO(int8_t, Variant::INT) -MAKE_TYPE_INFO(uint16_t, Variant::INT) -MAKE_TYPE_INFO(int16_t, Variant::INT) -MAKE_TYPE_INFO(uint32_t, Variant::INT) -MAKE_TYPE_INFO(int32_t, Variant::INT) -MAKE_TYPE_INFO(int64_t, Variant::INT) -MAKE_TYPE_INFO(uint64_t, Variant::INT) +MAKE_TYPE_INFO_WITH_META(uint8_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_UINT8) +MAKE_TYPE_INFO_WITH_META(int8_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_INT8) +MAKE_TYPE_INFO_WITH_META(uint16_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_UINT16) +MAKE_TYPE_INFO_WITH_META(int16_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_INT16) +MAKE_TYPE_INFO_WITH_META(uint32_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_UINT32) +MAKE_TYPE_INFO_WITH_META(int32_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_INT32) +MAKE_TYPE_INFO_WITH_META(uint64_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_UINT64) +MAKE_TYPE_INFO_WITH_META(int64_t, Variant::INT, GodotTypeInfo::METADATA_INT_IS_INT64) MAKE_TYPE_INFO(wchar_t, Variant::INT) -MAKE_TYPE_INFO(float, Variant::REAL) -MAKE_TYPE_INFO(double, Variant::REAL) +MAKE_TYPE_INFO_WITH_META(float, Variant::REAL, GodotTypeInfo::METADATA_REAL_IS_FLOAT) +MAKE_TYPE_INFO_WITH_META(double, Variant::REAL, GodotTypeInfo::METADATA_REAL_IS_DOUBLE) MAKE_TYPE_INFO(String, Variant::STRING) MAKE_TYPE_INFO(Vector2, Variant::VECTOR2) @@ -138,6 +173,7 @@ MAKE_TYPE_INFO(BSP_Tree, Variant::DICTIONARY) 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"); } @@ -145,6 +181,7 @@ struct GetTypeInfo<RefPtr> { template <> struct GetTypeInfo<const 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"); } @@ -154,6 +191,7 @@ struct GetTypeInfo<const RefPtr &> { template <> struct GetTypeInfo<Variant> { static const Variant::Type VARIANT_TYPE = Variant::NIL; + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; static inline PropertyInfo get_class_info() { return PropertyInfo(Variant::NIL, String(), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT); } @@ -162,25 +200,28 @@ struct GetTypeInfo<Variant> { template <> struct GetTypeInfo<const Variant &> { static const Variant::Type VARIANT_TYPE = Variant::NIL; + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; static inline PropertyInfo get_class_info() { return PropertyInfo(Variant::NIL, String(), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT); } }; -#define MAKE_TEMPLATE_TYPE_INFO(m_template, m_type, m_var_type) \ - template <> \ - struct GetTypeInfo<m_template<m_type> > { \ - static const Variant::Type VARIANT_TYPE = m_var_type; \ - static inline PropertyInfo get_class_info() { \ - return PropertyInfo(VARIANT_TYPE, String()); \ - } \ - }; \ - template <> \ - struct GetTypeInfo<const m_template<m_type> &> { \ - static const Variant::Type VARIANT_TYPE = m_var_type; \ - static inline PropertyInfo get_class_info() { \ - return PropertyInfo(VARIANT_TYPE, String()); \ - } \ +#define MAKE_TEMPLATE_TYPE_INFO(m_template, m_type, m_var_type) \ + template <> \ + struct GetTypeInfo<m_template<m_type> > { \ + static const Variant::Type VARIANT_TYPE = m_var_type; \ + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; \ + static inline PropertyInfo get_class_info() { \ + return PropertyInfo(VARIANT_TYPE, String()); \ + } \ + }; \ + template <> \ + struct GetTypeInfo<const m_template<m_type> &> { \ + static const Variant::Type VARIANT_TYPE = m_var_type; \ + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; \ + static inline PropertyInfo get_class_info() { \ + return PropertyInfo(VARIANT_TYPE, String()); \ + } \ }; MAKE_TEMPLATE_TYPE_INFO(Vector, uint8_t, Variant::POOL_BYTE_ARRAY) @@ -202,6 +243,7 @@ MAKE_TEMPLATE_TYPE_INFO(PoolVector, Face3, Variant::POOL_VECTOR3_ARRAY) template <typename T> struct GetTypeInfo<T *, typename EnableIf<TypeInherits<Object, T>::value>::type> { static const Variant::Type VARIANT_TYPE = Variant::OBJECT; + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; static inline PropertyInfo get_class_info() { return PropertyInfo(StringName(T::get_class_static())); } @@ -210,6 +252,7 @@ struct GetTypeInfo<T *, typename EnableIf<TypeInherits<Object, T>::value>::type> template <typename T> struct GetTypeInfo<const T *, typename EnableIf<TypeInherits<Object, T>::value>::type> { static const Variant::Type VARIANT_TYPE = Variant::OBJECT; + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; static inline PropertyInfo get_class_info() { return PropertyInfo(StringName(T::get_class_static())); } @@ -219,6 +262,7 @@ struct GetTypeInfo<const T *, typename EnableIf<TypeInherits<Object, T>::value>: template <> \ struct GetTypeInfo<m_impl> { \ 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::INT, String(), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_CLASS_IS_ENUM, String(#m_enum).replace("::", ".")); \ } \ @@ -237,10 +281,7 @@ inline StringName __constant_get_enum_name(T param, const String &p_constant) { return GetTypeInfo<T>::get_class_info().class_name; } -#define CLASS_INFO(m_type) \ - (GetTypeInfo<m_type *>::VARIANT_TYPE != Variant::NIL ? \ - GetTypeInfo<m_type *>::get_class_info() : \ - GetTypeInfo<m_type>::get_class_info()) +#define CLASS_INFO(m_type) (GetTypeInfo<m_type *>::get_class_info()) #else diff --git a/core/typedefs.h b/core/typedefs.h index 660139b90a..42f34c73cb 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -108,7 +108,6 @@ T *_nullptr() { #include "core/int_types.h" #include "core/error_list.h" -#include "core/error_macros.h" /** Generic ABS function, for math uses please use Math::abs */ @@ -175,6 +174,9 @@ inline void __swap_tmpl(T &x, T &y) { static _FORCE_INLINE_ unsigned int next_power_of_2(unsigned int x) { + if (x == 0) + return 0; + --x; x |= x >> 1; x |= x >> 2; diff --git a/core/ucaps.h b/core/ucaps.h index 5dd81296dd..ae5729a53d 100644 --- a/core/ucaps.h +++ b/core/ucaps.h @@ -704,672 +704,672 @@ static const int caps_table[CAPS_LEN][2] = { }; static const int reverse_caps_table[CAPS_LEN - 1][2] = { - { 0x41, 0x61 }, - { 0x42, 0x62 }, - { 0x43, 0x63 }, - { 0x44, 0x64 }, - { 0x45, 0x65 }, - { 0x46, 0x66 }, - { 0x47, 0x67 }, - { 0x48, 0x68 }, - { 0x49, 0x69 }, - { 0x4a, 0x6a }, - { 0x4b, 0x6b }, - { 0x4c, 0x6c }, - { 0x4d, 0x6d }, - { 0x4e, 0x6e }, - { 0x4f, 0x6f }, - { 0x50, 0x70 }, - { 0x51, 0x71 }, - { 0x52, 0x72 }, - { 0x53, 0x73 }, - { 0x54, 0x74 }, - { 0x55, 0x75 }, - { 0x56, 0x76 }, - { 0x57, 0x77 }, - { 0x58, 0x78 }, - { 0x59, 0x79 }, - { 0x5a, 0x7a }, - { 0xc0, 0xe0 }, - { 0xc1, 0xe1 }, - { 0xc2, 0xe2 }, - { 0xc3, 0xe3 }, - { 0xc4, 0xe4 }, - { 0xc5, 0xe5 }, - { 0xc6, 0xe6 }, - { 0xc7, 0xe7 }, - { 0xc8, 0xe8 }, - { 0xc9, 0xe9 }, - { 0xca, 0xea }, - { 0xcb, 0xeb }, - { 0xcc, 0xec }, - { 0xcd, 0xed }, - { 0xce, 0xee }, - { 0xcf, 0xef }, - { 0xd0, 0xf0 }, - { 0xd1, 0xf1 }, - { 0xd2, 0xf2 }, - { 0xd3, 0xf3 }, - { 0xd4, 0xf4 }, - { 0xd5, 0xf5 }, - { 0xd6, 0xf6 }, - { 0xd8, 0xf8 }, - { 0xd9, 0xf9 }, - { 0xda, 0xfa }, - { 0xdb, 0xfb }, - { 0xdc, 0xfc }, - { 0xdd, 0xfd }, - { 0xde, 0xfe }, - { 0x178, 0xff }, - { 0x100, 0x101 }, - { 0x102, 0x103 }, - { 0x104, 0x105 }, - { 0x106, 0x107 }, - { 0x108, 0x109 }, - { 0x10a, 0x10b }, - { 0x10c, 0x10d }, - { 0x10e, 0x10f }, - { 0x110, 0x111 }, - { 0x112, 0x113 }, - { 0x114, 0x115 }, - { 0x116, 0x117 }, - { 0x118, 0x119 }, - { 0x11a, 0x11b }, - { 0x11c, 0x11d }, - { 0x11e, 0x11f }, - { 0x120, 0x121 }, - { 0x122, 0x123 }, - { 0x124, 0x125 }, - { 0x126, 0x127 }, - { 0x128, 0x129 }, - { 0x12a, 0x12b }, - { 0x12c, 0x12d }, - { 0x12e, 0x12f }, - //{0x49,0x131}, - { 0x132, 0x133 }, - { 0x134, 0x135 }, - { 0x136, 0x137 }, - { 0x139, 0x13a }, - { 0x13b, 0x13c }, - { 0x13d, 0x13e }, - { 0x13f, 0x140 }, - { 0x141, 0x142 }, - { 0x143, 0x144 }, - { 0x145, 0x146 }, - { 0x147, 0x148 }, - { 0x14a, 0x14b }, - { 0x14c, 0x14d }, - { 0x14e, 0x14f }, - { 0x150, 0x151 }, - { 0x152, 0x153 }, - { 0x154, 0x155 }, - { 0x156, 0x157 }, - { 0x158, 0x159 }, - { 0x15a, 0x15b }, - { 0x15c, 0x15d }, - { 0x15e, 0x15f }, - { 0x160, 0x161 }, - { 0x162, 0x163 }, - { 0x164, 0x165 }, - { 0x166, 0x167 }, - { 0x168, 0x169 }, - { 0x16a, 0x16b }, - { 0x16c, 0x16d }, - { 0x16e, 0x16f }, - { 0x170, 0x171 }, - { 0x172, 0x173 }, - { 0x174, 0x175 }, - { 0x176, 0x177 }, - { 0x179, 0x17a }, - { 0x17b, 0x17c }, - { 0x17d, 0x17e }, - { 0x182, 0x183 }, - { 0x184, 0x185 }, - { 0x187, 0x188 }, - { 0x18b, 0x18c }, - { 0x191, 0x192 }, - { 0x198, 0x199 }, - { 0x1a0, 0x1a1 }, - { 0x1a2, 0x1a3 }, - { 0x1a4, 0x1a5 }, - { 0x1a7, 0x1a8 }, - { 0x1ac, 0x1ad }, - { 0x1af, 0x1b0 }, - { 0x1b3, 0x1b4 }, - { 0x1b5, 0x1b6 }, - { 0x1b8, 0x1b9 }, - { 0x1bc, 0x1bd }, - { 0x1c4, 0x1c6 }, - { 0x1c7, 0x1c9 }, - { 0x1ca, 0x1cc }, - { 0x1cd, 0x1ce }, - { 0x1cf, 0x1d0 }, - { 0x1d1, 0x1d2 }, - { 0x1d3, 0x1d4 }, - { 0x1d5, 0x1d6 }, - { 0x1d7, 0x1d8 }, - { 0x1d9, 0x1da }, - { 0x1db, 0x1dc }, - { 0x1de, 0x1df }, - { 0x1e0, 0x1e1 }, - { 0x1e2, 0x1e3 }, - { 0x1e4, 0x1e5 }, - { 0x1e6, 0x1e7 }, - { 0x1e8, 0x1e9 }, - { 0x1ea, 0x1eb }, - { 0x1ec, 0x1ed }, - { 0x1ee, 0x1ef }, - { 0x1f1, 0x1f3 }, - { 0x1f4, 0x1f5 }, - { 0x1fa, 0x1fb }, - { 0x1fc, 0x1fd }, - { 0x1fe, 0x1ff }, - { 0x200, 0x201 }, - { 0x202, 0x203 }, - { 0x204, 0x205 }, - { 0x206, 0x207 }, - { 0x208, 0x209 }, - { 0x20a, 0x20b }, - { 0x20c, 0x20d }, - { 0x20e, 0x20f }, - { 0x210, 0x211 }, - { 0x212, 0x213 }, - { 0x214, 0x215 }, - { 0x216, 0x217 }, - { 0x181, 0x253 }, - { 0x186, 0x254 }, - { 0x18a, 0x257 }, - { 0x18e, 0x258 }, - { 0x18f, 0x259 }, - { 0x190, 0x25b }, - { 0x193, 0x260 }, - { 0x194, 0x263 }, - { 0x197, 0x268 }, - { 0x196, 0x269 }, - { 0x19c, 0x26f }, - { 0x19d, 0x272 }, - { 0x19f, 0x275 }, - { 0x1a9, 0x283 }, - { 0x1ae, 0x288 }, - { 0x1b1, 0x28a }, - { 0x1b2, 0x28b }, - { 0x1b7, 0x292 }, - { 0x386, 0x3ac }, - { 0x388, 0x3ad }, - { 0x389, 0x3ae }, - { 0x38a, 0x3af }, - { 0x391, 0x3b1 }, - { 0x392, 0x3b2 }, - { 0x393, 0x3b3 }, - { 0x394, 0x3b4 }, - { 0x395, 0x3b5 }, - { 0x396, 0x3b6 }, - { 0x397, 0x3b7 }, - { 0x398, 0x3b8 }, - { 0x399, 0x3b9 }, - { 0x39a, 0x3ba }, - { 0x39b, 0x3bb }, - { 0x39c, 0x3bc }, - { 0x39d, 0x3bd }, - { 0x39e, 0x3be }, - { 0x39f, 0x3bf }, - { 0x3a0, 0x3c0 }, - { 0x3a1, 0x3c1 }, - { 0x3a3, 0x3c3 }, - { 0x3a4, 0x3c4 }, - { 0x3a5, 0x3c5 }, - { 0x3a6, 0x3c6 }, - { 0x3a7, 0x3c7 }, - { 0x3a8, 0x3c8 }, - { 0x3a9, 0x3c9 }, - { 0x3aa, 0x3ca }, - { 0x3ab, 0x3cb }, - { 0x38c, 0x3cc }, - { 0x38e, 0x3cd }, - { 0x38f, 0x3ce }, - { 0x3e2, 0x3e3 }, - { 0x3e4, 0x3e5 }, - { 0x3e6, 0x3e7 }, - { 0x3e8, 0x3e9 }, - { 0x3ea, 0x3eb }, - { 0x3ec, 0x3ed }, - { 0x3ee, 0x3ef }, - { 0x410, 0x430 }, - { 0x411, 0x431 }, - { 0x412, 0x432 }, - { 0x413, 0x433 }, - { 0x414, 0x434 }, - { 0x415, 0x435 }, - { 0x416, 0x436 }, - { 0x417, 0x437 }, - { 0x418, 0x438 }, - { 0x419, 0x439 }, - { 0x41a, 0x43a }, - { 0x41b, 0x43b }, - { 0x41c, 0x43c }, - { 0x41d, 0x43d }, - { 0x41e, 0x43e }, - { 0x41f, 0x43f }, - { 0x420, 0x440 }, - { 0x421, 0x441 }, - { 0x422, 0x442 }, - { 0x423, 0x443 }, - { 0x424, 0x444 }, - { 0x425, 0x445 }, - { 0x426, 0x446 }, - { 0x427, 0x447 }, - { 0x428, 0x448 }, - { 0x429, 0x449 }, - { 0x42a, 0x44a }, - { 0x42b, 0x44b }, - { 0x42c, 0x44c }, - { 0x42d, 0x44d }, - { 0x42e, 0x44e }, - { 0x42f, 0x44f }, - { 0x401, 0x451 }, - { 0x402, 0x452 }, - { 0x403, 0x453 }, - { 0x404, 0x454 }, - { 0x405, 0x455 }, - { 0x406, 0x456 }, - { 0x407, 0x457 }, - { 0x408, 0x458 }, - { 0x409, 0x459 }, - { 0x40a, 0x45a }, - { 0x40b, 0x45b }, - { 0x40c, 0x45c }, - { 0x40e, 0x45e }, - { 0x40f, 0x45f }, - { 0x460, 0x461 }, - { 0x462, 0x463 }, - { 0x464, 0x465 }, - { 0x466, 0x467 }, - { 0x468, 0x469 }, - { 0x46a, 0x46b }, - { 0x46c, 0x46d }, - { 0x46e, 0x46f }, - { 0x470, 0x471 }, - { 0x472, 0x473 }, - { 0x474, 0x475 }, - { 0x476, 0x477 }, - { 0x478, 0x479 }, - { 0x47a, 0x47b }, - { 0x47c, 0x47d }, - { 0x47e, 0x47f }, - { 0x480, 0x481 }, - { 0x490, 0x491 }, - { 0x492, 0x493 }, - { 0x494, 0x495 }, - { 0x496, 0x497 }, - { 0x498, 0x499 }, - { 0x49a, 0x49b }, - { 0x49c, 0x49d }, - { 0x49e, 0x49f }, - { 0x4a0, 0x4a1 }, - { 0x4a2, 0x4a3 }, - { 0x4a4, 0x4a5 }, - { 0x4a6, 0x4a7 }, - { 0x4a8, 0x4a9 }, - { 0x4aa, 0x4ab }, - { 0x4ac, 0x4ad }, - { 0x4ae, 0x4af }, - { 0x4b0, 0x4b1 }, - { 0x4b2, 0x4b3 }, - { 0x4b4, 0x4b5 }, - { 0x4b6, 0x4b7 }, - { 0x4b8, 0x4b9 }, - { 0x4ba, 0x4bb }, - { 0x4bc, 0x4bd }, - { 0x4be, 0x4bf }, - { 0x4c1, 0x4c2 }, - { 0x4c3, 0x4c4 }, - { 0x4c7, 0x4c8 }, - { 0x4cb, 0x4cc }, - { 0x4d0, 0x4d1 }, - { 0x4d2, 0x4d3 }, - { 0x4d4, 0x4d5 }, - { 0x4d6, 0x4d7 }, - { 0x4d8, 0x4d9 }, - { 0x4da, 0x4db }, - { 0x4dc, 0x4dd }, - { 0x4de, 0x4df }, - { 0x4e0, 0x4e1 }, - { 0x4e2, 0x4e3 }, - { 0x4e4, 0x4e5 }, - { 0x4e6, 0x4e7 }, - { 0x4e8, 0x4e9 }, - { 0x4ea, 0x4eb }, - { 0x4ee, 0x4ef }, - { 0x4f0, 0x4f1 }, - { 0x4f2, 0x4f3 }, - { 0x4f4, 0x4f5 }, - { 0x4f8, 0x4f9 }, - { 0x531, 0x561 }, - { 0x532, 0x562 }, - { 0x533, 0x563 }, - { 0x534, 0x564 }, - { 0x535, 0x565 }, - { 0x536, 0x566 }, - { 0x537, 0x567 }, - { 0x538, 0x568 }, - { 0x539, 0x569 }, - { 0x53a, 0x56a }, - { 0x53b, 0x56b }, - { 0x53c, 0x56c }, - { 0x53d, 0x56d }, - { 0x53e, 0x56e }, - { 0x53f, 0x56f }, - { 0x540, 0x570 }, - { 0x541, 0x571 }, - { 0x542, 0x572 }, - { 0x543, 0x573 }, - { 0x544, 0x574 }, - { 0x545, 0x575 }, - { 0x546, 0x576 }, - { 0x547, 0x577 }, - { 0x548, 0x578 }, - { 0x549, 0x579 }, - { 0x54a, 0x57a }, - { 0x54b, 0x57b }, - { 0x54c, 0x57c }, - { 0x54d, 0x57d }, - { 0x54e, 0x57e }, - { 0x54f, 0x57f }, - { 0x550, 0x580 }, - { 0x551, 0x581 }, - { 0x552, 0x582 }, - { 0x553, 0x583 }, - { 0x554, 0x584 }, - { 0x555, 0x585 }, - { 0x556, 0x586 }, - { 0x10a0, 0x10d0 }, - { 0x10a1, 0x10d1 }, - { 0x10a2, 0x10d2 }, - { 0x10a3, 0x10d3 }, - { 0x10a4, 0x10d4 }, - { 0x10a5, 0x10d5 }, - { 0x10a6, 0x10d6 }, - { 0x10a7, 0x10d7 }, - { 0x10a8, 0x10d8 }, - { 0x10a9, 0x10d9 }, - { 0x10aa, 0x10da }, - { 0x10ab, 0x10db }, - { 0x10ac, 0x10dc }, - { 0x10ad, 0x10dd }, - { 0x10ae, 0x10de }, - { 0x10af, 0x10df }, - { 0x10b0, 0x10e0 }, - { 0x10b1, 0x10e1 }, - { 0x10b2, 0x10e2 }, - { 0x10b3, 0x10e3 }, - { 0x10b4, 0x10e4 }, - { 0x10b5, 0x10e5 }, - { 0x10b6, 0x10e6 }, - { 0x10b7, 0x10e7 }, - { 0x10b8, 0x10e8 }, - { 0x10b9, 0x10e9 }, - { 0x10ba, 0x10ea }, - { 0x10bb, 0x10eb }, - { 0x10bc, 0x10ec }, - { 0x10bd, 0x10ed }, - { 0x10be, 0x10ee }, - { 0x10bf, 0x10ef }, - { 0x10c0, 0x10f0 }, - { 0x10c1, 0x10f1 }, - { 0x10c2, 0x10f2 }, - { 0x10c3, 0x10f3 }, - { 0x10c4, 0x10f4 }, - { 0x10c5, 0x10f5 }, - { 0x1e00, 0x1e01 }, - { 0x1e02, 0x1e03 }, - { 0x1e04, 0x1e05 }, - { 0x1e06, 0x1e07 }, - { 0x1e08, 0x1e09 }, - { 0x1e0a, 0x1e0b }, - { 0x1e0c, 0x1e0d }, - { 0x1e0e, 0x1e0f }, - { 0x1e10, 0x1e11 }, - { 0x1e12, 0x1e13 }, - { 0x1e14, 0x1e15 }, - { 0x1e16, 0x1e17 }, - { 0x1e18, 0x1e19 }, - { 0x1e1a, 0x1e1b }, - { 0x1e1c, 0x1e1d }, - { 0x1e1e, 0x1e1f }, - { 0x1e20, 0x1e21 }, - { 0x1e22, 0x1e23 }, - { 0x1e24, 0x1e25 }, - { 0x1e26, 0x1e27 }, - { 0x1e28, 0x1e29 }, - { 0x1e2a, 0x1e2b }, - { 0x1e2c, 0x1e2d }, - { 0x1e2e, 0x1e2f }, - { 0x1e30, 0x1e31 }, - { 0x1e32, 0x1e33 }, - { 0x1e34, 0x1e35 }, - { 0x1e36, 0x1e37 }, - { 0x1e38, 0x1e39 }, - { 0x1e3a, 0x1e3b }, - { 0x1e3c, 0x1e3d }, - { 0x1e3e, 0x1e3f }, - { 0x1e40, 0x1e41 }, - { 0x1e42, 0x1e43 }, - { 0x1e44, 0x1e45 }, - { 0x1e46, 0x1e47 }, - { 0x1e48, 0x1e49 }, - { 0x1e4a, 0x1e4b }, - { 0x1e4c, 0x1e4d }, - { 0x1e4e, 0x1e4f }, - { 0x1e50, 0x1e51 }, - { 0x1e52, 0x1e53 }, - { 0x1e54, 0x1e55 }, - { 0x1e56, 0x1e57 }, - { 0x1e58, 0x1e59 }, - { 0x1e5a, 0x1e5b }, - { 0x1e5c, 0x1e5d }, - { 0x1e5e, 0x1e5f }, - { 0x1e60, 0x1e61 }, - { 0x1e62, 0x1e63 }, - { 0x1e64, 0x1e65 }, - { 0x1e66, 0x1e67 }, - { 0x1e68, 0x1e69 }, - { 0x1e6a, 0x1e6b }, - { 0x1e6c, 0x1e6d }, - { 0x1e6e, 0x1e6f }, - { 0x1e70, 0x1e71 }, - { 0x1e72, 0x1e73 }, - { 0x1e74, 0x1e75 }, - { 0x1e76, 0x1e77 }, - { 0x1e78, 0x1e79 }, - { 0x1e7a, 0x1e7b }, - { 0x1e7c, 0x1e7d }, - { 0x1e7e, 0x1e7f }, - { 0x1e80, 0x1e81 }, - { 0x1e82, 0x1e83 }, - { 0x1e84, 0x1e85 }, - { 0x1e86, 0x1e87 }, - { 0x1e88, 0x1e89 }, - { 0x1e8a, 0x1e8b }, - { 0x1e8c, 0x1e8d }, - { 0x1e8e, 0x1e8f }, - { 0x1e90, 0x1e91 }, - { 0x1e92, 0x1e93 }, - { 0x1e94, 0x1e95 }, - { 0x1ea0, 0x1ea1 }, - { 0x1ea2, 0x1ea3 }, - { 0x1ea4, 0x1ea5 }, - { 0x1ea6, 0x1ea7 }, - { 0x1ea8, 0x1ea9 }, - { 0x1eaa, 0x1eab }, - { 0x1eac, 0x1ead }, - { 0x1eae, 0x1eaf }, - { 0x1eb0, 0x1eb1 }, - { 0x1eb2, 0x1eb3 }, - { 0x1eb4, 0x1eb5 }, - { 0x1eb6, 0x1eb7 }, - { 0x1eb8, 0x1eb9 }, - { 0x1eba, 0x1ebb }, - { 0x1ebc, 0x1ebd }, - { 0x1ebe, 0x1ebf }, - { 0x1ec0, 0x1ec1 }, - { 0x1ec2, 0x1ec3 }, - { 0x1ec4, 0x1ec5 }, - { 0x1ec6, 0x1ec7 }, - { 0x1ec8, 0x1ec9 }, - { 0x1eca, 0x1ecb }, - { 0x1ecc, 0x1ecd }, - { 0x1ece, 0x1ecf }, - { 0x1ed0, 0x1ed1 }, - { 0x1ed2, 0x1ed3 }, - { 0x1ed4, 0x1ed5 }, - { 0x1ed6, 0x1ed7 }, - { 0x1ed8, 0x1ed9 }, - { 0x1eda, 0x1edb }, - { 0x1edc, 0x1edd }, - { 0x1ede, 0x1edf }, - { 0x1ee0, 0x1ee1 }, - { 0x1ee2, 0x1ee3 }, - { 0x1ee4, 0x1ee5 }, - { 0x1ee6, 0x1ee7 }, - { 0x1ee8, 0x1ee9 }, - { 0x1eea, 0x1eeb }, - { 0x1eec, 0x1eed }, - { 0x1eee, 0x1eef }, - { 0x1ef0, 0x1ef1 }, - { 0x1ef2, 0x1ef3 }, - { 0x1ef4, 0x1ef5 }, - { 0x1ef6, 0x1ef7 }, - { 0x1ef8, 0x1ef9 }, - { 0x1f08, 0x1f00 }, - { 0x1f09, 0x1f01 }, - { 0x1f0a, 0x1f02 }, - { 0x1f0b, 0x1f03 }, - { 0x1f0c, 0x1f04 }, - { 0x1f0d, 0x1f05 }, - { 0x1f0e, 0x1f06 }, - { 0x1f0f, 0x1f07 }, - { 0x1f18, 0x1f10 }, - { 0x1f19, 0x1f11 }, - { 0x1f1a, 0x1f12 }, - { 0x1f1b, 0x1f13 }, - { 0x1f1c, 0x1f14 }, - { 0x1f1d, 0x1f15 }, - { 0x1f28, 0x1f20 }, - { 0x1f29, 0x1f21 }, - { 0x1f2a, 0x1f22 }, - { 0x1f2b, 0x1f23 }, - { 0x1f2c, 0x1f24 }, - { 0x1f2d, 0x1f25 }, - { 0x1f2e, 0x1f26 }, - { 0x1f2f, 0x1f27 }, - { 0x1f38, 0x1f30 }, - { 0x1f39, 0x1f31 }, - { 0x1f3a, 0x1f32 }, - { 0x1f3b, 0x1f33 }, - { 0x1f3c, 0x1f34 }, - { 0x1f3d, 0x1f35 }, - { 0x1f3e, 0x1f36 }, - { 0x1f3f, 0x1f37 }, - { 0x1f48, 0x1f40 }, - { 0x1f49, 0x1f41 }, - { 0x1f4a, 0x1f42 }, - { 0x1f4b, 0x1f43 }, - { 0x1f4c, 0x1f44 }, - { 0x1f4d, 0x1f45 }, - { 0x1f59, 0x1f51 }, - { 0x1f5b, 0x1f53 }, - { 0x1f5d, 0x1f55 }, - { 0x1f5f, 0x1f57 }, - { 0x1f68, 0x1f60 }, - { 0x1f69, 0x1f61 }, - { 0x1f6a, 0x1f62 }, - { 0x1f6b, 0x1f63 }, - { 0x1f6c, 0x1f64 }, - { 0x1f6d, 0x1f65 }, - { 0x1f6e, 0x1f66 }, - { 0x1f6f, 0x1f67 }, - { 0x1f88, 0x1f80 }, - { 0x1f89, 0x1f81 }, - { 0x1f8a, 0x1f82 }, - { 0x1f8b, 0x1f83 }, - { 0x1f8c, 0x1f84 }, - { 0x1f8d, 0x1f85 }, - { 0x1f8e, 0x1f86 }, - { 0x1f8f, 0x1f87 }, - { 0x1f98, 0x1f90 }, - { 0x1f99, 0x1f91 }, - { 0x1f9a, 0x1f92 }, - { 0x1f9b, 0x1f93 }, - { 0x1f9c, 0x1f94 }, - { 0x1f9d, 0x1f95 }, - { 0x1f9e, 0x1f96 }, - { 0x1f9f, 0x1f97 }, - { 0x1fa8, 0x1fa0 }, - { 0x1fa9, 0x1fa1 }, - { 0x1faa, 0x1fa2 }, - { 0x1fab, 0x1fa3 }, - { 0x1fac, 0x1fa4 }, - { 0x1fad, 0x1fa5 }, - { 0x1fae, 0x1fa6 }, - { 0x1faf, 0x1fa7 }, - { 0x1fb8, 0x1fb0 }, - { 0x1fb9, 0x1fb1 }, - { 0x1fd8, 0x1fd0 }, - { 0x1fd9, 0x1fd1 }, - { 0x1fe8, 0x1fe0 }, - { 0x1fe9, 0x1fe1 }, - { 0x24b6, 0x24d0 }, - { 0x24b7, 0x24d1 }, - { 0x24b8, 0x24d2 }, - { 0x24b9, 0x24d3 }, - { 0x24ba, 0x24d4 }, - { 0x24bb, 0x24d5 }, - { 0x24bc, 0x24d6 }, - { 0x24bd, 0x24d7 }, - { 0x24be, 0x24d8 }, - { 0x24bf, 0x24d9 }, - { 0x24c0, 0x24da }, - { 0x24c1, 0x24db }, - { 0x24c2, 0x24dc }, - { 0x24c3, 0x24dd }, - { 0x24c4, 0x24de }, - { 0x24c5, 0x24df }, - { 0x24c6, 0x24e0 }, - { 0x24c7, 0x24e1 }, - { 0x24c8, 0x24e2 }, - { 0x24c9, 0x24e3 }, - { 0x24ca, 0x24e4 }, - { 0x24cb, 0x24e5 }, - { 0x24cc, 0x24e6 }, - { 0x24cd, 0x24e7 }, - { 0x24ce, 0x24e8 }, - { 0x24cf, 0x24e9 }, - { 0xff21, 0xff41 }, - { 0xff22, 0xff42 }, - { 0xff23, 0xff43 }, - { 0xff24, 0xff44 }, - { 0xff25, 0xff45 }, - { 0xff26, 0xff46 }, - { 0xff27, 0xff47 }, - { 0xff28, 0xff48 }, - { 0xff29, 0xff49 }, - { 0xff2a, 0xff4a }, - { 0xff2b, 0xff4b }, - { 0xff2c, 0xff4c }, - { 0xff2d, 0xff4d }, - { 0xff2e, 0xff4e }, - { 0xff2f, 0xff4f }, - { 0xff30, 0xff50 }, - { 0xff31, 0xff51 }, - { 0xff32, 0xff52 }, - { 0xff33, 0xff53 }, - { 0xff34, 0xff54 }, - { 0xff35, 0xff55 }, - { 0xff36, 0xff56 }, - { 0xff37, 0xff57 }, - { 0xff38, 0xff58 }, - { 0xff39, 0xff59 }, - { 0xff3a, 0xff5a } + { 0x0041, 0x0061 }, + { 0x0042, 0x0062 }, + { 0x0043, 0x0063 }, + { 0x0044, 0x0064 }, + { 0x0045, 0x0065 }, + { 0x0046, 0x0066 }, + { 0x0047, 0x0067 }, + { 0x0048, 0x0068 }, + { 0x0049, 0x0069 }, + // { 0x0049, 0x0131 }, // dotless I + { 0x004A, 0x006A }, + { 0x004B, 0x006B }, + { 0x004C, 0x006C }, + { 0x004D, 0x006D }, + { 0x004E, 0x006E }, + { 0x004F, 0x006F }, + { 0x0050, 0x0070 }, + { 0x0051, 0x0071 }, + { 0x0052, 0x0072 }, + { 0x0053, 0x0073 }, + { 0x0054, 0x0074 }, + { 0x0055, 0x0075 }, + { 0x0056, 0x0076 }, + { 0x0057, 0x0077 }, + { 0x0058, 0x0078 }, + { 0x0059, 0x0079 }, + { 0x005A, 0x007A }, + { 0x00C0, 0x00E0 }, + { 0x00C1, 0x00E1 }, + { 0x00C2, 0x00E2 }, + { 0x00C3, 0x00E3 }, + { 0x00C4, 0x00E4 }, + { 0x00C5, 0x00E5 }, + { 0x00C6, 0x00E6 }, + { 0x00C7, 0x00E7 }, + { 0x00C8, 0x00E8 }, + { 0x00C9, 0x00E9 }, + { 0x00CA, 0x00EA }, + { 0x00CB, 0x00EB }, + { 0x00CC, 0x00EC }, + { 0x00CD, 0x00ED }, + { 0x00CE, 0x00EE }, + { 0x00CF, 0x00EF }, + { 0x00D0, 0x00F0 }, + { 0x00D1, 0x00F1 }, + { 0x00D2, 0x00F2 }, + { 0x00D3, 0x00F3 }, + { 0x00D4, 0x00F4 }, + { 0x00D5, 0x00F5 }, + { 0x00D6, 0x00F6 }, + { 0x00D8, 0x00F8 }, + { 0x00D9, 0x00F9 }, + { 0x00DA, 0x00FA }, + { 0x00DB, 0x00FB }, + { 0x00DC, 0x00FC }, + { 0x00DD, 0x00FD }, + { 0x00DE, 0x00FE }, + { 0x0100, 0x0101 }, + { 0x0102, 0x0103 }, + { 0x0104, 0x0105 }, + { 0x0106, 0x0107 }, + { 0x0108, 0x0109 }, + { 0x010A, 0x010B }, + { 0x010C, 0x010D }, + { 0x010E, 0x010F }, + { 0x0110, 0x0111 }, + { 0x0112, 0x0113 }, + { 0x0114, 0x0115 }, + { 0x0116, 0x0117 }, + { 0x0118, 0x0119 }, + { 0x011A, 0x011B }, + { 0x011C, 0x011D }, + { 0x011E, 0x011F }, + { 0x0120, 0x0121 }, + { 0x0122, 0x0123 }, + { 0x0124, 0x0125 }, + { 0x0126, 0x0127 }, + { 0x0128, 0x0129 }, + { 0x012A, 0x012B }, + { 0x012C, 0x012D }, + { 0x012E, 0x012F }, + { 0x0132, 0x0133 }, + { 0x0134, 0x0135 }, + { 0x0136, 0x0137 }, + { 0x0139, 0x013A }, + { 0x013B, 0x013C }, + { 0x013D, 0x013E }, + { 0x013F, 0x0140 }, + { 0x0141, 0x0142 }, + { 0x0143, 0x0144 }, + { 0x0145, 0x0146 }, + { 0x0147, 0x0148 }, + { 0x014A, 0x014B }, + { 0x014C, 0x014D }, + { 0x014E, 0x014F }, + { 0x0150, 0x0151 }, + { 0x0152, 0x0153 }, + { 0x0154, 0x0155 }, + { 0x0156, 0x0157 }, + { 0x0158, 0x0159 }, + { 0x015A, 0x015B }, + { 0x015C, 0x015D }, + { 0x015E, 0x015F }, + { 0x0160, 0x0161 }, + { 0x0162, 0x0163 }, + { 0x0164, 0x0165 }, + { 0x0166, 0x0167 }, + { 0x0168, 0x0169 }, + { 0x016A, 0x016B }, + { 0x016C, 0x016D }, + { 0x016E, 0x016F }, + { 0x0170, 0x0171 }, + { 0x0172, 0x0173 }, + { 0x0174, 0x0175 }, + { 0x0176, 0x0177 }, + { 0x0178, 0x00FF }, + { 0x0179, 0x017A }, + { 0x017B, 0x017C }, + { 0x017D, 0x017E }, + { 0x0181, 0x0253 }, + { 0x0182, 0x0183 }, + { 0x0184, 0x0185 }, + { 0x0186, 0x0254 }, + { 0x0187, 0x0188 }, + { 0x018A, 0x0257 }, + { 0x018B, 0x018C }, + { 0x018E, 0x0258 }, + { 0x018F, 0x0259 }, + { 0x0190, 0x025B }, + { 0x0191, 0x0192 }, + { 0x0193, 0x0260 }, + { 0x0194, 0x0263 }, + { 0x0196, 0x0269 }, + { 0x0197, 0x0268 }, + { 0x0198, 0x0199 }, + { 0x019C, 0x026F }, + { 0x019D, 0x0272 }, + { 0x019F, 0x0275 }, + { 0x01A0, 0x01A1 }, + { 0x01A2, 0x01A3 }, + { 0x01A4, 0x01A5 }, + { 0x01A7, 0x01A8 }, + { 0x01A9, 0x0283 }, + { 0x01AC, 0x01AD }, + { 0x01AE, 0x0288 }, + { 0x01AF, 0x01B0 }, + { 0x01B1, 0x028A }, + { 0x01B2, 0x028B }, + { 0x01B3, 0x01B4 }, + { 0x01B5, 0x01B6 }, + { 0x01B7, 0x0292 }, + { 0x01B8, 0x01B9 }, + { 0x01BC, 0x01BD }, + { 0x01C4, 0x01C6 }, + { 0x01C7, 0x01C9 }, + { 0x01CA, 0x01CC }, + { 0x01CD, 0x01CE }, + { 0x01CF, 0x01D0 }, + { 0x01D1, 0x01D2 }, + { 0x01D3, 0x01D4 }, + { 0x01D5, 0x01D6 }, + { 0x01D7, 0x01D8 }, + { 0x01D9, 0x01DA }, + { 0x01DB, 0x01DC }, + { 0x01DE, 0x01DF }, + { 0x01E0, 0x01E1 }, + { 0x01E2, 0x01E3 }, + { 0x01E4, 0x01E5 }, + { 0x01E6, 0x01E7 }, + { 0x01E8, 0x01E9 }, + { 0x01EA, 0x01EB }, + { 0x01EC, 0x01ED }, + { 0x01EE, 0x01EF }, + { 0x01F1, 0x01F3 }, + { 0x01F4, 0x01F5 }, + { 0x01FA, 0x01FB }, + { 0x01FC, 0x01FD }, + { 0x01FE, 0x01FF }, + { 0x0200, 0x0201 }, + { 0x0202, 0x0203 }, + { 0x0204, 0x0205 }, + { 0x0206, 0x0207 }, + { 0x0208, 0x0209 }, + { 0x020A, 0x020B }, + { 0x020C, 0x020D }, + { 0x020E, 0x020F }, + { 0x0210, 0x0211 }, + { 0x0212, 0x0213 }, + { 0x0214, 0x0215 }, + { 0x0216, 0x0217 }, + { 0x0386, 0x03AC }, + { 0x0388, 0x03AD }, + { 0x0389, 0x03AE }, + { 0x038A, 0x03AF }, + { 0x038C, 0x03CC }, + { 0x038E, 0x03CD }, + { 0x038F, 0x03CE }, + { 0x0391, 0x03B1 }, + { 0x0392, 0x03B2 }, + { 0x0393, 0x03B3 }, + { 0x0394, 0x03B4 }, + { 0x0395, 0x03B5 }, + { 0x0396, 0x03B6 }, + { 0x0397, 0x03B7 }, + { 0x0398, 0x03B8 }, + { 0x0399, 0x03B9 }, + { 0x039A, 0x03BA }, + { 0x039B, 0x03BB }, + { 0x039C, 0x03BC }, + { 0x039D, 0x03BD }, + { 0x039E, 0x03BE }, + { 0x039F, 0x03BF }, + { 0x03A0, 0x03C0 }, + { 0x03A1, 0x03C1 }, + { 0x03A3, 0x03C3 }, + { 0x03A4, 0x03C4 }, + { 0x03A5, 0x03C5 }, + { 0x03A6, 0x03C6 }, + { 0x03A7, 0x03C7 }, + { 0x03A8, 0x03C8 }, + { 0x03A9, 0x03C9 }, + { 0x03AA, 0x03CA }, + { 0x03AB, 0x03CB }, + { 0x03E2, 0x03E3 }, + { 0x03E4, 0x03E5 }, + { 0x03E6, 0x03E7 }, + { 0x03E8, 0x03E9 }, + { 0x03EA, 0x03EB }, + { 0x03EC, 0x03ED }, + { 0x03EE, 0x03EF }, + { 0x0401, 0x0451 }, + { 0x0402, 0x0452 }, + { 0x0403, 0x0453 }, + { 0x0404, 0x0454 }, + { 0x0405, 0x0455 }, + { 0x0406, 0x0456 }, + { 0x0407, 0x0457 }, + { 0x0408, 0x0458 }, + { 0x0409, 0x0459 }, + { 0x040A, 0x045A }, + { 0x040B, 0x045B }, + { 0x040C, 0x045C }, + { 0x040E, 0x045E }, + { 0x040F, 0x045F }, + { 0x0410, 0x0430 }, + { 0x0411, 0x0431 }, + { 0x0412, 0x0432 }, + { 0x0413, 0x0433 }, + { 0x0414, 0x0434 }, + { 0x0415, 0x0435 }, + { 0x0416, 0x0436 }, + { 0x0417, 0x0437 }, + { 0x0418, 0x0438 }, + { 0x0419, 0x0439 }, + { 0x041A, 0x043A }, + { 0x041B, 0x043B }, + { 0x041C, 0x043C }, + { 0x041D, 0x043D }, + { 0x041E, 0x043E }, + { 0x041F, 0x043F }, + { 0x0420, 0x0440 }, + { 0x0421, 0x0441 }, + { 0x0422, 0x0442 }, + { 0x0423, 0x0443 }, + { 0x0424, 0x0444 }, + { 0x0425, 0x0445 }, + { 0x0426, 0x0446 }, + { 0x0427, 0x0447 }, + { 0x0428, 0x0448 }, + { 0x0429, 0x0449 }, + { 0x042A, 0x044A }, + { 0x042B, 0x044B }, + { 0x042C, 0x044C }, + { 0x042D, 0x044D }, + { 0x042E, 0x044E }, + { 0x042F, 0x044F }, + { 0x0460, 0x0461 }, + { 0x0462, 0x0463 }, + { 0x0464, 0x0465 }, + { 0x0466, 0x0467 }, + { 0x0468, 0x0469 }, + { 0x046A, 0x046B }, + { 0x046C, 0x046D }, + { 0x046E, 0x046F }, + { 0x0470, 0x0471 }, + { 0x0472, 0x0473 }, + { 0x0474, 0x0475 }, + { 0x0476, 0x0477 }, + { 0x0478, 0x0479 }, + { 0x047A, 0x047B }, + { 0x047C, 0x047D }, + { 0x047E, 0x047F }, + { 0x0480, 0x0481 }, + { 0x0490, 0x0491 }, + { 0x0492, 0x0493 }, + { 0x0494, 0x0495 }, + { 0x0496, 0x0497 }, + { 0x0498, 0x0499 }, + { 0x049A, 0x049B }, + { 0x049C, 0x049D }, + { 0x049E, 0x049F }, + { 0x04A0, 0x04A1 }, + { 0x04A2, 0x04A3 }, + { 0x04A4, 0x04A5 }, + { 0x04A6, 0x04A7 }, + { 0x04A8, 0x04A9 }, + { 0x04AA, 0x04AB }, + { 0x04AC, 0x04AD }, + { 0x04AE, 0x04AF }, + { 0x04B0, 0x04B1 }, + { 0x04B2, 0x04B3 }, + { 0x04B4, 0x04B5 }, + { 0x04B6, 0x04B7 }, + { 0x04B8, 0x04B9 }, + { 0x04BA, 0x04BB }, + { 0x04BC, 0x04BD }, + { 0x04BE, 0x04BF }, + { 0x04C1, 0x04C2 }, + { 0x04C3, 0x04C4 }, + { 0x04C7, 0x04C8 }, + { 0x04CB, 0x04CC }, + { 0x04D0, 0x04D1 }, + { 0x04D2, 0x04D3 }, + { 0x04D4, 0x04D5 }, + { 0x04D6, 0x04D7 }, + { 0x04D8, 0x04D9 }, + { 0x04DA, 0x04DB }, + { 0x04DC, 0x04DD }, + { 0x04DE, 0x04DF }, + { 0x04E0, 0x04E1 }, + { 0x04E2, 0x04E3 }, + { 0x04E4, 0x04E5 }, + { 0x04E6, 0x04E7 }, + { 0x04E8, 0x04E9 }, + { 0x04EA, 0x04EB }, + { 0x04EE, 0x04EF }, + { 0x04F0, 0x04F1 }, + { 0x04F2, 0x04F3 }, + { 0x04F4, 0x04F5 }, + { 0x04F8, 0x04F9 }, + { 0x0531, 0x0561 }, + { 0x0532, 0x0562 }, + { 0x0533, 0x0563 }, + { 0x0534, 0x0564 }, + { 0x0535, 0x0565 }, + { 0x0536, 0x0566 }, + { 0x0537, 0x0567 }, + { 0x0538, 0x0568 }, + { 0x0539, 0x0569 }, + { 0x053A, 0x056A }, + { 0x053B, 0x056B }, + { 0x053C, 0x056C }, + { 0x053D, 0x056D }, + { 0x053E, 0x056E }, + { 0x053F, 0x056F }, + { 0x0540, 0x0570 }, + { 0x0541, 0x0571 }, + { 0x0542, 0x0572 }, + { 0x0543, 0x0573 }, + { 0x0544, 0x0574 }, + { 0x0545, 0x0575 }, + { 0x0546, 0x0576 }, + { 0x0547, 0x0577 }, + { 0x0548, 0x0578 }, + { 0x0549, 0x0579 }, + { 0x054A, 0x057A }, + { 0x054B, 0x057B }, + { 0x054C, 0x057C }, + { 0x054D, 0x057D }, + { 0x054E, 0x057E }, + { 0x054F, 0x057F }, + { 0x0550, 0x0580 }, + { 0x0551, 0x0581 }, + { 0x0552, 0x0582 }, + { 0x0553, 0x0583 }, + { 0x0554, 0x0584 }, + { 0x0555, 0x0585 }, + { 0x0556, 0x0586 }, + { 0x10A0, 0x10D0 }, + { 0x10A1, 0x10D1 }, + { 0x10A2, 0x10D2 }, + { 0x10A3, 0x10D3 }, + { 0x10A4, 0x10D4 }, + { 0x10A5, 0x10D5 }, + { 0x10A6, 0x10D6 }, + { 0x10A7, 0x10D7 }, + { 0x10A8, 0x10D8 }, + { 0x10A9, 0x10D9 }, + { 0x10AA, 0x10DA }, + { 0x10AB, 0x10DB }, + { 0x10AC, 0x10DC }, + { 0x10AD, 0x10DD }, + { 0x10AE, 0x10DE }, + { 0x10AF, 0x10DF }, + { 0x10B0, 0x10E0 }, + { 0x10B1, 0x10E1 }, + { 0x10B2, 0x10E2 }, + { 0x10B3, 0x10E3 }, + { 0x10B4, 0x10E4 }, + { 0x10B5, 0x10E5 }, + { 0x10B6, 0x10E6 }, + { 0x10B7, 0x10E7 }, + { 0x10B8, 0x10E8 }, + { 0x10B9, 0x10E9 }, + { 0x10BA, 0x10EA }, + { 0x10BB, 0x10EB }, + { 0x10BC, 0x10EC }, + { 0x10BD, 0x10ED }, + { 0x10BE, 0x10EE }, + { 0x10BF, 0x10EF }, + { 0x10C0, 0x10F0 }, + { 0x10C1, 0x10F1 }, + { 0x10C2, 0x10F2 }, + { 0x10C3, 0x10F3 }, + { 0x10C4, 0x10F4 }, + { 0x10C5, 0x10F5 }, + { 0x1E00, 0x1E01 }, + { 0x1E02, 0x1E03 }, + { 0x1E04, 0x1E05 }, + { 0x1E06, 0x1E07 }, + { 0x1E08, 0x1E09 }, + { 0x1E0A, 0x1E0B }, + { 0x1E0C, 0x1E0D }, + { 0x1E0E, 0x1E0F }, + { 0x1E10, 0x1E11 }, + { 0x1E12, 0x1E13 }, + { 0x1E14, 0x1E15 }, + { 0x1E16, 0x1E17 }, + { 0x1E18, 0x1E19 }, + { 0x1E1A, 0x1E1B }, + { 0x1E1C, 0x1E1D }, + { 0x1E1E, 0x1E1F }, + { 0x1E20, 0x1E21 }, + { 0x1E22, 0x1E23 }, + { 0x1E24, 0x1E25 }, + { 0x1E26, 0x1E27 }, + { 0x1E28, 0x1E29 }, + { 0x1E2A, 0x1E2B }, + { 0x1E2C, 0x1E2D }, + { 0x1E2E, 0x1E2F }, + { 0x1E30, 0x1E31 }, + { 0x1E32, 0x1E33 }, + { 0x1E34, 0x1E35 }, + { 0x1E36, 0x1E37 }, + { 0x1E38, 0x1E39 }, + { 0x1E3A, 0x1E3B }, + { 0x1E3C, 0x1E3D }, + { 0x1E3E, 0x1E3F }, + { 0x1E40, 0x1E41 }, + { 0x1E42, 0x1E43 }, + { 0x1E44, 0x1E45 }, + { 0x1E46, 0x1E47 }, + { 0x1E48, 0x1E49 }, + { 0x1E4A, 0x1E4B }, + { 0x1E4C, 0x1E4D }, + { 0x1E4E, 0x1E4F }, + { 0x1E50, 0x1E51 }, + { 0x1E52, 0x1E53 }, + { 0x1E54, 0x1E55 }, + { 0x1E56, 0x1E57 }, + { 0x1E58, 0x1E59 }, + { 0x1E5A, 0x1E5B }, + { 0x1E5C, 0x1E5D }, + { 0x1E5E, 0x1E5F }, + { 0x1E60, 0x1E61 }, + { 0x1E62, 0x1E63 }, + { 0x1E64, 0x1E65 }, + { 0x1E66, 0x1E67 }, + { 0x1E68, 0x1E69 }, + { 0x1E6A, 0x1E6B }, + { 0x1E6C, 0x1E6D }, + { 0x1E6E, 0x1E6F }, + { 0x1E70, 0x1E71 }, + { 0x1E72, 0x1E73 }, + { 0x1E74, 0x1E75 }, + { 0x1E76, 0x1E77 }, + { 0x1E78, 0x1E79 }, + { 0x1E7A, 0x1E7B }, + { 0x1E7C, 0x1E7D }, + { 0x1E7E, 0x1E7F }, + { 0x1E80, 0x1E81 }, + { 0x1E82, 0x1E83 }, + { 0x1E84, 0x1E85 }, + { 0x1E86, 0x1E87 }, + { 0x1E88, 0x1E89 }, + { 0x1E8A, 0x1E8B }, + { 0x1E8C, 0x1E8D }, + { 0x1E8E, 0x1E8F }, + { 0x1E90, 0x1E91 }, + { 0x1E92, 0x1E93 }, + { 0x1E94, 0x1E95 }, + { 0x1EA0, 0x1EA1 }, + { 0x1EA2, 0x1EA3 }, + { 0x1EA4, 0x1EA5 }, + { 0x1EA6, 0x1EA7 }, + { 0x1EA8, 0x1EA9 }, + { 0x1EAA, 0x1EAB }, + { 0x1EAC, 0x1EAD }, + { 0x1EAE, 0x1EAF }, + { 0x1EB0, 0x1EB1 }, + { 0x1EB2, 0x1EB3 }, + { 0x1EB4, 0x1EB5 }, + { 0x1EB6, 0x1EB7 }, + { 0x1EB8, 0x1EB9 }, + { 0x1EBA, 0x1EBB }, + { 0x1EBC, 0x1EBD }, + { 0x1EBE, 0x1EBF }, + { 0x1EC0, 0x1EC1 }, + { 0x1EC2, 0x1EC3 }, + { 0x1EC4, 0x1EC5 }, + { 0x1EC6, 0x1EC7 }, + { 0x1EC8, 0x1EC9 }, + { 0x1ECA, 0x1ECB }, + { 0x1ECC, 0x1ECD }, + { 0x1ECE, 0x1ECF }, + { 0x1ED0, 0x1ED1 }, + { 0x1ED2, 0x1ED3 }, + { 0x1ED4, 0x1ED5 }, + { 0x1ED6, 0x1ED7 }, + { 0x1ED8, 0x1ED9 }, + { 0x1EDA, 0x1EDB }, + { 0x1EDC, 0x1EDD }, + { 0x1EDE, 0x1EDF }, + { 0x1EE0, 0x1EE1 }, + { 0x1EE2, 0x1EE3 }, + { 0x1EE4, 0x1EE5 }, + { 0x1EE6, 0x1EE7 }, + { 0x1EE8, 0x1EE9 }, + { 0x1EEA, 0x1EEB }, + { 0x1EEC, 0x1EED }, + { 0x1EEE, 0x1EEF }, + { 0x1EF0, 0x1EF1 }, + { 0x1EF2, 0x1EF3 }, + { 0x1EF4, 0x1EF5 }, + { 0x1EF6, 0x1EF7 }, + { 0x1EF8, 0x1EF9 }, + { 0x1F08, 0x1F00 }, + { 0x1F09, 0x1F01 }, + { 0x1F0A, 0x1F02 }, + { 0x1F0B, 0x1F03 }, + { 0x1F0C, 0x1F04 }, + { 0x1F0D, 0x1F05 }, + { 0x1F0E, 0x1F06 }, + { 0x1F0F, 0x1F07 }, + { 0x1F18, 0x1F10 }, + { 0x1F19, 0x1F11 }, + { 0x1F1A, 0x1F12 }, + { 0x1F1B, 0x1F13 }, + { 0x1F1C, 0x1F14 }, + { 0x1F1D, 0x1F15 }, + { 0x1F28, 0x1F20 }, + { 0x1F29, 0x1F21 }, + { 0x1F2A, 0x1F22 }, + { 0x1F2B, 0x1F23 }, + { 0x1F2C, 0x1F24 }, + { 0x1F2D, 0x1F25 }, + { 0x1F2E, 0x1F26 }, + { 0x1F2F, 0x1F27 }, + { 0x1F38, 0x1F30 }, + { 0x1F39, 0x1F31 }, + { 0x1F3A, 0x1F32 }, + { 0x1F3B, 0x1F33 }, + { 0x1F3C, 0x1F34 }, + { 0x1F3D, 0x1F35 }, + { 0x1F3E, 0x1F36 }, + { 0x1F3F, 0x1F37 }, + { 0x1F48, 0x1F40 }, + { 0x1F49, 0x1F41 }, + { 0x1F4A, 0x1F42 }, + { 0x1F4B, 0x1F43 }, + { 0x1F4C, 0x1F44 }, + { 0x1F4D, 0x1F45 }, + { 0x1F59, 0x1F51 }, + { 0x1F5B, 0x1F53 }, + { 0x1F5D, 0x1F55 }, + { 0x1F5F, 0x1F57 }, + { 0x1F68, 0x1F60 }, + { 0x1F69, 0x1F61 }, + { 0x1F6A, 0x1F62 }, + { 0x1F6B, 0x1F63 }, + { 0x1F6C, 0x1F64 }, + { 0x1F6D, 0x1F65 }, + { 0x1F6E, 0x1F66 }, + { 0x1F6F, 0x1F67 }, + { 0x1F88, 0x1F80 }, + { 0x1F89, 0x1F81 }, + { 0x1F8A, 0x1F82 }, + { 0x1F8B, 0x1F83 }, + { 0x1F8C, 0x1F84 }, + { 0x1F8D, 0x1F85 }, + { 0x1F8E, 0x1F86 }, + { 0x1F8F, 0x1F87 }, + { 0x1F98, 0x1F90 }, + { 0x1F99, 0x1F91 }, + { 0x1F9A, 0x1F92 }, + { 0x1F9B, 0x1F93 }, + { 0x1F9C, 0x1F94 }, + { 0x1F9D, 0x1F95 }, + { 0x1F9E, 0x1F96 }, + { 0x1F9F, 0x1F97 }, + { 0x1FA8, 0x1FA0 }, + { 0x1FA9, 0x1FA1 }, + { 0x1FAA, 0x1FA2 }, + { 0x1FAB, 0x1FA3 }, + { 0x1FAC, 0x1FA4 }, + { 0x1FAD, 0x1FA5 }, + { 0x1FAE, 0x1FA6 }, + { 0x1FAF, 0x1FA7 }, + { 0x1FB8, 0x1FB0 }, + { 0x1FB9, 0x1FB1 }, + { 0x1FD8, 0x1FD0 }, + { 0x1FD9, 0x1FD1 }, + { 0x1FE8, 0x1FE0 }, + { 0x1FE9, 0x1FE1 }, + { 0x24B6, 0x24D0 }, + { 0x24B7, 0x24D1 }, + { 0x24B8, 0x24D2 }, + { 0x24B9, 0x24D3 }, + { 0x24BA, 0x24D4 }, + { 0x24BB, 0x24D5 }, + { 0x24BC, 0x24D6 }, + { 0x24BD, 0x24D7 }, + { 0x24BE, 0x24D8 }, + { 0x24BF, 0x24D9 }, + { 0x24C0, 0x24DA }, + { 0x24C1, 0x24DB }, + { 0x24C2, 0x24DC }, + { 0x24C3, 0x24DD }, + { 0x24C4, 0x24DE }, + { 0x24C5, 0x24DF }, + { 0x24C6, 0x24E0 }, + { 0x24C7, 0x24E1 }, + { 0x24C8, 0x24E2 }, + { 0x24C9, 0x24E3 }, + { 0x24CA, 0x24E4 }, + { 0x24CB, 0x24E5 }, + { 0x24CC, 0x24E6 }, + { 0x24CD, 0x24E7 }, + { 0x24CE, 0x24E8 }, + { 0x24CF, 0x24E9 }, + { 0xFF21, 0xFF41 }, + { 0xFF22, 0xFF42 }, + { 0xFF23, 0xFF43 }, + { 0xFF24, 0xFF44 }, + { 0xFF25, 0xFF45 }, + { 0xFF26, 0xFF46 }, + { 0xFF27, 0xFF47 }, + { 0xFF28, 0xFF48 }, + { 0xFF29, 0xFF49 }, + { 0xFF2A, 0xFF4A }, + { 0xFF2B, 0xFF4B }, + { 0xFF2C, 0xFF4C }, + { 0xFF2D, 0xFF4D }, + { 0xFF2E, 0xFF4E }, + { 0xFF2F, 0xFF4F }, + { 0xFF30, 0xFF50 }, + { 0xFF31, 0xFF51 }, + { 0xFF32, 0xFF52 }, + { 0xFF33, 0xFF53 }, + { 0xFF34, 0xFF54 }, + { 0xFF35, 0xFF55 }, + { 0xFF36, 0xFF56 }, + { 0xFF37, 0xFF57 }, + { 0xFF38, 0xFF58 }, + { 0xFF39, 0xFF59 }, + { 0xFF3A, 0xFF5A }, }; static int _find_upper(int ch) { diff --git a/core/undo_redo.cpp b/core/undo_redo.cpp index 69581e4115..f0c2b8eb9b 100644 --- a/core/undo_redo.cpp +++ b/core/undo_redo.cpp @@ -239,8 +239,8 @@ void UndoRedo::_pop_history_tail() { } } -bool UndoRedo::is_commiting_action() const { - return commiting > 0; +bool UndoRedo::is_committing_action() const { + return committing > 0; } void UndoRedo::commit_action() { @@ -255,9 +255,9 @@ void UndoRedo::commit_action() { merging = false; } - commiting++; + committing++; redo(); // perform action - commiting--; + committing--; if (callback && actions.size() > 0) { callback(callback_ud, actions[actions.size() - 1].name); } @@ -336,6 +336,7 @@ bool UndoRedo::redo() { _process_operation_list(actions.write[current_action].do_ops.front()); version++; + emit_signal("version_changed"); return true; } @@ -348,6 +349,8 @@ bool UndoRedo::undo() { _process_operation_list(actions.write[current_action].undo_ops.front()); current_action--; version--; + emit_signal("version_changed"); + return true; } @@ -359,18 +362,30 @@ void UndoRedo::clear_history(bool p_increase_version) { while (actions.size()) _pop_history_tail(); - if (p_increase_version) + if (p_increase_version) { version++; + emit_signal("version_changed"); + } } String UndoRedo::get_current_action_name() const { ERR_FAIL_COND_V(action_level > 0, ""); if (current_action < 0) - return ""; //nothing to redo + return ""; return actions[current_action].name; } +bool UndoRedo::has_undo() { + + return current_action >= 0; +} + +bool UndoRedo::has_redo() { + + return (current_action + 1) < actions.size(); +} + uint64_t UndoRedo::get_version() const { return version; @@ -396,7 +411,7 @@ void UndoRedo::set_property_notify_callback(PropertyNotifyCallback p_property_ca UndoRedo::UndoRedo() { - commiting = 0; + committing = 0; version = 1; action_level = 0; current_action = -1; @@ -496,10 +511,8 @@ void UndoRedo::_bind_methods() { ClassDB::bind_method(D_METHOD("create_action", "name", "merge_mode"), &UndoRedo::create_action, DEFVAL(MERGE_DISABLE)); ClassDB::bind_method(D_METHOD("commit_action"), &UndoRedo::commit_action); - ClassDB::bind_method(D_METHOD("is_commiting_action"), &UndoRedo::is_commiting_action); - - //ClassDB::bind_method(D_METHOD("add_do_method","p_object", "p_method", "VARIANT_ARG_LIST"),&UndoRedo::add_do_method); - //ClassDB::bind_method(D_METHOD("add_undo_method","p_object", "p_method", "VARIANT_ARG_LIST"),&UndoRedo::add_undo_method); + // FIXME: Typo in "commiting", fix in 4.0 when breaking compat. + ClassDB::bind_method(D_METHOD("is_commiting_action"), &UndoRedo::is_committing_action); { MethodInfo mi; @@ -525,10 +538,14 @@ void UndoRedo::_bind_methods() { ClassDB::bind_method(D_METHOD("add_undo_reference", "object"), &UndoRedo::add_undo_reference); ClassDB::bind_method(D_METHOD("clear_history", "increase_version"), &UndoRedo::clear_history, DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_current_action_name"), &UndoRedo::get_current_action_name); + ClassDB::bind_method(D_METHOD("has_undo"), &UndoRedo::has_undo); + ClassDB::bind_method(D_METHOD("has_redo"), &UndoRedo::has_redo); ClassDB::bind_method(D_METHOD("get_version"), &UndoRedo::get_version); ClassDB::bind_method(D_METHOD("redo"), &UndoRedo::redo); ClassDB::bind_method(D_METHOD("undo"), &UndoRedo::undo); + ADD_SIGNAL(MethodInfo("version_changed")); + BIND_ENUM_CONSTANT(MERGE_DISABLE); BIND_ENUM_CONSTANT(MERGE_ENDS); BIND_ENUM_CONSTANT(MERGE_ALL); diff --git a/core/undo_redo.h b/core/undo_redo.h index 6293e77acc..276d00d9af 100644 --- a/core/undo_redo.h +++ b/core/undo_redo.h @@ -95,7 +95,7 @@ private: MethodNotifyCallback method_callback; PropertyNotifyCallback property_callback; - int commiting; + int committing; protected: static void _bind_methods(); @@ -110,7 +110,7 @@ public: void add_do_reference(Object *p_object); void add_undo_reference(Object *p_object); - bool is_commiting_action() const; + bool is_committing_action() const; void commit_action(); bool redo(); @@ -118,6 +118,9 @@ public: String get_current_action_name() const; void clear_history(bool p_increase_version = true); + bool has_undo(); + bool has_redo(); + uint64_t get_version() const; void set_commit_notify_callback(CommitNotifyCallback p_callback, void *p_ud); diff --git a/core/ustring.cpp b/core/ustring.cpp index ff8fcaaaaf..6a1dc2295f 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -31,6 +31,7 @@ #include "ustring.h" #include "core/color.h" +#include "core/crypto/crypto_core.h" #include "core/math/math_funcs.h" #include "core/os/memory.h" #include "core/print_string.h" @@ -38,10 +39,8 @@ #include "core/ucaps.h" #include "core/variant.h" -#include "thirdparty/misc/md5.h" -#include "thirdparty/misc/sha256.h" - #include <wchar.h> +#include <cstdint> #ifndef NO_USE_STDLIB #include <stdio.h> @@ -123,6 +122,31 @@ const char *CharString::get_data() const { return ""; } +CharString &CharString::operator=(const char *p_cstr) { + + copy_from(p_cstr); + return *this; +} + +void CharString::copy_from(const char *p_cstr) { + + if (!p_cstr) { + resize(0); + return; + } + + size_t len = strlen(p_cstr); + + if (len == 0) { + resize(0); + return; + } + + resize(len + 1); // include terminating null char + + strcpy(ptrw(), p_cstr); +} + void String::copy_from(const char *p_cstr) { if (!p_cstr) { @@ -456,8 +480,6 @@ signed char String::nocasecmp_to(const String &p_str) const { this_str++; that_str++; } - - return 0; //should never reach anyway } signed char String::casecmp_to(const String &p_str) const { @@ -488,8 +510,6 @@ signed char String::casecmp_to(const String &p_str) const { this_str++; that_str++; } - - return 0; //should never reach anyway } signed char String::naturalnocasecmp_to(const String &p_str) const { @@ -706,8 +726,6 @@ String String::get_slicec(CharType p_splitter, int p_slice) const { i++; } - - return String(); //no find! } Vector<String> String::split_spaces() const { @@ -761,7 +779,7 @@ Vector<String> String::split(const String &p_splitter, bool p_allow_empty, int p if (p_allow_empty || (end > from)) { if (p_maxsplit <= 0) ret.push_back(substr(from, end - from)); - else if (p_maxsplit > 0) { + else { // Put rest of the string and leave cycle. if (p_maxsplit == ret.size()) { @@ -1319,7 +1337,17 @@ String String::num_scientific(double p_num) { char buf[256]; #if defined(__GNUC__) || defined(_MSC_VER) + +#if (defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER < 1900)) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT) + // MinGW and old MSC require _set_output_format() to conform to C99 output for printf + unsigned int old_exponent_format = _set_output_format(_TWO_DIGIT_EXPONENT); +#endif snprintf(buf, 256, "%lg", p_num); + +#if (defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER < 1900)) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT) + _set_output_format(old_exponent_format); +#endif + #else sprintf(buf, "%.16lg", p_num); #endif @@ -1388,7 +1416,7 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { if (skip == 0) { - uint8_t c = *ptrtmp; + uint8_t c = *ptrtmp >= 0 ? *ptrtmp : uint8_t(256 + *ptrtmp); /* Determine the number of characters in sequence */ if ((c & 0x80) == 0) @@ -1651,6 +1679,7 @@ int String::hex_to_int(bool p_with_prefix) const { return 0; } + ERR_FAIL_COND_V_MSG(hex > INT32_MAX / 16, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + *this + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); hex *= 16; hex += n; s++; @@ -1692,6 +1721,7 @@ int64_t String::hex_to_int64(bool p_with_prefix) const { return 0; } + ERR_FAIL_COND_V_MSG(hex > INT64_MAX / 16, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); hex *= 16; hex += n; s++; @@ -1700,6 +1730,46 @@ int64_t String::hex_to_int64(bool p_with_prefix) const { return hex * sign; } +int64_t String::bin_to_int64(bool p_with_prefix) const { + + if (p_with_prefix && length() < 3) + return 0; + + const CharType *s = ptr(); + + int64_t sign = s[0] == '-' ? -1 : 1; + + if (sign < 0) { + s++; + } + + if (p_with_prefix) { + if (s[0] != '0' || s[1] != 'b') + return 0; + s += 2; + } + + int64_t binary = 0; + + while (*s) { + + CharType c = LOWERCASE(*s); + int64_t n; + if (c == '0' || c == '1') { + n = c - '0'; + } else { + return 0; + } + + ERR_FAIL_COND_V_MSG(binary > INT64_MAX / 2, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + binary *= 2; + binary += n; + s++; + } + + return binary * sign; +} + int String::to_int() const { if (length() == 0) @@ -1715,6 +1785,7 @@ int String::to_int() const { CharType c = operator[](i); if (c >= '0' && c <= '9') { + ERR_FAIL_COND_V_MSG(integer > INT32_MAX / 10, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + *this + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); integer *= 10; integer += c - '0'; @@ -1742,6 +1813,7 @@ int64_t String::to_int64() const { CharType c = operator[](i); if (c >= '0' && c <= '9') { + ERR_FAIL_COND_V_MSG(integer > INT64_MAX / 10, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); integer *= 10; integer += c - '0'; @@ -1772,6 +1844,7 @@ int String::to_int(const char *p_str, int p_len) { char c = p_str[i]; if (c >= '0' && c <= '9') { + ERR_FAIL_COND_V_MSG(integer > INT32_MAX / 10, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); integer *= 10; integer += c - '0'; @@ -1844,7 +1917,7 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point 1.0e256 }; - int sign, expSign = false; + bool sign, expSign = false; double fraction, dblExp; const double *d; const C *p; @@ -2084,6 +2157,14 @@ int64_t String::to_int(const CharType *p_str, int p_len) { if (c >= '0' && c <= '9') { + if (integer > INT64_MAX / 10) { + String number(""); + str = p_str; + while (*str && str != limit) { + number += *(str++); + } + ERR_FAIL_V_MSG(sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + number + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + } integer *= 10; integer += c - '0'; } else { @@ -2196,54 +2277,63 @@ uint64_t String::hash64() const { String String::md5_text() const { CharString cs = utf8(); - MD5_CTX ctx; - MD5Init(&ctx); - MD5Update(&ctx, (unsigned char *)cs.ptr(), cs.length()); - MD5Final(&ctx); - return String::md5(ctx.digest); + unsigned char hash[16]; + CryptoCore::md5((unsigned char *)cs.ptr(), cs.length(), hash); + return String::hex_encode_buffer(hash, 16); +} + +String String::sha1_text() const { + CharString cs = utf8(); + unsigned char hash[20]; + CryptoCore::sha1((unsigned char *)cs.ptr(), cs.length(), hash); + return String::hex_encode_buffer(hash, 20); } String String::sha256_text() const { CharString cs = utf8(); unsigned char hash[32]; - sha256_context ctx; - sha256_init(&ctx); - sha256_hash(&ctx, (unsigned char *)cs.ptr(), cs.length()); - sha256_done(&ctx, hash); + CryptoCore::sha256((unsigned char *)cs.ptr(), cs.length(), hash); return String::hex_encode_buffer(hash, 32); } Vector<uint8_t> String::md5_buffer() const { CharString cs = utf8(); - MD5_CTX ctx; - MD5Init(&ctx); - MD5Update(&ctx, (unsigned char *)cs.ptr(), cs.length()); - MD5Final(&ctx); + unsigned char hash[16]; + CryptoCore::md5((unsigned char *)cs.ptr(), cs.length(), hash); Vector<uint8_t> ret; ret.resize(16); for (int i = 0; i < 16; i++) { - ret.write[i] = ctx.digest[i]; - }; - + ret.write[i] = hash[i]; + } return ret; }; +Vector<uint8_t> String::sha1_buffer() const { + CharString cs = utf8(); + unsigned char hash[20]; + CryptoCore::sha1((unsigned char *)cs.ptr(), cs.length(), hash); + + Vector<uint8_t> ret; + ret.resize(20); + for (int i = 0; i < 20; i++) { + ret.write[i] = hash[i]; + } + + return ret; +} + Vector<uint8_t> String::sha256_buffer() const { CharString cs = utf8(); unsigned char hash[32]; - sha256_context ctx; - sha256_init(&ctx); - sha256_hash(&ctx, (unsigned char *)cs.ptr(), cs.length()); - sha256_done(&ctx, hash); + CryptoCore::sha256((unsigned char *)cs.ptr(), cs.length(), hash); Vector<uint8_t> ret; ret.resize(32); for (int i = 0; i < 32; i++) { ret.write[i] = hash[i]; } - return ret; } @@ -2267,6 +2357,9 @@ String String::insert(int p_at_pos, const String &p_string) const { } String String::substr(int p_from, int p_chars) const { + if (p_chars == -1) + p_chars = length() - p_from; + if (empty() || p_from < 0 || p_from >= length() || p_chars <= 0) return ""; @@ -2661,6 +2754,51 @@ bool String::is_quoted() const { return is_enclosed_in("\"") || is_enclosed_in("'"); } +int String::_count(const String &p_string, int p_from, int p_to, bool p_case_insensitive) const { + if (p_string.empty()) { + return 0; + } + int len = length(); + int slen = p_string.length(); + if (len < slen) { + return 0; + } + String str; + if (p_from >= 0 && p_to >= 0) { + if (p_to == 0) { + p_to = len; + } else if (p_from >= p_to) { + return 0; + } + if (p_from == 0 && p_to == len) { + str = String(); + str.copy_from_unchecked(&c_str()[0], len); + } else { + str = substr(p_from, p_to - p_from); + } + } else { + return 0; + } + int c = 0; + int idx = -1; + do { + idx = p_case_insensitive ? str.findn(p_string) : str.find(p_string); + if (idx != -1) { + str = str.substr(idx + slen, str.length() - slen); + ++c; + } + } while (idx != -1); + return c; +} + +int String::count(const String &p_string, int p_from, int p_to) const { + return _count(p_string, p_from, p_to, false); +} + +int String::countn(const String &p_string, int p_from, int p_to) const { + return _count(p_string, p_from, p_to, true); +} + bool String::_base_is_subsequence_of(const String &p_string, bool case_insensitive) const { int len = length(); @@ -2892,16 +3030,24 @@ String String::replace(const char *p_key, const char *p_with) const { String String::replace_first(const String &p_key, const String &p_with) const { + int pos = find(p_key); + if (pos >= 0) { + return substr(0, pos) + p_with + substr(pos + p_key.length(), length()); + } + + return *this; +} +String String::replacen(const String &p_key, const String &p_with) const { + String new_string; int search_from = 0; int result = 0; - while ((result = find(p_key, search_from)) >= 0) { + while ((result = findn(p_key, search_from)) >= 0) { new_string += substr(search_from, result - search_from); new_string += p_with; search_from = result + p_key.length(); - break; } if (search_from == 0) { @@ -2910,28 +3056,22 @@ String String::replace_first(const String &p_key, const String &p_with) const { } new_string += substr(search_from, length() - search_from); - return new_string; } -String String::replacen(const String &p_key, const String &p_with) const { - String new_string; - int search_from = 0; - int result = 0; +String String::repeat(int p_count) const { - while ((result = findn(p_key, search_from)) >= 0) { + ERR_FAIL_COND_V_MSG(p_count < 0, "", "Parameter count should be a positive number."); - new_string += substr(search_from, result - search_from); - new_string += p_with; - search_from = result + p_key.length(); - } + String new_string; + const CharType *src = this->c_str(); - if (search_from == 0) { + new_string.resize(length() * p_count + 1); - return *this; - } + for (int i = 0; i < p_count; i++) + for (int j = 0; j < length(); j++) + new_string[i * length() + j] = src[j]; - new_string += substr(search_from, length() - search_from); return new_string; } @@ -3038,29 +3178,16 @@ String String::strip_edges(bool left, bool right) const { String String::strip_escapes() const { - int len = length(); - int beg = 0, end = len; - + String new_string; for (int i = 0; i < length(); i++) { - if (operator[](i) <= 31) - beg++; - else - break; - } - - for (int i = (int)(length() - 1); i >= 0; i--) { - - if (operator[](i) <= 31) - end--; - else - break; + // Escape characters on first page of the ASCII table, before 32 (Space). + if (operator[](i) < 32) + continue; + new_string += operator[](i); } - if (beg == 0 && end == len) - return *this; - - return substr(beg, end - beg); + return new_string; } String String::lstrip(const String &p_chars) const { @@ -3171,9 +3298,7 @@ String String::simplify_path() const { static int _humanize_digits(int p_num) { - if (p_num < 10) - return 2; - else if (p_num < 100) + if (p_num < 100) return 2; else if (p_num < 1024) return 1; @@ -3181,21 +3306,29 @@ static int _humanize_digits(int p_num) { return 0; } -String String::humanize_size(size_t p_size) { +String String::humanize_size(uint64_t p_size) { uint64_t _div = 1; - static const char *prefix[] = { " Bytes", " KB", " MB", " GB", "TB", " PB", "HB", "" }; + Vector<String> prefixes; + prefixes.push_back(RTR("B")); + prefixes.push_back(RTR("KiB")); + prefixes.push_back(RTR("MiB")); + prefixes.push_back(RTR("GiB")); + prefixes.push_back(RTR("TiB")); + prefixes.push_back(RTR("PiB")); + prefixes.push_back(RTR("EiB")); + int prefix_idx = 0; - while (p_size > (_div * 1024) && prefix[prefix_idx][0]) { + while (prefix_idx < prefixes.size() && p_size > (_div * 1024)) { _div *= 1024; prefix_idx++; } - int digits = prefix_idx > 0 ? _humanize_digits(p_size / _div) : 0; - double divisor = prefix_idx > 0 ? _div : 1; + const int digits = prefix_idx > 0 ? _humanize_digits(p_size / _div) : 0; + const double divisor = prefix_idx > 0 ? _div : 1; - return String::num(p_size / divisor, digits) + prefix[prefix_idx]; + return String::num(p_size / divisor).pad_decimals(digits) + " " + prefixes[prefix_idx]; } bool String::is_abs_path() const { @@ -3297,7 +3430,7 @@ String String::http_unescape() const { if ((ord1 >= '0' && ord1 <= '9') || (ord1 >= 'A' && ord1 <= 'Z')) { CharType ord2 = ord_at(i + 2); if ((ord2 >= '0' && ord2 <= '9') || (ord2 >= 'A' && ord2 <= 'Z')) { - char bytes[2] = { (char)ord1, (char)ord2 }; + char bytes[3] = { (char)ord1, (char)ord2, 0 }; res += (char)strtol(bytes, NULL, 16); i += 2; } @@ -3748,6 +3881,20 @@ bool String::is_valid_html_color() const { return Color::html_is_valid(*this); } +bool String::is_valid_filename() const { + + String stripped = strip_edges(); + if (*this != stripped) { + return false; + } + + if (stripped == String()) { + return false; + } + + return !(find(":") != -1 || find("/") != -1 || find("\\") != -1 || find("?") != -1 || find("*") != -1 || find("\"") != -1 || find("|") != -1 || find("%") != -1 || find("<") != -1 || find(">") != -1); +} + bool String::is_valid_ip_address() const { if (find(":") >= 0) { @@ -3883,7 +4030,6 @@ String String::percent_decode() const { uint8_t a = LOWERCASE(cs[i + 1]); uint8_t b = LOWERCASE(cs[i + 2]); - c = 0; if (a >= '0' && a <= '9') c = (a - '0') << 4; else if (a >= 'a' && a <= 'f') @@ -3922,6 +4068,11 @@ String itos(int64_t p_val) { return String::num_int64(p_val); } +String uitos(uint64_t p_val) { + + return String::num_uint64(p_val); +} + String rtos(double p_val) { return String::num(p_val); diff --git a/core/ustring.h b/core/ustring.h index 9288c1526e..dfc5044942 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -36,10 +36,6 @@ #include "core/typedefs.h" #include "core/vector.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - template <class T> class CharProxy { friend class CharString; @@ -101,12 +97,17 @@ public: _cowdata._ref(p_str._cowdata); return *this; } + _FORCE_INLINE_ CharString(const char *p_cstr) { copy_from(p_cstr); } + CharString &operator=(const char *p_cstr); bool operator<(const CharString &p_right) const; CharString &operator+=(char p_char); int length() const { return size() ? size() - 1 : 0; } const char *get_data() const; operator const char *() const { return get_data(); }; + +protected: + void copy_from(const char *p_cstr); }; typedef wchar_t CharType; @@ -132,6 +133,7 @@ class String { void copy_from(const CharType &p_char); void copy_from_unchecked(const CharType *p_char, const int p_length); bool _base_is_subsequence_of(const String &p_string, bool case_insensitive) const; + int _count(const String &p_string, int p_from, int p_to, bool p_case_insensitive) const; public: enum { @@ -196,7 +198,7 @@ public: } /* complex helpers */ - String substr(int p_from, int p_chars) const; + String substr(int p_from, int p_chars = -1) const; int find(const String &p_str, int p_from = 0) const; ///< return <0 if failed int find(const char *p_str, int p_from = 0) const; ///< return <0 if failed int find_char(const CharType &p_char, int p_from = 0) const; ///< return <0 if failed @@ -221,6 +223,7 @@ public: String replace(const String &p_key, const String &p_with) const; String replace(const char *p_key, const char *p_with) const; String replacen(const String &p_key, const String &p_with) const; + String repeat(int p_count) const; String insert(int p_at_pos, const String &p_string) const; String pad_decimals(int p_digits) const; String pad_zeros(int p_digits) const; @@ -246,6 +249,7 @@ public: int to_int() const; int64_t hex_to_int64(bool p_with_prefix = true) const; + int64_t bin_to_int64(bool p_with_prefix = true) const; int64_t to_int64() const; static int to_int(const char *p_str, int p_len = -1); static double to_double(const char *p_str); @@ -273,6 +277,9 @@ public: String to_upper() const; String to_lower() const; + int count(const String &p_string, int p_from = 0, int p_to = 0) const; + int countn(const String &p_string, int p_from = 0, int p_to = 0) const; + String left(int p_pos) const; String right(int p_pos) const; String dedent() const; @@ -299,8 +306,10 @@ public: uint32_t hash() const; /* hash the string */ uint64_t hash64() const; /* hash the string */ String md5_text() const; + String sha1_text() const; String sha256_text() const; Vector<uint8_t> md5_buffer() const; + Vector<uint8_t> sha1_buffer() const; Vector<uint8_t> sha256_buffer() const; _FORCE_INLINE_ bool empty() const { return length() == 0; } @@ -313,7 +322,7 @@ public: String path_to_file(const String &p_path) const; String get_base_dir() const; String get_file() const; - static String humanize_size(size_t p_size); + static String humanize_size(uint64_t p_size); String simplify_path() const; String xml_escape(bool p_escape_quotes = false) const; @@ -335,6 +344,7 @@ public: bool is_valid_hex_number(bool p_with_prefix) const; bool is_valid_html_color() const; bool is_valid_ip_address() const; + bool is_valid_filename() const; /** * The constructors must not depend on other overloads @@ -359,6 +369,7 @@ String operator+(const char *p_chr, const String &p_str); String operator+(CharType p_chr, const String &p_str); String itos(int64_t p_val); +String uitos(uint64_t p_val); String rtos(double p_val); String rtoss(double p_val); //scientific version @@ -397,8 +408,6 @@ _FORCE_INLINE_ bool is_str_less(const L *l_ptr, const R *r_ptr) { l_ptr++; r_ptr++; } - - CRASH_COND(true); // unreachable } /* end of namespace */ @@ -406,11 +415,18 @@ _FORCE_INLINE_ bool is_str_less(const L *l_ptr, const R *r_ptr) { //tool translate #ifdef TOOLS_ENABLED +//gets parsed String TTR(const String &); +//use for C strings +#define TTRC(m_value) (m_value) +//use to avoid parsing (for use later with C strings) +#define TTRGET(m_value) TTR(m_value) #else -#define TTR(m_val) (String()) +#define TTR(m_value) (String()) +#define TTRC(m_value) (m_value) +#define TTRGET(m_value) (m_value) #endif diff --git a/core/variant.cpp b/core/variant.cpp index 2ee2e8e293..e0cc6685f4 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -179,7 +179,8 @@ String Variant::get_type_name(Variant::Type p_type) { return "PoolColorArray"; } break; - default: {} + default: { + } } return ""; @@ -403,7 +404,8 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { valid_types = valid; } break; - default: {} + default: { + } } if (valid_types) { @@ -649,7 +651,8 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; - default: {} + default: { + } } if (valid_types) { @@ -706,7 +709,7 @@ bool Variant::is_zero() const { // atomic types case BOOL: { - return _data._bool == false; + return !(_data._bool); } break; case INT: { @@ -841,7 +844,8 @@ bool Variant::is_zero() const { return reinterpret_cast<const PoolVector<Color> *>(_data._mem)->size() == 0; } break; - default: {} + default: { + } } return false; @@ -896,7 +900,9 @@ bool Variant::is_one() const { } break; - default: { return !is_zero(); } + default: { + return !is_zero(); + } } return false; @@ -904,7 +910,15 @@ bool Variant::is_one() const { void Variant::reference(const Variant &p_variant) { - clear(); + switch (type) { + case NIL: + case BOOL: + case INT: + case REAL: + break; + default: + clear(); + } type = p_variant.type; @@ -1039,7 +1053,8 @@ void Variant::reference(const Variant &p_variant) { memnew_placement(_data._mem, PoolVector<Color>(*reinterpret_cast<const PoolVector<Color> *>(p_variant._data._mem))); } break; - default: {} + default: { + } } } @@ -1143,7 +1158,8 @@ void Variant::clear() { reinterpret_cast<PoolVector<Color> *>(_data._mem)->~PoolVector<Color>(); } break; - default: {} /* not needed */ + default: { + } /* not needed */ } type = NIL; @@ -1163,8 +1179,6 @@ Variant::operator signed int() const { return 0; } } - - return 0; } Variant::operator unsigned int() const { @@ -1180,8 +1194,6 @@ Variant::operator unsigned int() const { return 0; } } - - return 0; } Variant::operator int64_t() const { @@ -1198,8 +1210,6 @@ Variant::operator int64_t() const { return 0; } } - - return 0; } /* @@ -1236,8 +1246,6 @@ Variant::operator uint64_t() const { return 0; } } - - return 0; } #ifdef NEED_LONG_INT @@ -1292,8 +1300,6 @@ Variant::operator signed short() const { return 0; } } - - return 0; } Variant::operator unsigned short() const { @@ -1309,8 +1315,6 @@ Variant::operator unsigned short() const { return 0; } } - - return 0; } Variant::operator signed char() const { @@ -1326,8 +1330,6 @@ Variant::operator signed char() const { return 0; } } - - return 0; } Variant::operator unsigned char() const { @@ -1343,8 +1345,6 @@ Variant::operator unsigned char() const { return 0; } } - - return 0; } Variant::operator CharType() const { @@ -1366,8 +1366,6 @@ Variant::operator float() const { return 0; } } - - return 0; } Variant::operator double() const { @@ -1383,8 +1381,6 @@ Variant::operator double() const { return 0; } } - - return true; } Variant::operator StringName() const { @@ -1407,7 +1403,12 @@ struct _VariantStrPair { }; Variant::operator String() const { + List<const void *> stack; + + return stringify(stack); +} +String Variant::stringify(List<const void *> &stack) const { switch (type) { case NIL: return "Null"; @@ -1459,6 +1460,12 @@ Variant::operator String() const { case DICTIONARY: { const Dictionary &d = *reinterpret_cast<const Dictionary *>(_data._mem); + if (stack.find(d.id())) { + return "{...}"; + } + + stack.push_back(d.id()); + //const String *K=NULL; String str("{"); List<Variant> keys; @@ -1469,8 +1476,9 @@ Variant::operator String() const { for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { _VariantStrPair sp; - sp.key = String(E->get()); - sp.value = d[E->get()]; + sp.key = E->get().stringify(stack); + sp.value = d[E->get()].stringify(stack); + pairs.push_back(sp); } @@ -1553,12 +1561,19 @@ Variant::operator String() const { case ARRAY: { Array arr = operator Array(); + if (stack.find(arr.id())) { + return "[...]"; + } + stack.push_back(arr.id()); + String str("["); for (int i = 0; i < arr.size(); i++) { if (i) str += ", "; - str += String(arr[i]); - }; + + str += arr[i].stringify(stack); + } + str += "]"; return str; @@ -1574,7 +1589,7 @@ Variant::operator String() const { }; }; #endif - return "[" + _get_obj().obj->get_class() + ":" + itos(_get_obj().obj->get_instance_id()) + "]"; + return _get_obj().obj->to_string(); } else return "[Object:null]"; @@ -1733,10 +1748,7 @@ Variant::operator RID() const { } else if (type == OBJECT && _get_obj().obj) { #ifdef DEBUG_ENABLED if (ScriptDebugger::get_singleton()) { - if (!ObjectDB::instance_validate(_get_obj().obj)) { - ERR_EXPLAIN("Invalid pointer (object was deleted)"); - ERR_FAIL_V(RID()); - }; + ERR_FAIL_COND_V_MSG(!ObjectDB::instance_validate(_get_obj().obj), RID(), "Invalid pointer (object was deleted)."); }; #endif Variant::CallError ce; @@ -1823,10 +1835,10 @@ inline DA _convert_array_from_variant(const Variant &p_variant) { case Variant::POOL_COLOR_ARRAY: { return _convert_array<DA, PoolVector<Color> >(p_variant.operator PoolVector<Color>()); } - default: { return DA(); } + default: { + return DA(); + } } - - return DA(); } Variant::operator Array() const { @@ -2290,7 +2302,7 @@ Variant::Variant(const Object *p_object) { Variant::Variant(const Dictionary &p_dictionary) { type = DICTIONARY; - memnew_placement(_data._mem, (Dictionary)(p_dictionary)); + memnew_placement(_data._mem, Dictionary(p_dictionary)); } Variant::Variant(const Array &p_array) { @@ -2409,9 +2421,6 @@ Variant::Variant(const PoolVector<Face3> &p_face_array) { for (int j = 0; j < 3; j++) w[i * 3 + j] = r[i].vertex[j]; } - - r = PoolVector<Face3>::Read(); - w = PoolVector<Vector3>::Write(); } type = NIL; @@ -2642,7 +2651,8 @@ void Variant::operator=(const Variant &p_variant) { *reinterpret_cast<PoolVector<Color> *>(_data._mem) = *reinterpret_cast<const PoolVector<Color> *>(p_variant._data._mem); } break; - default: {} + default: { + } } } @@ -2919,7 +2929,8 @@ uint32_t Variant::hash() const { return hash; } break; - default: {} + default: { + } } return 0; @@ -3167,7 +3178,8 @@ bool Variant::is_shared() const { case OBJECT: return true; case ARRAY: return true; case DICTIONARY: return true; - default: {} + default: { + } } return false; @@ -3204,7 +3216,8 @@ Variant Variant::call(const StringName &p_method, VARIANT_ARG_DECLARE) { String err = "Too many arguments for method '" + p_method + "'"; ERR_PRINT(err.utf8().get_data()); } break; - default: {} + default: { + } } return ret; @@ -3286,7 +3299,7 @@ String vformat(const String &p_text, const Variant &p1, const Variant &p2, const bool error = false; String fmt = p_text.sprintf(args, &error); - ERR_FAIL_COND_V(error, String()); + ERR_FAIL_COND_V_MSG(error, String(), fmt); return fmt; } diff --git a/core/variant.h b/core/variant.h index 9215d15bf0..c4f69c3e8d 100644 --- a/core/variant.h +++ b/core/variant.h @@ -31,10 +31,6 @@ #ifndef VARIANT_H #define VARIANT_H -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - #include "core/array.h" #include "core/color.h" #include "core/dictionary.h" @@ -248,8 +244,8 @@ public: Variant(unsigned short p_short); Variant(signed char p_char); // real one Variant(unsigned char p_char); - Variant(int64_t p_char); // real one - Variant(uint64_t p_char); + Variant(int64_t p_int); // real one + Variant(uint64_t p_int); Variant(float p_float); Variant(double p_double); Variant(const String &p_string); @@ -262,11 +258,11 @@ public: Variant(const Plane &p_plane); Variant(const ::AABB &p_aabb); Variant(const Quat &p_quat); - Variant(const Basis &p_transform); + Variant(const Basis &p_matrix); Variant(const Transform2D &p_transform); Variant(const Transform &p_transform); Variant(const Color &p_color); - Variant(const NodePath &p_path); + Variant(const NodePath &p_node_path); Variant(const RefPtr &p_resource); Variant(const RID &p_rid); Variant(const Object *p_object); @@ -283,17 +279,17 @@ public: Variant(const PoolVector<Face3> &p_face_array); Variant(const Vector<Variant> &p_array); - Variant(const Vector<uint8_t> &p_raw_array); - Variant(const Vector<int> &p_int_array); - Variant(const Vector<real_t> &p_real_array); - Variant(const Vector<String> &p_string_array); - Variant(const Vector<StringName> &p_string_array); - Variant(const Vector<Vector3> &p_vector3_array); - Variant(const Vector<Color> &p_color_array); + Variant(const Vector<uint8_t> &p_array); + Variant(const Vector<int> &p_array); + Variant(const Vector<real_t> &p_array); + Variant(const Vector<String> &p_array); + Variant(const Vector<StringName> &p_array); + Variant(const Vector<Vector3> &p_array); + Variant(const Vector<Color> &p_array); Variant(const Vector<Plane> &p_array); // helper Variant(const Vector<RID> &p_array); // helper Variant(const Vector<Vector2> &p_array); // helper - Variant(const PoolVector<Vector2> &p_array); // helper + Variant(const PoolVector<Vector2> &p_vector2_array); // helper Variant(const IP_Address &p_address); @@ -401,6 +397,7 @@ public: bool hash_compare(const Variant &p_variant) const; bool booleanize() const; + String stringify(List<const void *> &stack) const; void static_assign(const Variant &p_variant); static void get_constructor_list(Variant::Type p_type, List<MethodInfo> *p_list); diff --git a/core/variant_call.cpp b/core/variant_call.cpp index ca8b4e119a..f6ecc506a4 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -32,6 +32,7 @@ #include "core/color_names.inc" #include "core/core_string_names.h" +#include "core/crypto/crypto_core.h" #include "core/io/compression.h" #include "core/object.h" #include "core/os/os.h" @@ -69,7 +70,7 @@ struct _VariantCall { for (int i = 0; i < arg_count; i++) { - if (!tptr[i] || tptr[i] == p_args[i]->type) + if (tptr[i] == Variant::NIL || tptr[i] == p_args[i]->type) continue; // all good if (!Variant::can_convert(p_args[i]->type, tptr[i])) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; @@ -236,6 +237,8 @@ struct _VariantCall { VCALL_LOCALMEM1R(String, casecmp_to); VCALL_LOCALMEM1R(String, nocasecmp_to); VCALL_LOCALMEM0R(String, length); + VCALL_LOCALMEM3R(String, count); + VCALL_LOCALMEM3R(String, countn); VCALL_LOCALMEM2R(String, substr); VCALL_LOCALMEM2R(String, find); VCALL_LOCALMEM1R(String, find_last); @@ -253,6 +256,7 @@ struct _VariantCall { VCALL_LOCALMEM2R(String, format); VCALL_LOCALMEM2R(String, replace); VCALL_LOCALMEM2R(String, replacen); + VCALL_LOCALMEM1R(String, repeat); VCALL_LOCALMEM2R(String, insert); VCALL_LOCALMEM0R(String, capitalize); VCALL_LOCALMEM3R(String, split); @@ -264,6 +268,7 @@ struct _VariantCall { VCALL_LOCALMEM1R(String, right); VCALL_LOCALMEM0R(String, dedent); VCALL_LOCALMEM2R(String, strip_edges); + VCALL_LOCALMEM0R(String, strip_escapes); VCALL_LOCALMEM1R(String, lstrip); VCALL_LOCALMEM1R(String, rstrip); VCALL_LOCALMEM0R(String, get_extension); @@ -273,16 +278,21 @@ struct _VariantCall { VCALL_LOCALMEM2(String, erase); VCALL_LOCALMEM0R(String, hash); VCALL_LOCALMEM0R(String, md5_text); + VCALL_LOCALMEM0R(String, sha1_text); VCALL_LOCALMEM0R(String, sha256_text); VCALL_LOCALMEM0R(String, md5_buffer); + VCALL_LOCALMEM0R(String, sha1_buffer); VCALL_LOCALMEM0R(String, sha256_buffer); VCALL_LOCALMEM0R(String, empty); + VCALL_LOCALMEM1R(String, humanize_size); VCALL_LOCALMEM0R(String, is_abs_path); VCALL_LOCALMEM0R(String, is_rel_path); VCALL_LOCALMEM0R(String, get_base_dir); VCALL_LOCALMEM0R(String, get_file); VCALL_LOCALMEM0R(String, xml_escape); VCALL_LOCALMEM0R(String, xml_unescape); + VCALL_LOCALMEM0R(String, http_escape); + VCALL_LOCALMEM0R(String, http_unescape); VCALL_LOCALMEM0R(String, c_escape); VCALL_LOCALMEM0R(String, c_unescape); VCALL_LOCALMEM0R(String, json_escape); @@ -294,6 +304,7 @@ struct _VariantCall { VCALL_LOCALMEM1R(String, is_valid_hex_number); VCALL_LOCALMEM0R(String, is_valid_html_color); VCALL_LOCALMEM0R(String, is_valid_ip_address); + VCALL_LOCALMEM0R(String, is_valid_filename); VCALL_LOCALMEM0R(String, to_int); VCALL_LOCALMEM0R(String, to_float); VCALL_LOCALMEM0R(String, hex_to_int); @@ -305,6 +316,10 @@ struct _VariantCall { static void _call_String_to_ascii(Variant &r_ret, Variant &p_self, const Variant **p_args) { String *s = reinterpret_cast<String *>(p_self._data._mem); + if (s->empty()) { + r_ret = PoolByteArray(); + return; + } CharString charstr = s->ascii(); PoolByteArray retval; @@ -312,7 +327,7 @@ struct _VariantCall { retval.resize(len); PoolByteArray::Write w = retval.write(); copymem(w.ptr(), charstr.ptr(), len); - w = PoolVector<uint8_t>::Write(); + w.release(); r_ret = retval; } @@ -320,6 +335,10 @@ struct _VariantCall { static void _call_String_to_utf8(Variant &r_ret, Variant &p_self, const Variant **p_args) { String *s = reinterpret_cast<String *>(p_self._data._mem); + if (s->empty()) { + r_ret = PoolByteArray(); + return; + } CharString charstr = s->utf8(); PoolByteArray retval; @@ -327,23 +346,28 @@ struct _VariantCall { retval.resize(len); PoolByteArray::Write w = retval.write(); copymem(w.ptr(), charstr.ptr(), len); - w = PoolVector<uint8_t>::Write(); + w.release(); r_ret = retval; } - VCALL_LOCALMEM0R(Vector2, normalized); + VCALL_LOCALMEM1R(Vector2, distance_to); + VCALL_LOCALMEM1R(Vector2, distance_squared_to); VCALL_LOCALMEM0R(Vector2, length); VCALL_LOCALMEM0R(Vector2, length_squared); + VCALL_LOCALMEM0R(Vector2, normalized); VCALL_LOCALMEM0R(Vector2, is_normalized); - VCALL_LOCALMEM1R(Vector2, distance_to); - VCALL_LOCALMEM1R(Vector2, distance_squared_to); + VCALL_LOCALMEM1R(Vector2, is_equal_approx); + VCALL_LOCALMEM1R(Vector2, posmod); + VCALL_LOCALMEM1R(Vector2, posmodv); VCALL_LOCALMEM1R(Vector2, project); VCALL_LOCALMEM1R(Vector2, angle_to); VCALL_LOCALMEM1R(Vector2, angle_to_point); + VCALL_LOCALMEM1R(Vector2, direction_to); VCALL_LOCALMEM2R(Vector2, linear_interpolate); VCALL_LOCALMEM2R(Vector2, slerp); VCALL_LOCALMEM4R(Vector2, cubic_interpolate); + VCALL_LOCALMEM2R(Vector2, move_toward); VCALL_LOCALMEM1R(Vector2, rotated); VCALL_LOCALMEM0R(Vector2, tangent); VCALL_LOCALMEM0R(Vector2, floor); @@ -359,32 +383,38 @@ struct _VariantCall { VCALL_LOCALMEM1R(Vector2, cross); VCALL_LOCALMEM0R(Vector2, abs); VCALL_LOCALMEM1R(Vector2, clamped); + VCALL_LOCALMEM0R(Vector2, sign); VCALL_LOCALMEM0R(Rect2, get_area); + VCALL_LOCALMEM0R(Rect2, has_no_area); + VCALL_LOCALMEM1R(Rect2, has_point); + VCALL_LOCALMEM1R(Rect2, is_equal_approx); VCALL_LOCALMEM1R(Rect2, intersects); VCALL_LOCALMEM1R(Rect2, encloses); - VCALL_LOCALMEM0R(Rect2, has_no_area); VCALL_LOCALMEM1R(Rect2, clip); VCALL_LOCALMEM1R(Rect2, merge); - VCALL_LOCALMEM1R(Rect2, has_point); + VCALL_LOCALMEM1R(Rect2, expand); VCALL_LOCALMEM1R(Rect2, grow); VCALL_LOCALMEM2R(Rect2, grow_margin); VCALL_LOCALMEM4R(Rect2, grow_individual); - VCALL_LOCALMEM1R(Rect2, expand); VCALL_LOCALMEM0R(Rect2, abs); VCALL_LOCALMEM0R(Vector3, min_axis); VCALL_LOCALMEM0R(Vector3, max_axis); + VCALL_LOCALMEM1R(Vector3, distance_to); + VCALL_LOCALMEM1R(Vector3, distance_squared_to); VCALL_LOCALMEM0R(Vector3, length); VCALL_LOCALMEM0R(Vector3, length_squared); - VCALL_LOCALMEM0R(Vector3, is_normalized); VCALL_LOCALMEM0R(Vector3, normalized); + VCALL_LOCALMEM0R(Vector3, is_normalized); + VCALL_LOCALMEM1R(Vector3, is_equal_approx); VCALL_LOCALMEM0R(Vector3, inverse); VCALL_LOCALMEM1R(Vector3, snapped); VCALL_LOCALMEM2R(Vector3, rotated); VCALL_LOCALMEM2R(Vector3, linear_interpolate); VCALL_LOCALMEM2R(Vector3, slerp); VCALL_LOCALMEM4R(Vector3, cubic_interpolate); + VCALL_LOCALMEM2R(Vector3, move_toward); VCALL_LOCALMEM1R(Vector3, dot); VCALL_LOCALMEM1R(Vector3, cross); VCALL_LOCALMEM1R(Vector3, outer); @@ -393,17 +423,20 @@ struct _VariantCall { VCALL_LOCALMEM0R(Vector3, floor); VCALL_LOCALMEM0R(Vector3, ceil); VCALL_LOCALMEM0R(Vector3, round); - VCALL_LOCALMEM1R(Vector3, distance_to); - VCALL_LOCALMEM1R(Vector3, distance_squared_to); + VCALL_LOCALMEM1R(Vector3, posmod); + VCALL_LOCALMEM1R(Vector3, posmodv); VCALL_LOCALMEM1R(Vector3, project); VCALL_LOCALMEM1R(Vector3, angle_to); + VCALL_LOCALMEM1R(Vector3, direction_to); VCALL_LOCALMEM1R(Vector3, slide); VCALL_LOCALMEM1R(Vector3, bounce); VCALL_LOCALMEM1R(Vector3, reflect); + VCALL_LOCALMEM0R(Vector3, sign); VCALL_LOCALMEM0R(Plane, normalized); VCALL_LOCALMEM0R(Plane, center); VCALL_LOCALMEM0R(Plane, get_any_point); + VCALL_LOCALMEM1R(Plane, is_equal_approx); VCALL_LOCALMEM1R(Plane, is_point_over); VCALL_LOCALMEM1R(Plane, distance_to); VCALL_LOCALMEM2R(Plane, has_point); @@ -438,6 +471,7 @@ struct _VariantCall { VCALL_LOCALMEM0R(Quat, length_squared); VCALL_LOCALMEM0R(Quat, normalized); VCALL_LOCALMEM0R(Quat, is_normalized); + VCALL_LOCALMEM1R(Quat, is_equal_approx); VCALL_LOCALMEM0R(Quat, inverse); VCALL_LOCALMEM1R(Quat, dot); VCALL_LOCALMEM1R(Quat, xform); @@ -463,6 +497,7 @@ struct _VariantCall { VCALL_LOCALMEM1R(Color, darkened); VCALL_LOCALMEM1R(Color, to_html); VCALL_LOCALMEM4R(Color, from_hsv); + VCALL_LOCALMEM1R(Color, is_equal_approx); VCALL_LOCALMEM0R(RID, get_id); @@ -515,6 +550,7 @@ struct _VariantCall { VCALL_LOCALMEM2R(Array, bsearch); VCALL_LOCALMEM4R(Array, bsearch_custom); VCALL_LOCALMEM1R(Array, duplicate); + VCALL_LOCALMEM4R(Array, slice); VCALL_LOCALMEM0(Array, invert); VCALL_LOCALMEM0R(Array, max); VCALL_LOCALMEM0R(Array, min); @@ -523,7 +559,7 @@ struct _VariantCall { PoolByteArray *ba = reinterpret_cast<PoolByteArray *>(p_self._data._mem); String s; - if (ba->size() >= 0) { + if (ba->size() > 0) { PoolByteArray::Read r = ba->read(); CharString cs; cs.resize(ba->size() + 1); @@ -539,7 +575,7 @@ struct _VariantCall { PoolByteArray *ba = reinterpret_cast<PoolByteArray *>(p_self._data._mem); String s; - if (ba->size() >= 0) { + if (ba->size() > 0) { PoolByteArray::Read r = ba->read(); s.parse_utf8((const char *)r.ptr(), ba->size()); } @@ -550,14 +586,15 @@ struct _VariantCall { PoolByteArray *ba = reinterpret_cast<PoolByteArray *>(p_self._data._mem); PoolByteArray compressed; - Compression::Mode mode = (Compression::Mode)(int)(*p_args[0]); + if (ba->size() > 0) { + Compression::Mode mode = (Compression::Mode)(int)(*p_args[0]); - compressed.resize(Compression::get_max_compressed_buffer_size(ba->size(), mode)); - int result = Compression::compress(compressed.write().ptr(), ba->read().ptr(), ba->size(), mode); - - result = result >= 0 ? result : 0; - compressed.resize(result); + compressed.resize(Compression::get_max_compressed_buffer_size(ba->size(), mode)); + int result = Compression::compress(compressed.write().ptr(), ba->read().ptr(), ba->size(), mode); + result = result >= 0 ? result : 0; + compressed.resize(result); + } r_ret = compressed; } @@ -569,10 +606,9 @@ struct _VariantCall { int buffer_size = (int)(*p_args[0]); - if (buffer_size < 0) { + if (buffer_size <= 0) { r_ret = decompressed; - ERR_EXPLAIN("Decompression buffer size is less than zero"); - ERR_FAIL(); + ERR_FAIL_MSG("Decompression buffer size must be greater than zero."); } decompressed.resize(buffer_size); @@ -584,7 +620,19 @@ struct _VariantCall { r_ret = decompressed; } + static void _call_PoolByteArray_hex_encode(Variant &r_ret, Variant &p_self, const Variant **p_args) { + PoolByteArray *ba = reinterpret_cast<PoolByteArray *>(p_self._data._mem); + if (ba->size() == 0) { + r_ret = String(); + return; + } + PoolByteArray::Read r = ba->read(); + String s = String::hex_encode_buffer(&r[0], ba->size()); + r_ret = s; + } + VCALL_LOCALMEM0R(PoolByteArray, size); + VCALL_LOCALMEM0R(PoolByteArray, empty); VCALL_LOCALMEM2(PoolByteArray, set); VCALL_LOCALMEM1R(PoolByteArray, get); VCALL_LOCALMEM1(PoolByteArray, push_back); @@ -597,6 +645,7 @@ struct _VariantCall { VCALL_LOCALMEM2R(PoolByteArray, subarray); VCALL_LOCALMEM0R(PoolIntArray, size); + VCALL_LOCALMEM0R(PoolIntArray, empty); VCALL_LOCALMEM2(PoolIntArray, set); VCALL_LOCALMEM1R(PoolIntArray, get); VCALL_LOCALMEM1(PoolIntArray, push_back); @@ -608,6 +657,7 @@ struct _VariantCall { VCALL_LOCALMEM0(PoolIntArray, invert); VCALL_LOCALMEM0R(PoolRealArray, size); + VCALL_LOCALMEM0R(PoolRealArray, empty); VCALL_LOCALMEM2(PoolRealArray, set); VCALL_LOCALMEM1R(PoolRealArray, get); VCALL_LOCALMEM1(PoolRealArray, push_back); @@ -619,6 +669,7 @@ struct _VariantCall { VCALL_LOCALMEM0(PoolRealArray, invert); VCALL_LOCALMEM0R(PoolStringArray, size); + VCALL_LOCALMEM0R(PoolStringArray, empty); VCALL_LOCALMEM2(PoolStringArray, set); VCALL_LOCALMEM1R(PoolStringArray, get); VCALL_LOCALMEM1(PoolStringArray, push_back); @@ -631,6 +682,7 @@ struct _VariantCall { VCALL_LOCALMEM1R(PoolStringArray, join); VCALL_LOCALMEM0R(PoolVector2Array, size); + VCALL_LOCALMEM0R(PoolVector2Array, empty); VCALL_LOCALMEM2(PoolVector2Array, set); VCALL_LOCALMEM1R(PoolVector2Array, get); VCALL_LOCALMEM1(PoolVector2Array, push_back); @@ -642,6 +694,7 @@ struct _VariantCall { VCALL_LOCALMEM0(PoolVector2Array, invert); VCALL_LOCALMEM0R(PoolVector3Array, size); + VCALL_LOCALMEM0R(PoolVector3Array, empty); VCALL_LOCALMEM2(PoolVector3Array, set); VCALL_LOCALMEM1R(PoolVector3Array, get); VCALL_LOCALMEM1(PoolVector3Array, push_back); @@ -653,6 +706,7 @@ struct _VariantCall { VCALL_LOCALMEM0(PoolVector3Array, invert); VCALL_LOCALMEM0R(PoolColorArray, size); + VCALL_LOCALMEM0R(PoolColorArray, empty); VCALL_LOCALMEM2(PoolColorArray, set); VCALL_LOCALMEM1R(PoolColorArray, get); VCALL_LOCALMEM1(PoolColorArray, push_back); @@ -691,13 +745,16 @@ struct _VariantCall { VCALL_PTR0R(AABB, get_area); VCALL_PTR0R(AABB, has_no_area); VCALL_PTR0R(AABB, has_no_surface); + VCALL_PTR1R(AABB, has_point); + VCALL_PTR1R(AABB, is_equal_approx); VCALL_PTR1R(AABB, intersects); VCALL_PTR1R(AABB, encloses); - VCALL_PTR1R(AABB, merge); - VCALL_PTR1R(AABB, intersection); VCALL_PTR1R(AABB, intersects_plane); VCALL_PTR2R(AABB, intersects_segment); - VCALL_PTR1R(AABB, has_point); + VCALL_PTR1R(AABB, intersection); + VCALL_PTR1R(AABB, merge); + VCALL_PTR1R(AABB, expand); + VCALL_PTR1R(AABB, grow); VCALL_PTR1R(AABB, get_support); VCALL_PTR0R(AABB, get_longest_axis); VCALL_PTR0R(AABB, get_longest_axis_index); @@ -705,8 +762,6 @@ struct _VariantCall { VCALL_PTR0R(AABB, get_shortest_axis); VCALL_PTR0R(AABB, get_shortest_axis_index); VCALL_PTR0R(AABB, get_shortest_axis_size); - VCALL_PTR1R(AABB, expand); - VCALL_PTR1R(AABB, grow); VCALL_PTR1R(AABB, get_endpoint); VCALL_PTR0R(Transform2D, inverse); @@ -719,6 +774,7 @@ struct _VariantCall { VCALL_PTR1R(Transform2D, scaled); VCALL_PTR1R(Transform2D, translated); VCALL_PTR2R(Transform2D, interpolate_with); + VCALL_PTR1R(Transform2D, is_equal_approx); static void _call_Transform2D_xform(Variant &r_ret, Variant &p_self, const Variant **p_args) { @@ -726,6 +782,7 @@ struct _VariantCall { case Variant::VECTOR2: r_ret = reinterpret_cast<Transform2D *>(p_self._data._ptr)->xform(p_args[0]->operator Vector2()); return; case Variant::RECT2: r_ret = reinterpret_cast<Transform2D *>(p_self._data._ptr)->xform(p_args[0]->operator Rect2()); return; + case Variant::POOL_VECTOR2_ARRAY: r_ret = reinterpret_cast<Transform2D *>(p_self._data._ptr)->xform(p_args[0]->operator PoolVector2Array()); return; default: r_ret = Variant(); } } @@ -736,6 +793,7 @@ struct _VariantCall { case Variant::VECTOR2: r_ret = reinterpret_cast<Transform2D *>(p_self._data._ptr)->xform_inv(p_args[0]->operator Vector2()); return; case Variant::RECT2: r_ret = reinterpret_cast<Transform2D *>(p_self._data._ptr)->xform_inv(p_args[0]->operator Rect2()); return; + case Variant::POOL_VECTOR2_ARRAY: r_ret = reinterpret_cast<Transform2D *>(p_self._data._ptr)->xform_inv(p_args[0]->operator PoolVector2Array()); return; default: r_ret = Variant(); } } @@ -773,7 +831,7 @@ struct _VariantCall { VCALL_PTR0R(Basis, get_orthogonal_index); VCALL_PTR0R(Basis, orthonormalized); VCALL_PTR2R(Basis, slerp); - VCALL_PTR2R(Basis, is_equal_approx); + VCALL_PTR2R(Basis, is_equal_approx); // TODO: Break compatibility in 4.0 to change this to an instance method (a.is_equal_approx(b) as VCALL_PTR1R) for consistency. VCALL_PTR0R(Basis, get_rotation_quat); VCALL_PTR0R(Transform, inverse); @@ -784,6 +842,7 @@ struct _VariantCall { VCALL_PTR0R(Transform, orthonormalized); VCALL_PTR2R(Transform, looking_at); VCALL_PTR2R(Transform, interpolate_with); + VCALL_PTR1R(Transform, is_equal_approx); static void _call_Transform_xform(Variant &r_ret, Variant &p_self, const Variant **p_args) { @@ -792,6 +851,7 @@ struct _VariantCall { case Variant::VECTOR3: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform(p_args[0]->operator Vector3()); return; case Variant::PLANE: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform(p_args[0]->operator Plane()); return; case Variant::AABB: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform(p_args[0]->operator ::AABB()); return; + case Variant::POOL_VECTOR3_ARRAY: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform(p_args[0]->operator ::PoolVector3Array()); return; default: r_ret = Variant(); } } @@ -803,6 +863,7 @@ struct _VariantCall { case Variant::VECTOR3: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform_inv(p_args[0]->operator Vector3()); return; case Variant::PLANE: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform_inv(p_args[0]->operator Plane()); return; case Variant::AABB: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform_inv(p_args[0]->operator ::AABB()); return; + case Variant::POOL_VECTOR3_ARRAY: r_ret = reinterpret_cast<Transform *>(p_self._data._ptr)->xform_inv(p_args[0]->operator ::PoolVector3Array()); return; default: r_ret = Variant(); } } @@ -891,7 +952,7 @@ struct _VariantCall { static void Quat_init2(Variant &r_ret, const Variant **p_args) { - r_ret = Quat(((Vector3)(*p_args[0])), ((float)(*p_args[1]))); + r_ret = Quat(((Vector3)(*p_args[0])), ((real_t)(*p_args[1]))); } static void Quat_init3(Variant &r_ret, const Variant **p_args) { @@ -1476,10 +1537,13 @@ void register_variant_methods() { ADDFUNC1R(STRING, INT, String, casecmp_to, STRING, "to", varray()); ADDFUNC1R(STRING, INT, String, nocasecmp_to, STRING, "to", varray()); ADDFUNC0R(STRING, INT, String, length, varray()); - ADDFUNC2R(STRING, STRING, String, substr, INT, "from", INT, "len", varray()); + ADDFUNC2R(STRING, STRING, String, substr, INT, "from", INT, "len", varray(-1)); ADDFUNC2R(STRING, INT, String, find, STRING, "what", INT, "from", varray(0)); + ADDFUNC3R(STRING, INT, String, count, STRING, "what", INT, "from", INT, "to", varray(0, 0)); + ADDFUNC3R(STRING, INT, String, countn, STRING, "what", INT, "from", INT, "to", varray(0, 0)); + ADDFUNC1R(STRING, INT, String, find_last, STRING, "what", varray()); ADDFUNC2R(STRING, INT, String, findn, STRING, "what", INT, "from", varray(0)); ADDFUNC2R(STRING, INT, String, rfind, STRING, "what", INT, "from", varray(-1)); @@ -1496,11 +1560,12 @@ void register_variant_methods() { ADDFUNC2R(STRING, STRING, String, format, NIL, "values", STRING, "placeholder", varray("{_}")); ADDFUNC2R(STRING, STRING, String, replace, STRING, "what", STRING, "forwhat", varray()); ADDFUNC2R(STRING, STRING, String, replacen, STRING, "what", STRING, "forwhat", varray()); + ADDFUNC1R(STRING, STRING, String, repeat, INT, "count", varray()); ADDFUNC2R(STRING, STRING, String, insert, INT, "position", STRING, "what", varray()); ADDFUNC0R(STRING, STRING, String, capitalize, varray()); - ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, split, STRING, "divisor", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0)); - ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, rsplit, STRING, "divisor", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0)); - ADDFUNC2R(STRING, POOL_REAL_ARRAY, String, split_floats, STRING, "divisor", BOOL, "allow_empty", varray(true)); + ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, split, STRING, "delimiter", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0)); + ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, rsplit, STRING, "delimiter", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0)); + ADDFUNC2R(STRING, POOL_REAL_ARRAY, String, split_floats, STRING, "delimiter", BOOL, "allow_empty", varray(true)); ADDFUNC0R(STRING, STRING, String, to_upper, varray()); ADDFUNC0R(STRING, STRING, String, to_lower, varray()); @@ -1508,6 +1573,7 @@ void register_variant_methods() { ADDFUNC1R(STRING, STRING, String, left, INT, "position", varray()); ADDFUNC1R(STRING, STRING, String, right, INT, "position", varray()); ADDFUNC2R(STRING, STRING, String, strip_edges, BOOL, "left", BOOL, "right", varray(true, true)); + ADDFUNC0R(STRING, STRING, String, strip_escapes, varray()); ADDFUNC1R(STRING, STRING, String, lstrip, STRING, "chars", varray()); ADDFUNC1R(STRING, STRING, String, rstrip, STRING, "chars", varray()); ADDFUNC0R(STRING, STRING, String, get_extension, varray()); @@ -1518,16 +1584,21 @@ void register_variant_methods() { ADDFUNC2(STRING, NIL, String, erase, INT, "position", INT, "chars", varray()); ADDFUNC0R(STRING, INT, String, hash, varray()); ADDFUNC0R(STRING, STRING, String, md5_text, varray()); + ADDFUNC0R(STRING, STRING, String, sha1_text, varray()); ADDFUNC0R(STRING, STRING, String, sha256_text, varray()); ADDFUNC0R(STRING, POOL_BYTE_ARRAY, String, md5_buffer, varray()); + ADDFUNC0R(STRING, POOL_BYTE_ARRAY, String, sha1_buffer, varray()); ADDFUNC0R(STRING, POOL_BYTE_ARRAY, String, sha256_buffer, varray()); ADDFUNC0R(STRING, BOOL, String, empty, varray()); + ADDFUNC1R(STRING, STRING, String, humanize_size, INT, "size", varray()); ADDFUNC0R(STRING, BOOL, String, is_abs_path, varray()); ADDFUNC0R(STRING, BOOL, String, is_rel_path, varray()); ADDFUNC0R(STRING, STRING, String, get_base_dir, varray()); ADDFUNC0R(STRING, STRING, String, get_file, varray()); ADDFUNC0R(STRING, STRING, String, xml_escape, varray()); ADDFUNC0R(STRING, STRING, String, xml_unescape, varray()); + ADDFUNC0R(STRING, STRING, String, http_escape, varray()); + ADDFUNC0R(STRING, STRING, String, http_unescape, varray()); ADDFUNC0R(STRING, STRING, String, c_escape, varray()); ADDFUNC0R(STRING, STRING, String, c_unescape, varray()); ADDFUNC0R(STRING, STRING, String, json_escape, varray()); @@ -1539,6 +1610,7 @@ void register_variant_methods() { ADDFUNC1R(STRING, BOOL, String, is_valid_hex_number, BOOL, "with_prefix", varray(false)); ADDFUNC0R(STRING, BOOL, String, is_valid_html_color, varray()); ADDFUNC0R(STRING, BOOL, String, is_valid_ip_address, varray()); + ADDFUNC0R(STRING, BOOL, String, is_valid_filename, varray()); ADDFUNC0R(STRING, INT, String, to_int, varray()); ADDFUNC0R(STRING, REAL, String, to_float, varray()); ADDFUNC0R(STRING, INT, String, hex_to_int, varray()); @@ -1550,19 +1622,24 @@ void register_variant_methods() { ADDFUNC0R(STRING, POOL_BYTE_ARRAY, String, to_ascii, varray()); ADDFUNC0R(STRING, POOL_BYTE_ARRAY, String, to_utf8, varray()); - ADDFUNC0R(VECTOR2, VECTOR2, Vector2, normalized, varray()); - ADDFUNC0R(VECTOR2, REAL, Vector2, length, varray()); ADDFUNC0R(VECTOR2, REAL, Vector2, angle, varray()); - ADDFUNC0R(VECTOR2, REAL, Vector2, length_squared, varray()); - ADDFUNC0R(VECTOR2, BOOL, Vector2, is_normalized, varray()); + ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to, VECTOR2, "to", varray()); + ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to_point, VECTOR2, "to", varray()); + ADDFUNC1R(VECTOR2, VECTOR2, Vector2, direction_to, VECTOR2, "b", varray()); ADDFUNC1R(VECTOR2, REAL, Vector2, distance_to, VECTOR2, "to", varray()); ADDFUNC1R(VECTOR2, REAL, Vector2, distance_squared_to, VECTOR2, "to", varray()); + ADDFUNC0R(VECTOR2, REAL, Vector2, length, varray()); + ADDFUNC0R(VECTOR2, REAL, Vector2, length_squared, varray()); + ADDFUNC0R(VECTOR2, VECTOR2, Vector2, normalized, varray()); + ADDFUNC0R(VECTOR2, BOOL, Vector2, is_normalized, varray()); + ADDFUNC1R(VECTOR2, BOOL, Vector2, is_equal_approx, VECTOR2, "v", varray()); + ADDFUNC1R(VECTOR2, VECTOR2, Vector2, posmod, REAL, "mod", varray()); + ADDFUNC1R(VECTOR2, VECTOR2, Vector2, posmodv, VECTOR2, "modv", varray()); ADDFUNC1R(VECTOR2, VECTOR2, Vector2, project, VECTOR2, "b", varray()); - ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to, VECTOR2, "to", varray()); - ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to_point, VECTOR2, "to", varray()); ADDFUNC2R(VECTOR2, VECTOR2, Vector2, linear_interpolate, VECTOR2, "b", REAL, "t", varray()); ADDFUNC2R(VECTOR2, VECTOR2, Vector2, slerp, VECTOR2, "b", REAL, "t", varray()); ADDFUNC4R(VECTOR2, VECTOR2, Vector2, cubic_interpolate, VECTOR2, "b", VECTOR2, "pre_a", VECTOR2, "post_b", REAL, "t", varray()); + ADDFUNC2R(VECTOR2, VECTOR2, Vector2, move_toward, VECTOR2, "to", REAL, "delta", varray()); ADDFUNC1R(VECTOR2, VECTOR2, Vector2, rotated, REAL, "phi", varray()); ADDFUNC0R(VECTOR2, VECTOR2, Vector2, tangent, varray()); ADDFUNC0R(VECTOR2, VECTOR2, Vector2, floor, varray()); @@ -1577,32 +1654,40 @@ void register_variant_methods() { ADDFUNC1R(VECTOR2, REAL, Vector2, cross, VECTOR2, "with", varray()); ADDFUNC0R(VECTOR2, VECTOR2, Vector2, abs, varray()); ADDFUNC1R(VECTOR2, VECTOR2, Vector2, clamped, REAL, "length", varray()); + ADDFUNC0R(VECTOR2, VECTOR2, Vector2, sign, varray()); ADDFUNC0R(RECT2, REAL, Rect2, get_area, varray()); + ADDFUNC0R(RECT2, BOOL, Rect2, has_no_area, varray()); + ADDFUNC1R(RECT2, BOOL, Rect2, has_point, VECTOR2, "point", varray()); + ADDFUNC1R(RECT2, BOOL, Rect2, is_equal_approx, RECT2, "rect", varray()); ADDFUNC1R(RECT2, BOOL, Rect2, intersects, RECT2, "b", varray()); ADDFUNC1R(RECT2, BOOL, Rect2, encloses, RECT2, "b", varray()); - ADDFUNC0R(RECT2, BOOL, Rect2, has_no_area, varray()); ADDFUNC1R(RECT2, RECT2, Rect2, clip, RECT2, "b", varray()); ADDFUNC1R(RECT2, RECT2, Rect2, merge, RECT2, "b", varray()); - ADDFUNC1R(RECT2, BOOL, Rect2, has_point, VECTOR2, "point", varray()); + ADDFUNC1R(RECT2, RECT2, Rect2, expand, VECTOR2, "to", varray()); ADDFUNC1R(RECT2, RECT2, Rect2, grow, REAL, "by", varray()); ADDFUNC2R(RECT2, RECT2, Rect2, grow_margin, INT, "margin", REAL, "by", varray()); ADDFUNC4R(RECT2, RECT2, Rect2, grow_individual, REAL, "left", REAL, "top", REAL, "right", REAL, " bottom", varray()); - ADDFUNC1R(RECT2, RECT2, Rect2, expand, VECTOR2, "to", varray()); ADDFUNC0R(RECT2, RECT2, Rect2, abs, varray()); ADDFUNC0R(VECTOR3, INT, Vector3, min_axis, varray()); ADDFUNC0R(VECTOR3, INT, Vector3, max_axis, varray()); + ADDFUNC1R(VECTOR3, REAL, Vector3, angle_to, VECTOR3, "to", varray()); + ADDFUNC1R(VECTOR3, VECTOR3, Vector3, direction_to, VECTOR3, "b", varray()); + ADDFUNC1R(VECTOR3, REAL, Vector3, distance_to, VECTOR3, "b", varray()); + ADDFUNC1R(VECTOR3, REAL, Vector3, distance_squared_to, VECTOR3, "b", varray()); ADDFUNC0R(VECTOR3, REAL, Vector3, length, varray()); ADDFUNC0R(VECTOR3, REAL, Vector3, length_squared, varray()); - ADDFUNC0R(VECTOR3, BOOL, Vector3, is_normalized, varray()); ADDFUNC0R(VECTOR3, VECTOR3, Vector3, normalized, varray()); + ADDFUNC0R(VECTOR3, BOOL, Vector3, is_normalized, varray()); + ADDFUNC1R(VECTOR3, BOOL, Vector3, is_equal_approx, VECTOR3, "v", varray()); ADDFUNC0R(VECTOR3, VECTOR3, Vector3, inverse, varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, snapped, VECTOR3, "by", varray()); ADDFUNC2R(VECTOR3, VECTOR3, Vector3, rotated, VECTOR3, "axis", REAL, "phi", varray()); ADDFUNC2R(VECTOR3, VECTOR3, Vector3, linear_interpolate, VECTOR3, "b", REAL, "t", varray()); ADDFUNC2R(VECTOR3, VECTOR3, Vector3, slerp, VECTOR3, "b", REAL, "t", varray()); ADDFUNC4R(VECTOR3, VECTOR3, Vector3, cubic_interpolate, VECTOR3, "b", VECTOR3, "pre_a", VECTOR3, "post_b", REAL, "t", varray()); + ADDFUNC2R(VECTOR3, VECTOR3, Vector3, move_toward, VECTOR3, "to", REAL, "delta", varray()); ADDFUNC1R(VECTOR3, REAL, Vector3, dot, VECTOR3, "b", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, cross, VECTOR3, "b", varray()); ADDFUNC1R(VECTOR3, BASIS, Vector3, outer, VECTOR3, "b", varray()); @@ -1611,17 +1696,18 @@ void register_variant_methods() { ADDFUNC0R(VECTOR3, VECTOR3, Vector3, floor, varray()); ADDFUNC0R(VECTOR3, VECTOR3, Vector3, ceil, varray()); ADDFUNC0R(VECTOR3, VECTOR3, Vector3, round, varray()); - ADDFUNC1R(VECTOR3, REAL, Vector3, distance_to, VECTOR3, "b", varray()); - ADDFUNC1R(VECTOR3, REAL, Vector3, distance_squared_to, VECTOR3, "b", varray()); + ADDFUNC1R(VECTOR3, VECTOR3, Vector3, posmod, REAL, "mod", varray()); + ADDFUNC1R(VECTOR3, VECTOR3, Vector3, posmodv, VECTOR3, "modv", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, project, VECTOR3, "b", varray()); - ADDFUNC1R(VECTOR3, REAL, Vector3, angle_to, VECTOR3, "to", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, slide, VECTOR3, "n", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, bounce, VECTOR3, "n", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, reflect, VECTOR3, "n", varray()); + ADDFUNC0R(VECTOR3, VECTOR3, Vector3, sign, varray()); ADDFUNC0R(PLANE, PLANE, Plane, normalized, varray()); ADDFUNC0R(PLANE, VECTOR3, Plane, center, varray()); ADDFUNC0R(PLANE, VECTOR3, Plane, get_any_point, varray()); + ADDFUNC1R(PLANE, BOOL, Plane, is_equal_approx, PLANE, "plane", varray()); ADDFUNC1R(PLANE, BOOL, Plane, is_point_over, VECTOR3, "point", varray()); ADDFUNC1R(PLANE, REAL, Plane, distance_to, VECTOR3, "point", varray()); ADDFUNC2R(PLANE, BOOL, Plane, has_point, VECTOR3, "point", REAL, "epsilon", varray(CMP_EPSILON)); @@ -1634,6 +1720,7 @@ void register_variant_methods() { ADDFUNC0R(QUAT, REAL, Quat, length_squared, varray()); ADDFUNC0R(QUAT, QUAT, Quat, normalized, varray()); ADDFUNC0R(QUAT, BOOL, Quat, is_normalized, varray()); + ADDFUNC1R(QUAT, BOOL, Quat, is_equal_approx, QUAT, "quat", varray()); ADDFUNC0R(QUAT, QUAT, Quat, inverse, varray()); ADDFUNC1R(QUAT, REAL, Quat, dot, QUAT, "b", varray()); ADDFUNC1R(QUAT, VECTOR3, Quat, xform, VECTOR3, "v", varray()); @@ -1659,6 +1746,7 @@ void register_variant_methods() { ADDFUNC1R(COLOR, COLOR, Color, darkened, REAL, "amount", varray()); ADDFUNC1R(COLOR, STRING, Color, to_html, BOOL, "with_alpha", varray(true)); ADDFUNC4R(COLOR, COLOR, Color, from_hsv, REAL, "h", REAL, "s", REAL, "v", REAL, "a", varray(1.0)); + ADDFUNC1R(COLOR, BOOL, Color, is_equal_approx, COLOR, "color", varray()); ADDFUNC0R(_RID, INT, RID, get_id, varray()); @@ -1710,10 +1798,12 @@ void register_variant_methods() { ADDFUNC4R(ARRAY, INT, Array, bsearch_custom, NIL, "value", OBJECT, "obj", STRING, "func", BOOL, "before", varray(true)); ADDFUNC0NC(ARRAY, NIL, Array, invert, varray()); ADDFUNC1R(ARRAY, ARRAY, Array, duplicate, BOOL, "deep", varray(false)); + ADDFUNC4R(ARRAY, ARRAY, Array, slice, INT, "begin", INT, "end", INT, "step", BOOL, "deep", varray(1, false)); ADDFUNC0R(ARRAY, NIL, Array, max, varray()); ADDFUNC0R(ARRAY, NIL, Array, min, varray()); ADDFUNC0R(POOL_BYTE_ARRAY, INT, PoolByteArray, size, varray()); + ADDFUNC0R(POOL_BYTE_ARRAY, BOOL, PoolByteArray, empty, varray()); ADDFUNC2(POOL_BYTE_ARRAY, NIL, PoolByteArray, set, INT, "idx", INT, "byte", varray()); ADDFUNC1(POOL_BYTE_ARRAY, NIL, PoolByteArray, push_back, INT, "byte", varray()); ADDFUNC1(POOL_BYTE_ARRAY, NIL, PoolByteArray, append, INT, "byte", varray()); @@ -1726,10 +1816,12 @@ void register_variant_methods() { ADDFUNC0R(POOL_BYTE_ARRAY, STRING, PoolByteArray, get_string_from_ascii, varray()); ADDFUNC0R(POOL_BYTE_ARRAY, STRING, PoolByteArray, get_string_from_utf8, varray()); + ADDFUNC0R(POOL_BYTE_ARRAY, STRING, PoolByteArray, hex_encode, varray()); ADDFUNC1R(POOL_BYTE_ARRAY, POOL_BYTE_ARRAY, PoolByteArray, compress, INT, "compression_mode", varray(0)); ADDFUNC2R(POOL_BYTE_ARRAY, POOL_BYTE_ARRAY, PoolByteArray, decompress, INT, "buffer_size", INT, "compression_mode", varray(0)); ADDFUNC0R(POOL_INT_ARRAY, INT, PoolIntArray, size, varray()); + ADDFUNC0R(POOL_INT_ARRAY, BOOL, PoolIntArray, empty, varray()); ADDFUNC2(POOL_INT_ARRAY, NIL, PoolIntArray, set, INT, "idx", INT, "integer", varray()); ADDFUNC1(POOL_INT_ARRAY, NIL, PoolIntArray, push_back, INT, "integer", varray()); ADDFUNC1(POOL_INT_ARRAY, NIL, PoolIntArray, append, INT, "integer", varray()); @@ -1740,6 +1832,7 @@ void register_variant_methods() { ADDFUNC0(POOL_INT_ARRAY, NIL, PoolIntArray, invert, varray()); ADDFUNC0R(POOL_REAL_ARRAY, INT, PoolRealArray, size, varray()); + ADDFUNC0R(POOL_REAL_ARRAY, BOOL, PoolRealArray, empty, varray()); ADDFUNC2(POOL_REAL_ARRAY, NIL, PoolRealArray, set, INT, "idx", REAL, "value", varray()); ADDFUNC1(POOL_REAL_ARRAY, NIL, PoolRealArray, push_back, REAL, "value", varray()); ADDFUNC1(POOL_REAL_ARRAY, NIL, PoolRealArray, append, REAL, "value", varray()); @@ -1750,6 +1843,7 @@ void register_variant_methods() { ADDFUNC0(POOL_REAL_ARRAY, NIL, PoolRealArray, invert, varray()); ADDFUNC0R(POOL_STRING_ARRAY, INT, PoolStringArray, size, varray()); + ADDFUNC0R(POOL_STRING_ARRAY, BOOL, PoolStringArray, empty, varray()); ADDFUNC2(POOL_STRING_ARRAY, NIL, PoolStringArray, set, INT, "idx", STRING, "string", varray()); ADDFUNC1(POOL_STRING_ARRAY, NIL, PoolStringArray, push_back, STRING, "string", varray()); ADDFUNC1(POOL_STRING_ARRAY, NIL, PoolStringArray, append, STRING, "string", varray()); @@ -1761,6 +1855,7 @@ void register_variant_methods() { ADDFUNC1(POOL_STRING_ARRAY, STRING, PoolStringArray, join, STRING, "delimiter", varray()); ADDFUNC0R(POOL_VECTOR2_ARRAY, INT, PoolVector2Array, size, varray()); + ADDFUNC0R(POOL_VECTOR2_ARRAY, BOOL, PoolVector2Array, empty, varray()); ADDFUNC2(POOL_VECTOR2_ARRAY, NIL, PoolVector2Array, set, INT, "idx", VECTOR2, "vector2", varray()); ADDFUNC1(POOL_VECTOR2_ARRAY, NIL, PoolVector2Array, push_back, VECTOR2, "vector2", varray()); ADDFUNC1(POOL_VECTOR2_ARRAY, NIL, PoolVector2Array, append, VECTOR2, "vector2", varray()); @@ -1771,6 +1866,7 @@ void register_variant_methods() { ADDFUNC0(POOL_VECTOR2_ARRAY, NIL, PoolVector2Array, invert, varray()); ADDFUNC0R(POOL_VECTOR3_ARRAY, INT, PoolVector3Array, size, varray()); + ADDFUNC0R(POOL_VECTOR3_ARRAY, BOOL, PoolVector3Array, empty, varray()); ADDFUNC2(POOL_VECTOR3_ARRAY, NIL, PoolVector3Array, set, INT, "idx", VECTOR3, "vector3", varray()); ADDFUNC1(POOL_VECTOR3_ARRAY, NIL, PoolVector3Array, push_back, VECTOR3, "vector3", varray()); ADDFUNC1(POOL_VECTOR3_ARRAY, NIL, PoolVector3Array, append, VECTOR3, "vector3", varray()); @@ -1781,6 +1877,7 @@ void register_variant_methods() { ADDFUNC0(POOL_VECTOR3_ARRAY, NIL, PoolVector3Array, invert, varray()); ADDFUNC0R(POOL_COLOR_ARRAY, INT, PoolColorArray, size, varray()); + ADDFUNC0R(POOL_COLOR_ARRAY, BOOL, PoolColorArray, empty, varray()); ADDFUNC2(POOL_COLOR_ARRAY, NIL, PoolColorArray, set, INT, "idx", COLOR, "color", varray()); ADDFUNC1(POOL_COLOR_ARRAY, NIL, PoolColorArray, push_back, COLOR, "color", varray()); ADDFUNC1(POOL_COLOR_ARRAY, NIL, PoolColorArray, append, COLOR, "color", varray()); @@ -1795,13 +1892,16 @@ void register_variant_methods() { ADDFUNC0R(AABB, REAL, AABB, get_area, varray()); ADDFUNC0R(AABB, BOOL, AABB, has_no_area, varray()); ADDFUNC0R(AABB, BOOL, AABB, has_no_surface, varray()); + ADDFUNC1R(AABB, BOOL, AABB, has_point, VECTOR3, "point", varray()); + ADDFUNC1R(AABB, BOOL, AABB, is_equal_approx, AABB, "aabb", varray()); ADDFUNC1R(AABB, BOOL, AABB, intersects, AABB, "with", varray()); ADDFUNC1R(AABB, BOOL, AABB, encloses, AABB, "with", varray()); - ADDFUNC1R(AABB, AABB, AABB, merge, AABB, "with", varray()); - ADDFUNC1R(AABB, AABB, AABB, intersection, AABB, "with", varray()); ADDFUNC1R(AABB, BOOL, AABB, intersects_plane, PLANE, "plane", varray()); ADDFUNC2R(AABB, BOOL, AABB, intersects_segment, VECTOR3, "from", VECTOR3, "to", varray()); - ADDFUNC1R(AABB, BOOL, AABB, has_point, VECTOR3, "point", varray()); + ADDFUNC1R(AABB, AABB, AABB, intersection, AABB, "with", varray()); + ADDFUNC1R(AABB, AABB, AABB, merge, AABB, "with", varray()); + ADDFUNC1R(AABB, AABB, AABB, expand, VECTOR3, "to_point", varray()); + ADDFUNC1R(AABB, AABB, AABB, grow, REAL, "by", varray()); ADDFUNC1R(AABB, VECTOR3, AABB, get_support, VECTOR3, "dir", varray()); ADDFUNC0R(AABB, VECTOR3, AABB, get_longest_axis, varray()); ADDFUNC0R(AABB, INT, AABB, get_longest_axis_index, varray()); @@ -1809,8 +1909,6 @@ void register_variant_methods() { ADDFUNC0R(AABB, VECTOR3, AABB, get_shortest_axis, varray()); ADDFUNC0R(AABB, INT, AABB, get_shortest_axis_index, varray()); ADDFUNC0R(AABB, REAL, AABB, get_shortest_axis_size, varray()); - ADDFUNC1R(AABB, AABB, AABB, expand, VECTOR3, "to_point", varray()); - ADDFUNC1R(AABB, AABB, AABB, grow, REAL, "by", varray()); ADDFUNC1R(AABB, VECTOR3, AABB, get_endpoint, INT, "idx", varray()); ADDFUNC0R(TRANSFORM2D, TRANSFORM2D, Transform2D, inverse, varray()); @@ -1827,6 +1925,7 @@ void register_variant_methods() { ADDFUNC1R(TRANSFORM2D, VECTOR2, Transform2D, basis_xform, VECTOR2, "v", varray()); ADDFUNC1R(TRANSFORM2D, VECTOR2, Transform2D, basis_xform_inv, VECTOR2, "v", varray()); ADDFUNC2R(TRANSFORM2D, TRANSFORM2D, Transform2D, interpolate_with, TRANSFORM2D, "transform", REAL, "weight", varray()); + ADDFUNC1R(TRANSFORM2D, BOOL, Transform2D, is_equal_approx, TRANSFORM2D, "transform", varray()); ADDFUNC0R(BASIS, BASIS, Basis, inverse, varray()); ADDFUNC0R(BASIS, BASIS, Basis, transposed, varray()); @@ -1843,7 +1942,7 @@ void register_variant_methods() { ADDFUNC1R(BASIS, VECTOR3, Basis, xform_inv, VECTOR3, "v", varray()); ADDFUNC0R(BASIS, INT, Basis, get_orthogonal_index, varray()); ADDFUNC2R(BASIS, BASIS, Basis, slerp, BASIS, "b", REAL, "t", varray()); - ADDFUNC2R(BASIS, BOOL, Basis, is_equal_approx, BASIS, "b", REAL, "epsilon", varray(CMP_EPSILON)); + ADDFUNC2R(BASIS, BOOL, Basis, is_equal_approx, BASIS, "b", REAL, "epsilon", varray(CMP_EPSILON)); // TODO: Replace in 4.0, see other TODO. ADDFUNC0R(BASIS, QUAT, Basis, get_rotation_quat, varray()); ADDFUNC0R(TRANSFORM, TRANSFORM, Transform, inverse, varray()); @@ -1854,6 +1953,7 @@ void register_variant_methods() { ADDFUNC1R(TRANSFORM, TRANSFORM, Transform, translated, VECTOR3, "ofs", varray()); ADDFUNC2R(TRANSFORM, TRANSFORM, Transform, looking_at, VECTOR3, "target", VECTOR3, "up", varray()); ADDFUNC2R(TRANSFORM, TRANSFORM, Transform, interpolate_with, TRANSFORM, "transform", REAL, "weight", varray()); + ADDFUNC1R(TRANSFORM, BOOL, Transform, is_equal_approx, TRANSFORM, "transform", varray()); ADDFUNC1R(TRANSFORM, NIL, Transform, xform, NIL, "v", varray()); ADDFUNC1R(TRANSFORM, NIL, Transform, xform_inv, NIL, "v", varray()); @@ -1909,6 +2009,9 @@ void register_variant_methods() { _VariantCall::add_variant_constant(Variant::VECTOR3, "FORWARD", Vector3(0, 0, -1)); _VariantCall::add_variant_constant(Variant::VECTOR3, "BACK", Vector3(0, 0, 1)); + _VariantCall::add_constant(Variant::VECTOR2, "AXIS_X", Vector2::AXIS_X); + _VariantCall::add_constant(Variant::VECTOR2, "AXIS_Y", Vector2::AXIS_Y); + _VariantCall::add_variant_constant(Variant::VECTOR2, "ZERO", Vector2(0, 0)); _VariantCall::add_variant_constant(Variant::VECTOR2, "ONE", Vector2(1, 1)); _VariantCall::add_variant_constant(Variant::VECTOR2, "INF", Vector2(Math_INF, Math_INF)); @@ -1917,19 +2020,27 @@ void register_variant_methods() { _VariantCall::add_variant_constant(Variant::VECTOR2, "UP", Vector2(0, -1)); _VariantCall::add_variant_constant(Variant::VECTOR2, "DOWN", Vector2(0, 1)); - _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "IDENTITY", Transform2D(1, 0, 0, 1, 0, 0)); + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "IDENTITY", Transform2D()); _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_X", Transform2D(-1, 0, 0, 1, 0, 0)); _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_Y", Transform2D(1, 0, 0, -1, 0, 0)); - Transform identity_transform, transform_x, transform_y, transform_z; - identity_transform.set(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); + Transform identity_transform = Transform(); + Transform flip_x_transform = Transform(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); + Transform flip_y_transform = Transform(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0); + Transform flip_z_transform = Transform(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0); _VariantCall::add_variant_constant(Variant::TRANSFORM, "IDENTITY", identity_transform); - transform_x.set(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); - _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_X", transform_x); - transform_y.set(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0); - _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Y", transform_y); - transform_z.set(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0); - _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Z", transform_z); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_X", flip_x_transform); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Y", flip_y_transform); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Z", flip_z_transform); + + Basis identity_basis = Basis(); + Basis flip_x_basis = Basis(-1, 0, 0, 0, 1, 0, 0, 0, 1); + Basis flip_y_basis = Basis(1, 0, 0, 0, -1, 0, 0, 0, 1); + Basis flip_z_basis = Basis(1, 0, 0, 0, 1, 0, 0, 0, -1); + _VariantCall::add_variant_constant(Variant::BASIS, "IDENTITY", identity_basis); + _VariantCall::add_variant_constant(Variant::BASIS, "FLIP_X", flip_x_basis); + _VariantCall::add_variant_constant(Variant::BASIS, "FLIP_Y", flip_y_basis); + _VariantCall::add_variant_constant(Variant::BASIS, "FLIP_Z", flip_z_basis); _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_YZ", Plane(Vector3(1, 0, 0), 0)); _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_XZ", Plane(Vector3(0, 1, 0), 0)); diff --git a/core/variant_op.cpp b/core/variant_op.cpp index b40b6ce4a6..ea9e29e744 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -2183,7 +2183,8 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid) return; } - return obj->set(p_index, p_value, r_valid); + obj->set(p_index, p_value, r_valid); + return; } } break; case DICTIONARY: { @@ -2612,7 +2613,7 @@ bool Variant::in(const Variant &p_index, bool *r_valid) const { if (r_valid) { *r_valid = false; } - return "Attempted get on stray pointer."; + return true; // Attempted get on stray pointer. } } #endif @@ -2781,7 +2782,8 @@ bool Variant::in(const Variant &p_index, bool *r_valid) const { return false; } } break; - default: {} + default: { + } } if (r_valid) @@ -2912,7 +2914,8 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const { //nothing } break; - default: {} + default: { + } } } @@ -3251,7 +3254,8 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const { r_iter = idx; return true; } break; - default: {} + default: { + } } valid = false; @@ -3408,7 +3412,8 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const { #endif return arr->get(idx); } break; - default: {} + default: { + } } r_valid = false; diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp index 0056fc75b6..fe2c981c3c 100644 --- a/core/variant_parser.cpp +++ b/core/variant_parser.cpp @@ -436,8 +436,6 @@ Error VariantParser::_parse_enginecfg(Stream *p_stream, Vector<String> &strings, line++; } } - - return OK; } template <class T> @@ -799,8 +797,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } } - return OK; - } else if (id == "Resource" || id == "SubResource" || id == "ExtResource") { get_token(p_stream, token, line, r_err_str); @@ -864,8 +860,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, return ERR_PARSE_ERROR; } } - - return OK; #ifndef DISABLE_DEPRECATED } else if (id == "InputEvent") { @@ -1256,8 +1250,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, r_err_str = "Expected value, got " + String(tk_name[token.type]) + "."; return ERR_PARSE_ERROR; } - - return ERR_PARSE_ERROR; } Error VariantParser::_parse_array(Array &array, Stream *p_stream, int &line, String &r_err_str, ResourceParser *p_res_parser) { @@ -1301,8 +1293,6 @@ Error VariantParser::_parse_array(Array &array, Stream *p_stream, int &line, Str array.push_back(v); need_comma = true; } - - return OK; } Error VariantParser::_parse_dictionary(Dictionary &object, Stream *p_stream, int &line, String &r_err_str, ResourceParser *p_res_parser) { @@ -1372,8 +1362,6 @@ Error VariantParser::_parse_dictionary(Dictionary &object, Stream *p_stream, int at_key = true; } } - - return OK; } Error VariantParser::_parse_tag(Token &token, Stream *p_stream, int &line, String &r_err_str, Tag &r_tag, ResourceParser *p_res_parser, bool p_simple_tag) { @@ -1534,7 +1522,7 @@ Error VariantParser::parse_tag_assign_eof(Stream *p_stream, int &line, String &r return err; if (tk.type != TK_STRING) { r_err_str = "Error reading quoted string"; - return err; + return ERR_INVALID_DATA; } what = tk.value; @@ -1542,20 +1530,19 @@ Error VariantParser::parse_tag_assign_eof(Stream *p_stream, int &line, String &r } else if (c != '=') { what += String::chr(c); } else { + if (p_stream->is_utf8()) { + what.parse_utf8(what.ascii(true).get_data()); + } r_assign = what; Token token; get_token(p_stream, token, line, r_err_str); Error err = parse_value(token, r_value, p_stream, line, r_err_str, p_res_parser); - if (err) { - } return err; } } else if (c == '\n') { line++; } } - - return OK; } Error VariantParser::parse(Stream *p_stream, Variant &r_ret, String &r_err_str, int &r_err_line, ResourceParser *p_res_parser) { @@ -1948,7 +1935,8 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, " )"); } break; - default: {} + default: { + } } return OK; diff --git a/core/vector.h b/core/vector.h index 93ee003519..e6bb4a96fc 100644 --- a/core/vector.h +++ b/core/vector.h @@ -150,7 +150,7 @@ template <class T> bool Vector<T>::push_back(const T &p_elem) { Error err = resize(size() + 1); - ERR_FAIL_COND_V(err, true) + ERR_FAIL_COND_V(err, true); set(size() - 1, p_elem); return false; diff --git a/core/vmap.h b/core/vmap.h index f46ed70a0f..ed66b46993 100644 --- a/core/vmap.h +++ b/core/vmap.h @@ -65,7 +65,7 @@ private: const Pair *a = _cowdata.ptr(); int middle = 0; -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED if (low > high) ERR_PRINT("low > high, this may be a bug"); #endif @@ -196,8 +196,7 @@ public: int pos = _find_exact(p_key); if (pos < 0) { - V val; - pos = insert(p_key, val); + pos = insert(p_key, V()); } return _cowdata.get_m(pos).value; diff --git a/core/vset.h b/core/vset.h index 678ec507ba..5f087a5a03 100644 --- a/core/vset.h +++ b/core/vset.h @@ -50,7 +50,7 @@ class VSet { const T *a = &_data[0]; int middle = 0; -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED if (low > high) ERR_PRINT("low > high, this may be a bug"); #endif |