diff --git a/godot/.github/workflows/linux_builds.yml b/godot/.github/workflows/linux_builds.yml index dc3d9f378..348e2bb31 100644 --- a/godot/.github/workflows/linux_builds.yml +++ b/godot/.github/workflows/linux_builds.yml @@ -106,7 +106,7 @@ jobs: # TODO: Figure out somehow how to embed this one. - name: wayland-scanner dependency run: | - sudo apt-get install libwayland-bin + sudo apt-get install libwayland-bin libegl-dev - name: Free disk space on runner run: | diff --git a/godot/.gitrepo b/godot/.gitrepo index 7e1bc3d59..fd10b4a95 100644 --- a/godot/.gitrepo +++ b/godot/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/V-Sekai/godot.git branch = groups-staging-4.4 - commit = 72728eab208b88681e4fe134e6803fd0c221338a - parent = 36f0f607371ee76888b532cf09ab705cfeca3267 + commit = 718319725c5006aa8ff459139cf800411a7df2fa + parent = 750c80d93ae2ce070eb5695ecc8cf5d9d1edecaf method = merge cmdver = 0.4.6 diff --git a/godot/COPYRIGHT.txt b/godot/COPYRIGHT.txt index 6d227d661..ecf9ed0f4 100644 --- a/godot/COPYRIGHT.txt +++ b/godot/COPYRIGHT.txt @@ -341,7 +341,7 @@ License: Apache-2.0 Files: ./thirdparty/meshoptimizer/ Comment: meshoptimizer -Copyright: 2016-2023, Arseny Kapoulkine +Copyright: 2016-2024, Arseny Kapoulkine License: Expat Files: ./thirdparty/mingw-std-threads/ diff --git a/godot/core/config/engine.cpp b/godot/core/config/engine.cpp index d77c91331..9db2e6fbe 100644 --- a/godot/core/config/engine.cpp +++ b/godot/core/config/engine.cpp @@ -267,6 +267,12 @@ bool Engine::is_extra_gpu_memory_tracking_enabled() const { return extra_gpu_memory_tracking; } +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) +bool Engine::is_accurate_breadcrumbs_enabled() const { + return accurate_breadcrumbs; +} +#endif + void Engine::set_print_to_stdout(bool p_enabled) { CoreGlobals::print_line_enabled = p_enabled; } diff --git a/godot/core/config/engine.h b/godot/core/config/engine.h index a0b1ffa98..fd7fbd717 100644 --- a/godot/core/config/engine.h +++ b/godot/core/config/engine.h @@ -73,6 +73,9 @@ class Engine { bool use_validation_layers = false; bool generate_spirv_debug_info = false; bool extra_gpu_memory_tracking = false; +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) + bool accurate_breadcrumbs = false; +#endif int32_t gpu_idx = -1; uint64_t _process_frames = 0; @@ -186,6 +189,9 @@ class Engine { bool is_validation_layers_enabled() const; bool is_generate_spirv_debug_info_enabled() const; bool is_extra_gpu_memory_tracking_enabled() const; +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) + bool is_accurate_breadcrumbs_enabled() const; +#endif int32_t get_gpu_index() const; void increment_frames_drawn(); diff --git a/godot/core/core_bind.cpp b/godot/core/core_bind.cpp index e360a25ca..28088cf6b 100644 --- a/godot/core/core_bind.cpp +++ b/godot/core/core_bind.cpp @@ -934,6 +934,19 @@ Dictionary Geometry2D::make_atlas(const Vector &p_rects) { return ret; } +TypedArray Geometry2D::bresenham_line(const Point2i &p_from, const Point2i &p_to) { + Vector points = ::Geometry2D::bresenham_line(p_from, p_to); + + TypedArray result; + result.resize(points.size()); + + for (int i = 0; i < points.size(); i++) { + result[i] = points[i]; + } + + return result; +} + void Geometry2D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_point_in_circle", "point", "circle_position", "circle_radius"), &Geometry2D::is_point_in_circle); ClassDB::bind_method(D_METHOD("segment_intersects_circle", "segment_from", "segment_to", "circle_position", "circle_radius"), &Geometry2D::segment_intersects_circle); @@ -968,6 +981,8 @@ void Geometry2D::_bind_methods() { ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &Geometry2D::make_atlas); + ClassDB::bind_method(D_METHOD("bresenham_line", "from", "to"), &Geometry2D::bresenham_line); + BIND_ENUM_CONSTANT(OPERATION_UNION); BIND_ENUM_CONSTANT(OPERATION_DIFFERENCE); BIND_ENUM_CONSTANT(OPERATION_INTERSECTION); diff --git a/godot/core/core_bind.h b/godot/core/core_bind.h index 2d43bd376..d3a352aed 100644 --- a/godot/core/core_bind.h +++ b/godot/core/core_bind.h @@ -326,6 +326,8 @@ class Geometry2D : public Object { Dictionary make_atlas(const Vector &p_rects); + TypedArray bresenham_line(const Point2i &p_from, const Point2i &p_to); + Geometry2D() { singleton = this; } }; diff --git a/godot/core/error/error_macros.cpp b/godot/core/error/error_macros.cpp index 813ee7684..a2369992e 100644 --- a/godot/core/error/error_macros.cpp +++ b/godot/core/error/error_macros.cpp @@ -107,6 +107,28 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co _global_unlock(); } +// For printing errors when we may crash at any point, so we must flush ASAP a lot of lines +// but we don't want to make it noisy by printing lots of file & line info (because it's already +// been printing by a preceding _err_print_error). +void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type) { + if (OS::get_singleton()) { + OS::get_singleton()->printerr("ERROR: %s\n", p_error.utf8().get_data()); + } else { + // Fallback if errors happen before OS init or after it's destroyed. + const char *err_details = p_error.utf8().get_data(); + fprintf(stderr, "ERROR: %s\n", err_details); + } + + _global_lock(); + ErrorHandlerList *l = error_handler_list; + while (l) { + l->errfunc(l->userdata, "", "", 0, p_error.utf8().get_data(), "", false, p_type); + l = l->next; + } + + _global_unlock(); +} + // Errors with message. (All combinations of p_error and p_message as String or char*.) void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) { _err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message, p_editor_notify, p_type); diff --git a/godot/core/error/error_macros.h b/godot/core/error/error_macros.h index 19c16667d..752fd605e 100644 --- a/godot/core/error/error_macros.h +++ b/godot/core/error/error_macros.h @@ -68,6 +68,7 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify = false, 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, bool p_editor_notify = false, 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, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +void _err_print_error_asap(const String &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, const char *p_message = "", bool p_editor_notify = false, 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 p_editor_notify = false, bool fatal = false); void _err_flush_stdout(); diff --git a/godot/core/math/geometry_2d.h b/godot/core/math/geometry_2d.h index 83ebdc5a8..abd395d8d 100644 --- a/godot/core/math/geometry_2d.h +++ b/godot/core/math/geometry_2d.h @@ -451,17 +451,17 @@ class Geometry2D { return H; } - static Vector bresenham_line(const Point2i &p_start, const Point2i &p_end) { + static Vector bresenham_line(const Point2i &p_from, const Point2i &p_to) { Vector points; - Vector2i delta = (p_end - p_start).abs() * 2; - Vector2i step = (p_end - p_start).sign(); - Vector2i current = p_start; + Vector2i delta = (p_to - p_from).abs() * 2; + Vector2i step = (p_to - p_from).sign(); + Vector2i current = p_from; if (delta.x > delta.y) { int err = delta.x / 2; - for (; current.x != p_end.x; current.x += step.x) { + for (; current.x != p_to.x; current.x += step.x) { points.push_back(current); err -= delta.y; @@ -473,7 +473,7 @@ class Geometry2D { } else { int err = delta.y / 2; - for (; current.y != p_end.y; current.y += step.y) { + for (; current.y != p_to.y; current.y += step.y) { points.push_back(current); err -= delta.x; diff --git a/godot/core/string/char_range.inc b/godot/core/string/char_range.inc index 2b081b96d..efae75780 100644 --- a/godot/core/string/char_range.inc +++ b/godot/core/string/char_range.inc @@ -33,14 +33,17 @@ #include "core/typedefs.h" +// Unicode Derived Core Properties +// Source: https://www.unicode.org/Public/16.0.0/ucd/DerivedCoreProperties.txt + struct CharRange { char32_t start; char32_t end; }; -inline constexpr CharRange xid_start[] = { +constexpr inline CharRange xid_start[] = { { 0x41, 0x5a }, - { 0x5f, 0x5f }, + { 0x5f, 0x5f }, // Underscore technically isn't in XID_Start, but for our purposes it's included. { 0x61, 0x7a }, { 0xaa, 0xaa }, { 0xb5, 0xb5 }, @@ -54,7 +57,7 @@ inline constexpr CharRange xid_start[] = { { 0x2ee, 0x2ee }, { 0x370, 0x374 }, { 0x376, 0x377 }, - { 0x37a, 0x37d }, + { 0x37b, 0x37d }, { 0x37f, 0x37f }, { 0x386, 0x386 }, { 0x388, 0x38a }, @@ -182,7 +185,7 @@ inline constexpr CharRange xid_start[] = { { 0xdbd, 0xdbd }, { 0xdc0, 0xdc6 }, { 0xe01, 0xe30 }, - { 0xe32, 0xe33 }, + { 0xe32, 0xe32 }, { 0xe40, 0xe46 }, { 0xe81, 0xe82 }, { 0xe84, 0xe84 }, @@ -190,7 +193,7 @@ inline constexpr CharRange xid_start[] = { { 0xe8c, 0xea3 }, { 0xea5, 0xea5 }, { 0xea7, 0xeb0 }, - { 0xeb2, 0xeb3 }, + { 0xeb2, 0xeb2 }, { 0xebd, 0xebd }, { 0xec0, 0xec4 }, { 0xec6, 0xec6 }, @@ -245,8 +248,7 @@ inline constexpr CharRange xid_start[] = { { 0x17d7, 0x17d7 }, { 0x17dc, 0x17dc }, { 0x1820, 0x1878 }, - { 0x1880, 0x1884 }, - { 0x1887, 0x18a8 }, + { 0x1880, 0x18a8 }, { 0x18aa, 0x18aa }, { 0x18b0, 0x18f5 }, { 0x1900, 0x191e }, @@ -265,7 +267,7 @@ inline constexpr CharRange xid_start[] = { { 0x1c00, 0x1c23 }, { 0x1c4d, 0x1c4f }, { 0x1c5a, 0x1c7d }, - { 0x1c80, 0x1c88 }, + { 0x1c80, 0x1c8a }, { 0x1c90, 0x1cba }, { 0x1cbd, 0x1cbf }, { 0x1ce9, 0x1cec }, @@ -330,7 +332,7 @@ inline constexpr CharRange xid_start[] = { { 0x3031, 0x3035 }, { 0x3038, 0x303c }, { 0x3041, 0x3096 }, - { 0x309b, 0x309f }, + { 0x309d, 0x309f }, { 0x30a1, 0x30fa }, { 0x30fc, 0x30ff }, { 0x3105, 0x312f }, @@ -348,10 +350,10 @@ inline constexpr CharRange xid_start[] = { { 0xa6a0, 0xa6ef }, { 0xa717, 0xa71f }, { 0xa722, 0xa788 }, - { 0xa78b, 0xa7ca }, + { 0xa78b, 0xa7cd }, { 0xa7d0, 0xa7d1 }, { 0xa7d3, 0xa7d3 }, - { 0xa7d5, 0xa7d9 }, + { 0xa7d5, 0xa7dc }, { 0xa7f2, 0xa801 }, { 0xa803, 0xa805 }, { 0xa807, 0xa80a }, @@ -406,15 +408,22 @@ inline constexpr CharRange xid_start[] = { { 0xfb40, 0xfb41 }, { 0xfb43, 0xfb44 }, { 0xfb46, 0xfbb1 }, - { 0xfbd3, 0xfd3d }, + { 0xfbd3, 0xfc5d }, + { 0xfc64, 0xfd3d }, { 0xfd50, 0xfd8f }, { 0xfd92, 0xfdc7 }, - { 0xfdf0, 0xfdfb }, - { 0xfe70, 0xfe74 }, - { 0xfe76, 0xfefc }, + { 0xfdf0, 0xfdf9 }, + { 0xfe71, 0xfe71 }, + { 0xfe73, 0xfe73 }, + { 0xfe77, 0xfe77 }, + { 0xfe79, 0xfe79 }, + { 0xfe7b, 0xfe7b }, + { 0xfe7d, 0xfe7d }, + { 0xfe7f, 0xfefc }, { 0xff21, 0xff3a }, { 0xff41, 0xff5a }, - { 0xff66, 0xffbe }, + { 0xff66, 0xff9d }, + { 0xffa0, 0xffbe }, { 0xffc2, 0xffc7 }, { 0xffca, 0xffcf }, { 0xffd2, 0xffd7 }, @@ -449,6 +458,7 @@ inline constexpr CharRange xid_start[] = { { 0x105a3, 0x105b1 }, { 0x105b3, 0x105b9 }, { 0x105bb, 0x105bc }, + { 0x105c0, 0x105f3 }, { 0x10600, 0x10736 }, { 0x10740, 0x10755 }, { 0x10760, 0x10767 }, @@ -485,8 +495,11 @@ inline constexpr CharRange xid_start[] = { { 0x10c80, 0x10cb2 }, { 0x10cc0, 0x10cf2 }, { 0x10d00, 0x10d23 }, + { 0x10d4a, 0x10d65 }, + { 0x10d6f, 0x10d85 }, { 0x10e80, 0x10ea9 }, { 0x10eb0, 0x10eb1 }, + { 0x10ec2, 0x10ec4 }, { 0x10f00, 0x10f1c }, { 0x10f27, 0x10f27 }, { 0x10f30, 0x10f45 }, @@ -509,6 +522,7 @@ inline constexpr CharRange xid_start[] = { { 0x111dc, 0x111dc }, { 0x11200, 0x11211 }, { 0x11213, 0x1122b }, + { 0x1123f, 0x11240 }, { 0x11280, 0x11286 }, { 0x11288, 0x11288 }, { 0x1128a, 0x1128d }, @@ -524,6 +538,13 @@ inline constexpr CharRange xid_start[] = { { 0x1133d, 0x1133d }, { 0x11350, 0x11350 }, { 0x1135d, 0x11361 }, + { 0x11380, 0x11389 }, + { 0x1138b, 0x1138b }, + { 0x1138e, 0x1138e }, + { 0x11390, 0x113b5 }, + { 0x113b7, 0x113b7 }, + { 0x113d1, 0x113d1 }, + { 0x113d3, 0x113d3 }, { 0x11400, 0x11434 }, { 0x11447, 0x1144a }, { 0x1145f, 0x11461 }, @@ -558,6 +579,7 @@ inline constexpr CharRange xid_start[] = { { 0x11a5c, 0x11a89 }, { 0x11a9d, 0x11a9d }, { 0x11ab0, 0x11af8 }, + { 0x11bc0, 0x11be0 }, { 0x11c00, 0x11c08 }, { 0x11c0a, 0x11c2e }, { 0x11c40, 0x11c40 }, @@ -571,13 +593,19 @@ inline constexpr CharRange xid_start[] = { { 0x11d6a, 0x11d89 }, { 0x11d98, 0x11d98 }, { 0x11ee0, 0x11ef2 }, + { 0x11f02, 0x11f02 }, + { 0x11f04, 0x11f10 }, + { 0x11f12, 0x11f33 }, { 0x11fb0, 0x11fb0 }, { 0x12000, 0x12399 }, { 0x12400, 0x1246e }, { 0x12480, 0x12543 }, { 0x12f90, 0x12ff0 }, - { 0x13000, 0x1342e }, + { 0x13000, 0x1342f }, + { 0x13441, 0x13446 }, + { 0x13460, 0x143fa }, { 0x14400, 0x14646 }, + { 0x16100, 0x1611d }, { 0x16800, 0x16a38 }, { 0x16a40, 0x16a5e }, { 0x16a70, 0x16abe }, @@ -586,6 +614,7 @@ inline constexpr CharRange xid_start[] = { { 0x16b40, 0x16b43 }, { 0x16b63, 0x16b77 }, { 0x16b7d, 0x16b8f }, + { 0x16d40, 0x16d6c }, { 0x16e40, 0x16e7f }, { 0x16f00, 0x16f4a }, { 0x16f50, 0x16f50 }, @@ -594,12 +623,14 @@ inline constexpr CharRange xid_start[] = { { 0x16fe3, 0x16fe3 }, { 0x17000, 0x187f7 }, { 0x18800, 0x18cd5 }, - { 0x18d00, 0x18d08 }, + { 0x18cff, 0x18d08 }, { 0x1aff0, 0x1aff3 }, { 0x1aff5, 0x1affb }, { 0x1affd, 0x1affe }, { 0x1b000, 0x1b122 }, + { 0x1b132, 0x1b132 }, { 0x1b150, 0x1b152 }, + { 0x1b155, 0x1b155 }, { 0x1b164, 0x1b167 }, { 0x1b170, 0x1b2fb }, { 0x1bc00, 0x1bc6a }, @@ -637,11 +668,16 @@ inline constexpr CharRange xid_start[] = { { 0x1d7aa, 0x1d7c2 }, { 0x1d7c4, 0x1d7cb }, { 0x1df00, 0x1df1e }, + { 0x1df25, 0x1df2a }, + { 0x1e030, 0x1e06d }, { 0x1e100, 0x1e12c }, { 0x1e137, 0x1e13d }, { 0x1e14e, 0x1e14e }, { 0x1e290, 0x1e2ad }, { 0x1e2c0, 0x1e2eb }, + { 0x1e4d0, 0x1e4eb }, + { 0x1e5d0, 0x1e5ed }, + { 0x1e5f0, 0x1e5f0 }, { 0x1e7e0, 0x1e7e6 }, { 0x1e7e8, 0x1e7eb }, { 0x1e7ed, 0x1e7ee }, @@ -683,15 +719,17 @@ inline constexpr CharRange xid_start[] = { { 0x1eea5, 0x1eea9 }, { 0x1eeab, 0x1eebb }, { 0x20000, 0x2a6df }, - { 0x2a700, 0x2b738 }, + { 0x2a700, 0x2b739 }, { 0x2b740, 0x2b81d }, { 0x2b820, 0x2cea1 }, { 0x2ceb0, 0x2ebe0 }, + { 0x2ebf0, 0x2ee5d }, { 0x2f800, 0x2fa1d }, { 0x30000, 0x3134a }, + { 0x31350, 0x323af }, }; -inline constexpr CharRange xid_continue[] = { +constexpr inline CharRange xid_continue[] = { { 0x30, 0x39 }, { 0x41, 0x5a }, { 0x5f, 0x5f }, @@ -709,7 +747,7 @@ inline constexpr CharRange xid_continue[] = { { 0x2ee, 0x2ee }, { 0x300, 0x374 }, { 0x376, 0x377 }, - { 0x37a, 0x37d }, + { 0x37b, 0x37d }, { 0x37f, 0x37f }, { 0x386, 0x38a }, { 0x38c, 0x38c }, @@ -745,7 +783,7 @@ inline constexpr CharRange xid_continue[] = { { 0x860, 0x86a }, { 0x870, 0x887 }, { 0x889, 0x88e }, - { 0x898, 0x8e1 }, + { 0x897, 0x8e1 }, { 0x8e3, 0x963 }, { 0x966, 0x96f }, { 0x971, 0x983 }, @@ -850,7 +888,7 @@ inline constexpr CharRange xid_continue[] = { { 0xcdd, 0xcde }, { 0xce0, 0xce3 }, { 0xce6, 0xcef }, - { 0xcf1, 0xcf2 }, + { 0xcf1, 0xcf3 }, { 0xd00, 0xd0c }, { 0xd0e, 0xd10 }, { 0xd12, 0xd44 }, @@ -883,7 +921,7 @@ inline constexpr CharRange xid_continue[] = { { 0xea7, 0xebd }, { 0xec0, 0xec4 }, { 0xec6, 0xec6 }, - { 0xec8, 0xecd }, + { 0xec8, 0xece }, { 0xed0, 0xed9 }, { 0xedc, 0xedf }, { 0xf00, 0xf00 }, @@ -921,8 +959,7 @@ inline constexpr CharRange xid_continue[] = { { 0x1312, 0x1315 }, { 0x1318, 0x135a }, { 0x135d, 0x135f }, - { 0x1369, 0x1369 }, - { 0x1371, 0x1371 }, + { 0x1369, 0x1371 }, { 0x1380, 0x138f }, { 0x13a0, 0x13f5 }, { 0x13f8, 0x13fd }, @@ -969,7 +1006,7 @@ inline constexpr CharRange xid_continue[] = { { 0x1c00, 0x1c37 }, { 0x1c40, 0x1c49 }, { 0x1c4d, 0x1c7d }, - { 0x1c80, 0x1c88 }, + { 0x1c80, 0x1c8a }, { 0x1c90, 0x1cba }, { 0x1cbd, 0x1cbf }, { 0x1cd0, 0x1cd2 }, @@ -993,6 +1030,7 @@ inline constexpr CharRange xid_continue[] = { { 0x1fe0, 0x1fec }, { 0x1ff2, 0x1ff4 }, { 0x1ff6, 0x1ffc }, + { 0x200c, 0x200d }, { 0x203f, 0x2040 }, { 0x2054, 0x2054 }, { 0x2071, 0x2071 }, @@ -1036,9 +1074,9 @@ inline constexpr CharRange xid_continue[] = { { 0x3031, 0x3035 }, { 0x3038, 0x303c }, { 0x3041, 0x3096 }, - { 0x3099, 0x309f }, - { 0x30a1, 0x30fa }, - { 0x30fc, 0x30ff }, + { 0x3099, 0x309a }, + { 0x309d, 0x309f }, + { 0x30a1, 0x30ff }, { 0x3105, 0x312f }, { 0x3131, 0x318e }, { 0x31a0, 0x31bf }, @@ -1053,10 +1091,10 @@ inline constexpr CharRange xid_continue[] = { { 0xa67f, 0xa6f1 }, { 0xa717, 0xa71f }, { 0xa722, 0xa788 }, - { 0xa78b, 0xa7ca }, + { 0xa78b, 0xa7cd }, { 0xa7d0, 0xa7d1 }, { 0xa7d3, 0xa7d3 }, - { 0xa7d5, 0xa7d9 }, + { 0xa7d5, 0xa7dc }, { 0xa7f2, 0xa827 }, { 0xa82c, 0xa82c }, { 0xa840, 0xa873 }, @@ -1102,21 +1140,27 @@ inline constexpr CharRange xid_continue[] = { { 0xfb40, 0xfb41 }, { 0xfb43, 0xfb44 }, { 0xfb46, 0xfbb1 }, - { 0xfbd3, 0xfd3d }, + { 0xfbd3, 0xfc5d }, + { 0xfc64, 0xfd3d }, { 0xfd50, 0xfd8f }, { 0xfd92, 0xfdc7 }, - { 0xfdf0, 0xfdfb }, + { 0xfdf0, 0xfdf9 }, { 0xfe00, 0xfe0f }, { 0xfe20, 0xfe2f }, { 0xfe33, 0xfe34 }, { 0xfe4d, 0xfe4f }, - { 0xfe70, 0xfe74 }, - { 0xfe76, 0xfefc }, + { 0xfe71, 0xfe71 }, + { 0xfe73, 0xfe73 }, + { 0xfe77, 0xfe77 }, + { 0xfe79, 0xfe79 }, + { 0xfe7b, 0xfe7b }, + { 0xfe7d, 0xfe7d }, + { 0xfe7f, 0xfefc }, { 0xff10, 0xff19 }, { 0xff21, 0xff3a }, { 0xff3f, 0xff3f }, { 0xff41, 0xff5a }, - { 0xff66, 0xffbe }, + { 0xff65, 0xffbe }, { 0xffc2, 0xffc7 }, { 0xffca, 0xffcf }, { 0xffd2, 0xffd7 }, @@ -1154,6 +1198,7 @@ inline constexpr CharRange xid_continue[] = { { 0x105a3, 0x105b1 }, { 0x105b3, 0x105b9 }, { 0x105bb, 0x105bc }, + { 0x105c0, 0x105f3 }, { 0x10600, 0x10736 }, { 0x10740, 0x10755 }, { 0x10760, 0x10767 }, @@ -1194,10 +1239,14 @@ inline constexpr CharRange xid_continue[] = { { 0x10cc0, 0x10cf2 }, { 0x10d00, 0x10d27 }, { 0x10d30, 0x10d39 }, + { 0x10d40, 0x10d65 }, + { 0x10d69, 0x10d6d }, + { 0x10d6f, 0x10d85 }, { 0x10e80, 0x10ea9 }, { 0x10eab, 0x10eac }, { 0x10eb0, 0x10eb1 }, - { 0x10f00, 0x10f1c }, + { 0x10ec2, 0x10ec4 }, + { 0x10efc, 0x10f1c }, { 0x10f27, 0x10f27 }, { 0x10f30, 0x10f50 }, { 0x10f70, 0x10f85 }, @@ -1220,7 +1269,7 @@ inline constexpr CharRange xid_continue[] = { { 0x111dc, 0x111dc }, { 0x11200, 0x11211 }, { 0x11213, 0x11237 }, - { 0x1123e, 0x1123e }, + { 0x1123e, 0x11241 }, { 0x11280, 0x11286 }, { 0x11288, 0x11288 }, { 0x1128a, 0x1128d }, @@ -1243,6 +1292,16 @@ inline constexpr CharRange xid_continue[] = { { 0x1135d, 0x11363 }, { 0x11366, 0x1136c }, { 0x11370, 0x11374 }, + { 0x11380, 0x11389 }, + { 0x1138b, 0x1138b }, + { 0x1138e, 0x1138e }, + { 0x11390, 0x113b5 }, + { 0x113b7, 0x113c0 }, + { 0x113c2, 0x113c2 }, + { 0x113c5, 0x113c5 }, + { 0x113c7, 0x113ca }, + { 0x113cc, 0x113d3 }, + { 0x113e1, 0x113e2 }, { 0x11400, 0x1144a }, { 0x11450, 0x11459 }, { 0x1145e, 0x11461 }, @@ -1257,6 +1316,7 @@ inline constexpr CharRange xid_continue[] = { { 0x11650, 0x11659 }, { 0x11680, 0x116b8 }, { 0x116c0, 0x116c9 }, + { 0x116d0, 0x116e3 }, { 0x11700, 0x1171a }, { 0x1171d, 0x1172b }, { 0x11730, 0x11739 }, @@ -1280,6 +1340,8 @@ inline constexpr CharRange xid_continue[] = { { 0x11a50, 0x11a99 }, { 0x11a9d, 0x11a9d }, { 0x11ab0, 0x11af8 }, + { 0x11bc0, 0x11be0 }, + { 0x11bf0, 0x11bf9 }, { 0x11c00, 0x11c08 }, { 0x11c0a, 0x11c36 }, { 0x11c38, 0x11c40 }, @@ -1301,13 +1363,20 @@ inline constexpr CharRange xid_continue[] = { { 0x11d93, 0x11d98 }, { 0x11da0, 0x11da9 }, { 0x11ee0, 0x11ef6 }, + { 0x11f00, 0x11f10 }, + { 0x11f12, 0x11f3a }, + { 0x11f3e, 0x11f42 }, + { 0x11f50, 0x11f5a }, { 0x11fb0, 0x11fb0 }, { 0x12000, 0x12399 }, { 0x12400, 0x1246e }, { 0x12480, 0x12543 }, { 0x12f90, 0x12ff0 }, - { 0x13000, 0x1342e }, + { 0x13000, 0x1342f }, + { 0x13440, 0x13455 }, + { 0x13460, 0x143fa }, { 0x14400, 0x14646 }, + { 0x16100, 0x16139 }, { 0x16800, 0x16a38 }, { 0x16a40, 0x16a5e }, { 0x16a60, 0x16a69 }, @@ -1320,6 +1389,8 @@ inline constexpr CharRange xid_continue[] = { { 0x16b50, 0x16b59 }, { 0x16b63, 0x16b77 }, { 0x16b7d, 0x16b8f }, + { 0x16d40, 0x16d6c }, + { 0x16d70, 0x16d79 }, { 0x16e40, 0x16e7f }, { 0x16f00, 0x16f4a }, { 0x16f4f, 0x16f87 }, @@ -1329,12 +1400,14 @@ inline constexpr CharRange xid_continue[] = { { 0x16ff0, 0x16ff1 }, { 0x17000, 0x187f7 }, { 0x18800, 0x18cd5 }, - { 0x18d00, 0x18d08 }, + { 0x18cff, 0x18d08 }, { 0x1aff0, 0x1aff3 }, { 0x1aff5, 0x1affb }, { 0x1affd, 0x1affe }, { 0x1b000, 0x1b122 }, + { 0x1b132, 0x1b132 }, { 0x1b150, 0x1b152 }, + { 0x1b155, 0x1b155 }, { 0x1b164, 0x1b167 }, { 0x1b170, 0x1b2fb }, { 0x1bc00, 0x1bc6a }, @@ -1342,6 +1415,7 @@ inline constexpr CharRange xid_continue[] = { { 0x1bc80, 0x1bc88 }, { 0x1bc90, 0x1bc99 }, { 0x1bc9d, 0x1bc9e }, + { 0x1ccf0, 0x1ccf9 }, { 0x1cf00, 0x1cf2d }, { 0x1cf30, 0x1cf46 }, { 0x1d165, 0x1d169 }, @@ -1388,17 +1462,22 @@ inline constexpr CharRange xid_continue[] = { { 0x1da9b, 0x1da9f }, { 0x1daa1, 0x1daaf }, { 0x1df00, 0x1df1e }, + { 0x1df25, 0x1df2a }, { 0x1e000, 0x1e006 }, { 0x1e008, 0x1e018 }, { 0x1e01b, 0x1e021 }, { 0x1e023, 0x1e024 }, { 0x1e026, 0x1e02a }, + { 0x1e030, 0x1e06d }, + { 0x1e08f, 0x1e08f }, { 0x1e100, 0x1e12c }, { 0x1e130, 0x1e13d }, { 0x1e140, 0x1e149 }, { 0x1e14e, 0x1e14e }, { 0x1e290, 0x1e2ae }, { 0x1e2c0, 0x1e2f9 }, + { 0x1e4d0, 0x1e4f9 }, + { 0x1e5d0, 0x1e5fa }, { 0x1e7e0, 0x1e7e6 }, { 0x1e7e8, 0x1e7eb }, { 0x1e7ed, 0x1e7ee }, @@ -1442,16 +1521,18 @@ inline constexpr CharRange xid_continue[] = { { 0x1eeab, 0x1eebb }, { 0x1fbf0, 0x1fbf9 }, { 0x20000, 0x2a6df }, - { 0x2a700, 0x2b738 }, + { 0x2a700, 0x2b739 }, { 0x2b740, 0x2b81d }, { 0x2b820, 0x2cea1 }, { 0x2ceb0, 0x2ebe0 }, + { 0x2ebf0, 0x2ee5d }, { 0x2f800, 0x2fa1d }, { 0x30000, 0x3134a }, + { 0x31350, 0x323af }, { 0xe0100, 0xe01ef }, }; -inline constexpr CharRange uppercase_letter[] = { +constexpr inline CharRange uppercase_letter[] = { { 0x41, 0x5a }, { 0xc0, 0xd6 }, { 0xd8, 0xde }, @@ -1728,6 +1809,7 @@ inline constexpr CharRange uppercase_letter[] = { { 0x10c7, 0x10c7 }, { 0x10cd, 0x10cd }, { 0x13a0, 0x13f5 }, + { 0x1c89, 0x1c89 }, { 0x1c90, 0x1cba }, { 0x1cbd, 0x1cbf }, { 0x1e00, 0x1e00 }, @@ -1882,7 +1964,9 @@ inline constexpr CharRange uppercase_letter[] = { { 0x2130, 0x2133 }, { 0x213e, 0x213f }, { 0x2145, 0x2145 }, + { 0x2160, 0x216f }, { 0x2183, 0x2183 }, + { 0x24b6, 0x24cf }, { 0x2c00, 0x2c2f }, { 0x2c60, 0x2c60 }, { 0x2c62, 0x2c64 }, @@ -2052,9 +2136,12 @@ inline constexpr CharRange uppercase_letter[] = { { 0xa7c2, 0xa7c2 }, { 0xa7c4, 0xa7c7 }, { 0xa7c9, 0xa7c9 }, + { 0xa7cb, 0xa7cc }, { 0xa7d0, 0xa7d0 }, { 0xa7d6, 0xa7d6 }, { 0xa7d8, 0xa7d8 }, + { 0xa7da, 0xa7da }, + { 0xa7dc, 0xa7dc }, { 0xa7f5, 0xa7f5 }, { 0xff21, 0xff3a }, { 0x10400, 0x10427 }, @@ -2064,6 +2151,7 @@ inline constexpr CharRange uppercase_letter[] = { { 0x1058c, 0x10592 }, { 0x10594, 0x10595 }, { 0x10c80, 0x10cb2 }, + { 0x10d50, 0x10d65 }, { 0x118a0, 0x118bf }, { 0x16e40, 0x16e5f }, { 0x1d400, 0x1d419 }, @@ -2098,11 +2186,16 @@ inline constexpr CharRange uppercase_letter[] = { { 0x1d790, 0x1d7a8 }, { 0x1d7ca, 0x1d7ca }, { 0x1e900, 0x1e921 }, + { 0x1f130, 0x1f149 }, + { 0x1f150, 0x1f169 }, + { 0x1f170, 0x1f189 }, }; -inline constexpr CharRange lowercase_letter[] = { +constexpr inline CharRange lowercase_letter[] = { { 0x61, 0x7a }, + { 0xaa, 0xaa }, { 0xb5, 0xb5 }, + { 0xba, 0xba }, { 0xdf, 0xf6 }, { 0xf8, 0xff }, { 0x101, 0x101 }, @@ -2246,11 +2339,14 @@ inline constexpr CharRange lowercase_letter[] = { { 0x24b, 0x24b }, { 0x24d, 0x24d }, { 0x24f, 0x293 }, - { 0x295, 0x2af }, + { 0x295, 0x2b8 }, + { 0x2c0, 0x2c1 }, + { 0x2e0, 0x2e4 }, + { 0x345, 0x345 }, { 0x371, 0x371 }, { 0x373, 0x373 }, { 0x377, 0x377 }, - { 0x37b, 0x37d }, + { 0x37a, 0x37d }, { 0x390, 0x390 }, { 0x3ac, 0x3ce }, { 0x3d0, 0x3d1 }, @@ -2372,12 +2468,11 @@ inline constexpr CharRange lowercase_letter[] = { { 0x52f, 0x52f }, { 0x560, 0x588 }, { 0x10d0, 0x10fa }, - { 0x10fd, 0x10ff }, + { 0x10fc, 0x10ff }, { 0x13f8, 0x13fd }, { 0x1c80, 0x1c88 }, - { 0x1d00, 0x1d2b }, - { 0x1d6b, 0x1d77 }, - { 0x1d79, 0x1d9a }, + { 0x1c8a, 0x1c8a }, + { 0x1d00, 0x1dbf }, { 0x1e01, 0x1e01 }, { 0x1e03, 0x1e03 }, { 0x1e05, 0x1e05 }, @@ -2522,6 +2617,9 @@ inline constexpr CharRange lowercase_letter[] = { { 0x1fe0, 0x1fe7 }, { 0x1ff2, 0x1ff4 }, { 0x1ff6, 0x1ff7 }, + { 0x2071, 0x2071 }, + { 0x207f, 0x207f }, + { 0x2090, 0x209c }, { 0x210a, 0x210a }, { 0x210e, 0x210f }, { 0x2113, 0x2113 }, @@ -2531,7 +2629,9 @@ inline constexpr CharRange lowercase_letter[] = { { 0x213c, 0x213d }, { 0x2146, 0x2149 }, { 0x214e, 0x214e }, + { 0x2170, 0x217f }, { 0x2184, 0x2184 }, + { 0x24d0, 0x24e9 }, { 0x2c30, 0x2c5f }, { 0x2c61, 0x2c61 }, { 0x2c65, 0x2c66 }, @@ -2540,7 +2640,7 @@ inline constexpr CharRange lowercase_letter[] = { { 0x2c6c, 0x2c6c }, { 0x2c71, 0x2c71 }, { 0x2c73, 0x2c74 }, - { 0x2c76, 0x2c7b }, + { 0x2c76, 0x2c7d }, { 0x2c81, 0x2c81 }, { 0x2c83, 0x2c83 }, { 0x2c85, 0x2c85 }, @@ -2633,7 +2733,7 @@ inline constexpr CharRange lowercase_letter[] = { { 0xa695, 0xa695 }, { 0xa697, 0xa697 }, { 0xa699, 0xa699 }, - { 0xa69b, 0xa69b }, + { 0xa69b, 0xa69d }, { 0xa723, 0xa723 }, { 0xa725, 0xa725 }, { 0xa727, 0xa727 }, @@ -2671,8 +2771,7 @@ inline constexpr CharRange lowercase_letter[] = { { 0xa769, 0xa769 }, { 0xa76b, 0xa76b }, { 0xa76d, 0xa76d }, - { 0xa76f, 0xa76f }, - { 0xa771, 0xa778 }, + { 0xa76f, 0xa778 }, { 0xa77a, 0xa77a }, { 0xa77c, 0xa77c }, { 0xa77f, 0xa77f }, @@ -2705,15 +2804,18 @@ inline constexpr CharRange lowercase_letter[] = { { 0xa7c3, 0xa7c3 }, { 0xa7c8, 0xa7c8 }, { 0xa7ca, 0xa7ca }, + { 0xa7cd, 0xa7cd }, { 0xa7d1, 0xa7d1 }, { 0xa7d3, 0xa7d3 }, { 0xa7d5, 0xa7d5 }, { 0xa7d7, 0xa7d7 }, { 0xa7d9, 0xa7d9 }, + { 0xa7db, 0xa7db }, + { 0xa7f2, 0xa7f4 }, { 0xa7f6, 0xa7f6 }, - { 0xa7fa, 0xa7fa }, + { 0xa7f8, 0xa7fa }, { 0xab30, 0xab5a }, - { 0xab60, 0xab68 }, + { 0xab5c, 0xab69 }, { 0xab70, 0xabbf }, { 0xfb00, 0xfb06 }, { 0xfb13, 0xfb17 }, @@ -2724,7 +2826,12 @@ inline constexpr CharRange lowercase_letter[] = { { 0x105a3, 0x105b1 }, { 0x105b3, 0x105b9 }, { 0x105bb, 0x105bc }, + { 0x10780, 0x10780 }, + { 0x10783, 0x10785 }, + { 0x10787, 0x107b0 }, + { 0x107b2, 0x107ba }, { 0x10cc0, 0x10cf2 }, + { 0x10d70, 0x10d85 }, { 0x118c0, 0x118df }, { 0x16e60, 0x16e7f }, { 0x1d41a, 0x1d433 }, @@ -2758,10 +2865,11 @@ inline constexpr CharRange lowercase_letter[] = { { 0x1df00, 0x1df09 }, { 0x1df0b, 0x1df1e }, { 0x1df25, 0x1df2a }, + { 0x1e030, 0x1e06d }, { 0x1e922, 0x1e943 }, }; -inline constexpr CharRange unicode_letter[] = { +constexpr inline CharRange unicode_letter[] = { { 0x41, 0x5a }, { 0x61, 0x7a }, { 0xaa, 0xaa }, @@ -2774,7 +2882,8 @@ inline constexpr CharRange unicode_letter[] = { { 0x2e0, 0x2e4 }, { 0x2ec, 0x2ec }, { 0x2ee, 0x2ee }, - { 0x370, 0x374 }, + { 0x345, 0x345 }, + { 0x363, 0x374 }, { 0x376, 0x377 }, { 0x37a, 0x37d }, { 0x37f, 0x37f }, @@ -2788,49 +2897,58 @@ inline constexpr CharRange unicode_letter[] = { { 0x531, 0x556 }, { 0x559, 0x559 }, { 0x560, 0x588 }, + { 0x5b0, 0x5bd }, + { 0x5bf, 0x5bf }, + { 0x5c1, 0x5c2 }, + { 0x5c4, 0x5c5 }, + { 0x5c7, 0x5c7 }, { 0x5d0, 0x5ea }, { 0x5ef, 0x5f2 }, - { 0x620, 0x64a }, - { 0x66e, 0x66f }, - { 0x671, 0x6d3 }, - { 0x6d5, 0x6d5 }, - { 0x6e5, 0x6e6 }, - { 0x6ee, 0x6ef }, + { 0x610, 0x61a }, + { 0x620, 0x657 }, + { 0x659, 0x65f }, + { 0x66e, 0x6d3 }, + { 0x6d5, 0x6dc }, + { 0x6e1, 0x6e8 }, + { 0x6ed, 0x6ef }, { 0x6fa, 0x6fc }, { 0x6ff, 0x6ff }, - { 0x710, 0x710 }, - { 0x712, 0x72f }, - { 0x74d, 0x7a5 }, - { 0x7b1, 0x7b1 }, + { 0x710, 0x73f }, + { 0x74d, 0x7b1 }, { 0x7ca, 0x7ea }, { 0x7f4, 0x7f5 }, { 0x7fa, 0x7fa }, - { 0x800, 0x815 }, - { 0x81a, 0x81a }, - { 0x824, 0x824 }, - { 0x828, 0x828 }, + { 0x800, 0x817 }, + { 0x81a, 0x82c }, { 0x840, 0x858 }, { 0x860, 0x86a }, { 0x870, 0x887 }, { 0x889, 0x88e }, + { 0x897, 0x897 }, { 0x8a0, 0x8c9 }, - { 0x904, 0x939 }, - { 0x93d, 0x93d }, - { 0x950, 0x950 }, - { 0x958, 0x961 }, - { 0x971, 0x980 }, + { 0x8d4, 0x8df }, + { 0x8e3, 0x8e9 }, + { 0x8f0, 0x93b }, + { 0x93d, 0x94c }, + { 0x94e, 0x950 }, + { 0x955, 0x963 }, + { 0x971, 0x983 }, { 0x985, 0x98c }, { 0x98f, 0x990 }, { 0x993, 0x9a8 }, { 0x9aa, 0x9b0 }, { 0x9b2, 0x9b2 }, { 0x9b6, 0x9b9 }, - { 0x9bd, 0x9bd }, + { 0x9bd, 0x9c4 }, + { 0x9c7, 0x9c8 }, + { 0x9cb, 0x9cc }, { 0x9ce, 0x9ce }, + { 0x9d7, 0x9d7 }, { 0x9dc, 0x9dd }, - { 0x9df, 0x9e1 }, + { 0x9df, 0x9e3 }, { 0x9f0, 0x9f1 }, { 0x9fc, 0x9fc }, + { 0xa01, 0xa03 }, { 0xa05, 0xa0a }, { 0xa0f, 0xa10 }, { 0xa13, 0xa28 }, @@ -2838,30 +2956,41 @@ inline constexpr CharRange unicode_letter[] = { { 0xa32, 0xa33 }, { 0xa35, 0xa36 }, { 0xa38, 0xa39 }, + { 0xa3e, 0xa42 }, + { 0xa47, 0xa48 }, + { 0xa4b, 0xa4c }, + { 0xa51, 0xa51 }, { 0xa59, 0xa5c }, { 0xa5e, 0xa5e }, - { 0xa72, 0xa74 }, + { 0xa70, 0xa75 }, + { 0xa81, 0xa83 }, { 0xa85, 0xa8d }, { 0xa8f, 0xa91 }, { 0xa93, 0xaa8 }, { 0xaaa, 0xab0 }, { 0xab2, 0xab3 }, { 0xab5, 0xab9 }, - { 0xabd, 0xabd }, + { 0xabd, 0xac5 }, + { 0xac7, 0xac9 }, + { 0xacb, 0xacc }, { 0xad0, 0xad0 }, - { 0xae0, 0xae1 }, - { 0xaf9, 0xaf9 }, + { 0xae0, 0xae3 }, + { 0xaf9, 0xafc }, + { 0xb01, 0xb03 }, { 0xb05, 0xb0c }, { 0xb0f, 0xb10 }, { 0xb13, 0xb28 }, { 0xb2a, 0xb30 }, { 0xb32, 0xb33 }, { 0xb35, 0xb39 }, - { 0xb3d, 0xb3d }, + { 0xb3d, 0xb44 }, + { 0xb47, 0xb48 }, + { 0xb4b, 0xb4c }, + { 0xb56, 0xb57 }, { 0xb5c, 0xb5d }, - { 0xb5f, 0xb61 }, + { 0xb5f, 0xb63 }, { 0xb71, 0xb71 }, - { 0xb83, 0xb83 }, + { 0xb82, 0xb83 }, { 0xb85, 0xb8a }, { 0xb8e, 0xb90 }, { 0xb92, 0xb95 }, @@ -2871,65 +3000,80 @@ inline constexpr CharRange unicode_letter[] = { { 0xba3, 0xba4 }, { 0xba8, 0xbaa }, { 0xbae, 0xbb9 }, + { 0xbbe, 0xbc2 }, + { 0xbc6, 0xbc8 }, + { 0xbca, 0xbcc }, { 0xbd0, 0xbd0 }, - { 0xc05, 0xc0c }, + { 0xbd7, 0xbd7 }, + { 0xc00, 0xc0c }, { 0xc0e, 0xc10 }, { 0xc12, 0xc28 }, { 0xc2a, 0xc39 }, - { 0xc3d, 0xc3d }, + { 0xc3d, 0xc44 }, + { 0xc46, 0xc48 }, + { 0xc4a, 0xc4c }, + { 0xc55, 0xc56 }, { 0xc58, 0xc5a }, { 0xc5d, 0xc5d }, - { 0xc60, 0xc61 }, - { 0xc80, 0xc80 }, + { 0xc60, 0xc63 }, + { 0xc80, 0xc83 }, { 0xc85, 0xc8c }, { 0xc8e, 0xc90 }, { 0xc92, 0xca8 }, { 0xcaa, 0xcb3 }, { 0xcb5, 0xcb9 }, - { 0xcbd, 0xcbd }, + { 0xcbd, 0xcc4 }, + { 0xcc6, 0xcc8 }, + { 0xcca, 0xccc }, + { 0xcd5, 0xcd6 }, { 0xcdd, 0xcde }, - { 0xce0, 0xce1 }, - { 0xcf1, 0xcf2 }, - { 0xd04, 0xd0c }, + { 0xce0, 0xce3 }, + { 0xcf1, 0xcf3 }, + { 0xd00, 0xd0c }, { 0xd0e, 0xd10 }, { 0xd12, 0xd3a }, - { 0xd3d, 0xd3d }, + { 0xd3d, 0xd44 }, + { 0xd46, 0xd48 }, + { 0xd4a, 0xd4c }, { 0xd4e, 0xd4e }, - { 0xd54, 0xd56 }, - { 0xd5f, 0xd61 }, + { 0xd54, 0xd57 }, + { 0xd5f, 0xd63 }, { 0xd7a, 0xd7f }, + { 0xd81, 0xd83 }, { 0xd85, 0xd96 }, { 0xd9a, 0xdb1 }, { 0xdb3, 0xdbb }, { 0xdbd, 0xdbd }, { 0xdc0, 0xdc6 }, - { 0xe01, 0xe30 }, - { 0xe32, 0xe33 }, + { 0xdcf, 0xdd4 }, + { 0xdd6, 0xdd6 }, + { 0xdd8, 0xddf }, + { 0xdf2, 0xdf3 }, + { 0xe01, 0xe3a }, { 0xe40, 0xe46 }, + { 0xe4d, 0xe4d }, { 0xe81, 0xe82 }, { 0xe84, 0xe84 }, { 0xe86, 0xe8a }, { 0xe8c, 0xea3 }, { 0xea5, 0xea5 }, - { 0xea7, 0xeb0 }, - { 0xeb2, 0xeb3 }, - { 0xebd, 0xebd }, + { 0xea7, 0xeb9 }, + { 0xebb, 0xebd }, { 0xec0, 0xec4 }, { 0xec6, 0xec6 }, + { 0xecd, 0xecd }, { 0xedc, 0xedf }, { 0xf00, 0xf00 }, { 0xf40, 0xf47 }, { 0xf49, 0xf6c }, - { 0xf88, 0xf8c }, - { 0x1000, 0x102a }, - { 0x103f, 0x103f }, - { 0x1050, 0x1055 }, - { 0x105a, 0x105d }, - { 0x1061, 0x1061 }, - { 0x1065, 0x1066 }, - { 0x106e, 0x1070 }, - { 0x1075, 0x1081 }, - { 0x108e, 0x108e }, + { 0xf71, 0xf83 }, + { 0xf88, 0xf97 }, + { 0xf99, 0xfbc }, + { 0x1000, 0x1036 }, + { 0x1038, 0x1038 }, + { 0x103b, 0x103f }, + { 0x1050, 0x108f }, + { 0x109a, 0x109d }, { 0x10a0, 0x10c5 }, { 0x10c7, 0x10c7 }, { 0x10cd, 0x10cd }, @@ -2957,37 +3101,44 @@ inline constexpr CharRange unicode_letter[] = { { 0x166f, 0x167f }, { 0x1681, 0x169a }, { 0x16a0, 0x16ea }, - { 0x16f1, 0x16f8 }, - { 0x1700, 0x1711 }, - { 0x171f, 0x1731 }, - { 0x1740, 0x1751 }, + { 0x16ee, 0x16f8 }, + { 0x1700, 0x1713 }, + { 0x171f, 0x1733 }, + { 0x1740, 0x1753 }, { 0x1760, 0x176c }, { 0x176e, 0x1770 }, + { 0x1772, 0x1773 }, { 0x1780, 0x17b3 }, + { 0x17b6, 0x17c8 }, { 0x17d7, 0x17d7 }, { 0x17dc, 0x17dc }, { 0x1820, 0x1878 }, - { 0x1880, 0x1884 }, - { 0x1887, 0x18a8 }, - { 0x18aa, 0x18aa }, + { 0x1880, 0x18aa }, { 0x18b0, 0x18f5 }, { 0x1900, 0x191e }, + { 0x1920, 0x192b }, + { 0x1930, 0x1938 }, { 0x1950, 0x196d }, { 0x1970, 0x1974 }, { 0x1980, 0x19ab }, { 0x19b0, 0x19c9 }, - { 0x1a00, 0x1a16 }, - { 0x1a20, 0x1a54 }, + { 0x1a00, 0x1a1b }, + { 0x1a20, 0x1a5e }, + { 0x1a61, 0x1a74 }, { 0x1aa7, 0x1aa7 }, - { 0x1b05, 0x1b33 }, + { 0x1abf, 0x1ac0 }, + { 0x1acc, 0x1ace }, + { 0x1b00, 0x1b33 }, + { 0x1b35, 0x1b43 }, { 0x1b45, 0x1b4c }, - { 0x1b83, 0x1ba0 }, - { 0x1bae, 0x1baf }, + { 0x1b80, 0x1ba9 }, + { 0x1bac, 0x1baf }, { 0x1bba, 0x1be5 }, - { 0x1c00, 0x1c23 }, + { 0x1be7, 0x1bf1 }, + { 0x1c00, 0x1c36 }, { 0x1c4d, 0x1c4f }, { 0x1c5a, 0x1c7d }, - { 0x1c80, 0x1c88 }, + { 0x1c80, 0x1c8a }, { 0x1c90, 0x1cba }, { 0x1cbd, 0x1cbf }, { 0x1ce9, 0x1cec }, @@ -2995,6 +3146,7 @@ inline constexpr CharRange unicode_letter[] = { { 0x1cf5, 0x1cf6 }, { 0x1cfa, 0x1cfa }, { 0x1d00, 0x1dbf }, + { 0x1dd3, 0x1df4 }, { 0x1e00, 0x1f15 }, { 0x1f18, 0x1f1d }, { 0x1f20, 0x1f45 }, @@ -3030,7 +3182,8 @@ inline constexpr CharRange unicode_letter[] = { { 0x213c, 0x213f }, { 0x2145, 0x2149 }, { 0x214e, 0x214e }, - { 0x2183, 0x2184 }, + { 0x2160, 0x2188 }, + { 0x24b6, 0x24e9 }, { 0x2c00, 0x2ce4 }, { 0x2ceb, 0x2cee }, { 0x2cf2, 0x2cf3 }, @@ -3048,10 +3201,12 @@ inline constexpr CharRange unicode_letter[] = { { 0x2dc8, 0x2dce }, { 0x2dd0, 0x2dd6 }, { 0x2dd8, 0x2dde }, + { 0x2de0, 0x2dff }, { 0x2e2f, 0x2e2f }, - { 0x3005, 0x3006 }, + { 0x3005, 0x3007 }, + { 0x3021, 0x3029 }, { 0x3031, 0x3035 }, - { 0x303b, 0x303c }, + { 0x3038, 0x303c }, { 0x3041, 0x3096 }, { 0x309d, 0x309f }, { 0x30a1, 0x30fa }, @@ -3067,45 +3222,39 @@ inline constexpr CharRange unicode_letter[] = { { 0xa610, 0xa61f }, { 0xa62a, 0xa62b }, { 0xa640, 0xa66e }, - { 0xa67f, 0xa69d }, - { 0xa6a0, 0xa6e5 }, + { 0xa674, 0xa67b }, + { 0xa67f, 0xa6ef }, { 0xa717, 0xa71f }, { 0xa722, 0xa788 }, - { 0xa78b, 0xa7ca }, + { 0xa78b, 0xa7cd }, { 0xa7d0, 0xa7d1 }, { 0xa7d3, 0xa7d3 }, - { 0xa7d5, 0xa7d9 }, - { 0xa7f2, 0xa801 }, - { 0xa803, 0xa805 }, - { 0xa807, 0xa80a }, - { 0xa80c, 0xa822 }, + { 0xa7d5, 0xa7dc }, + { 0xa7f2, 0xa805 }, + { 0xa807, 0xa827 }, { 0xa840, 0xa873 }, - { 0xa882, 0xa8b3 }, + { 0xa880, 0xa8c3 }, + { 0xa8c5, 0xa8c5 }, { 0xa8f2, 0xa8f7 }, { 0xa8fb, 0xa8fb }, - { 0xa8fd, 0xa8fe }, - { 0xa90a, 0xa925 }, - { 0xa930, 0xa946 }, + { 0xa8fd, 0xa8ff }, + { 0xa90a, 0xa92a }, + { 0xa930, 0xa952 }, { 0xa960, 0xa97c }, - { 0xa984, 0xa9b2 }, + { 0xa980, 0xa9b2 }, + { 0xa9b4, 0xa9bf }, { 0xa9cf, 0xa9cf }, - { 0xa9e0, 0xa9e4 }, - { 0xa9e6, 0xa9ef }, + { 0xa9e0, 0xa9ef }, { 0xa9fa, 0xa9fe }, - { 0xaa00, 0xaa28 }, - { 0xaa40, 0xaa42 }, - { 0xaa44, 0xaa4b }, + { 0xaa00, 0xaa36 }, + { 0xaa40, 0xaa4d }, { 0xaa60, 0xaa76 }, - { 0xaa7a, 0xaa7a }, - { 0xaa7e, 0xaaaf }, - { 0xaab1, 0xaab1 }, - { 0xaab5, 0xaab6 }, - { 0xaab9, 0xaabd }, + { 0xaa7a, 0xaabe }, { 0xaac0, 0xaac0 }, { 0xaac2, 0xaac2 }, { 0xaadb, 0xaadd }, - { 0xaae0, 0xaaea }, - { 0xaaf2, 0xaaf4 }, + { 0xaae0, 0xaaef }, + { 0xaaf2, 0xaaf5 }, { 0xab01, 0xab06 }, { 0xab09, 0xab0e }, { 0xab11, 0xab16 }, @@ -3113,7 +3262,7 @@ inline constexpr CharRange unicode_letter[] = { { 0xab28, 0xab2e }, { 0xab30, 0xab5a }, { 0xab5c, 0xab69 }, - { 0xab70, 0xabe2 }, + { 0xab70, 0xabea }, { 0xac00, 0xd7a3 }, { 0xd7b0, 0xd7c6 }, { 0xd7cb, 0xd7fb }, @@ -3121,8 +3270,7 @@ inline constexpr CharRange unicode_letter[] = { { 0xfa70, 0xfad9 }, { 0xfb00, 0xfb06 }, { 0xfb13, 0xfb17 }, - { 0xfb1d, 0xfb1d }, - { 0xfb1f, 0xfb28 }, + { 0xfb1d, 0xfb28 }, { 0xfb2a, 0xfb36 }, { 0xfb38, 0xfb3c }, { 0xfb3e, 0xfb3e }, @@ -3149,15 +3297,16 @@ inline constexpr CharRange unicode_letter[] = { { 0x1003f, 0x1004d }, { 0x10050, 0x1005d }, { 0x10080, 0x100fa }, + { 0x10140, 0x10174 }, { 0x10280, 0x1029c }, { 0x102a0, 0x102d0 }, { 0x10300, 0x1031f }, - { 0x1032d, 0x10340 }, - { 0x10342, 0x10349 }, - { 0x10350, 0x10375 }, + { 0x1032d, 0x1034a }, + { 0x10350, 0x1037a }, { 0x10380, 0x1039d }, { 0x103a0, 0x103c3 }, { 0x103c8, 0x103cf }, + { 0x103d1, 0x103d5 }, { 0x10400, 0x1049d }, { 0x104b0, 0x104d3 }, { 0x104d8, 0x104fb }, @@ -3171,6 +3320,7 @@ inline constexpr CharRange unicode_letter[] = { { 0x105a3, 0x105b1 }, { 0x105b3, 0x105b9 }, { 0x105bb, 0x105bc }, + { 0x105c0, 0x105f3 }, { 0x10600, 0x10736 }, { 0x10740, 0x10755 }, { 0x10760, 0x10767 }, @@ -3191,8 +3341,9 @@ inline constexpr CharRange unicode_letter[] = { { 0x10920, 0x10939 }, { 0x10980, 0x109b7 }, { 0x109be, 0x109bf }, - { 0x10a00, 0x10a00 }, - { 0x10a10, 0x10a13 }, + { 0x10a00, 0x10a03 }, + { 0x10a05, 0x10a06 }, + { 0x10a0c, 0x10a13 }, { 0x10a15, 0x10a17 }, { 0x10a19, 0x10a35 }, { 0x10a60, 0x10a7c }, @@ -3206,104 +3357,143 @@ inline constexpr CharRange unicode_letter[] = { { 0x10c00, 0x10c48 }, { 0x10c80, 0x10cb2 }, { 0x10cc0, 0x10cf2 }, - { 0x10d00, 0x10d23 }, + { 0x10d00, 0x10d27 }, + { 0x10d4a, 0x10d65 }, + { 0x10d69, 0x10d69 }, + { 0x10d6f, 0x10d85 }, { 0x10e80, 0x10ea9 }, + { 0x10eab, 0x10eac }, { 0x10eb0, 0x10eb1 }, + { 0x10ec2, 0x10ec4 }, + { 0x10efc, 0x10efc }, { 0x10f00, 0x10f1c }, { 0x10f27, 0x10f27 }, { 0x10f30, 0x10f45 }, { 0x10f70, 0x10f81 }, { 0x10fb0, 0x10fc4 }, { 0x10fe0, 0x10ff6 }, - { 0x11003, 0x11037 }, - { 0x11071, 0x11072 }, - { 0x11075, 0x11075 }, - { 0x11083, 0x110af }, + { 0x11000, 0x11045 }, + { 0x11071, 0x11075 }, + { 0x11080, 0x110b8 }, + { 0x110c2, 0x110c2 }, { 0x110d0, 0x110e8 }, - { 0x11103, 0x11126 }, - { 0x11144, 0x11144 }, - { 0x11147, 0x11147 }, + { 0x11100, 0x11132 }, + { 0x11144, 0x11147 }, { 0x11150, 0x11172 }, { 0x11176, 0x11176 }, - { 0x11183, 0x111b2 }, + { 0x11180, 0x111bf }, { 0x111c1, 0x111c4 }, + { 0x111ce, 0x111cf }, { 0x111da, 0x111da }, { 0x111dc, 0x111dc }, { 0x11200, 0x11211 }, - { 0x11213, 0x1122b }, - { 0x1123f, 0x11240 }, + { 0x11213, 0x11234 }, + { 0x11237, 0x11237 }, + { 0x1123e, 0x11241 }, { 0x11280, 0x11286 }, { 0x11288, 0x11288 }, { 0x1128a, 0x1128d }, { 0x1128f, 0x1129d }, { 0x1129f, 0x112a8 }, - { 0x112b0, 0x112de }, + { 0x112b0, 0x112e8 }, + { 0x11300, 0x11303 }, { 0x11305, 0x1130c }, { 0x1130f, 0x11310 }, { 0x11313, 0x11328 }, { 0x1132a, 0x11330 }, { 0x11332, 0x11333 }, { 0x11335, 0x11339 }, - { 0x1133d, 0x1133d }, + { 0x1133d, 0x11344 }, + { 0x11347, 0x11348 }, + { 0x1134b, 0x1134c }, { 0x11350, 0x11350 }, - { 0x1135d, 0x11361 }, - { 0x11400, 0x11434 }, + { 0x11357, 0x11357 }, + { 0x1135d, 0x11363 }, + { 0x11380, 0x11389 }, + { 0x1138b, 0x1138b }, + { 0x1138e, 0x1138e }, + { 0x11390, 0x113b5 }, + { 0x113b7, 0x113c0 }, + { 0x113c2, 0x113c2 }, + { 0x113c5, 0x113c5 }, + { 0x113c7, 0x113ca }, + { 0x113cc, 0x113cd }, + { 0x113d1, 0x113d1 }, + { 0x113d3, 0x113d3 }, + { 0x11400, 0x11441 }, + { 0x11443, 0x11445 }, { 0x11447, 0x1144a }, { 0x1145f, 0x11461 }, - { 0x11480, 0x114af }, + { 0x11480, 0x114c1 }, { 0x114c4, 0x114c5 }, { 0x114c7, 0x114c7 }, - { 0x11580, 0x115ae }, - { 0x115d8, 0x115db }, - { 0x11600, 0x1162f }, + { 0x11580, 0x115b5 }, + { 0x115b8, 0x115be }, + { 0x115d8, 0x115dd }, + { 0x11600, 0x1163e }, + { 0x11640, 0x11640 }, { 0x11644, 0x11644 }, - { 0x11680, 0x116aa }, + { 0x11680, 0x116b5 }, { 0x116b8, 0x116b8 }, { 0x11700, 0x1171a }, + { 0x1171d, 0x1172a }, { 0x11740, 0x11746 }, - { 0x11800, 0x1182b }, + { 0x11800, 0x11838 }, { 0x118a0, 0x118df }, { 0x118ff, 0x11906 }, { 0x11909, 0x11909 }, { 0x1190c, 0x11913 }, { 0x11915, 0x11916 }, - { 0x11918, 0x1192f }, - { 0x1193f, 0x1193f }, - { 0x11941, 0x11941 }, + { 0x11918, 0x11935 }, + { 0x11937, 0x11938 }, + { 0x1193b, 0x1193c }, + { 0x1193f, 0x11942 }, { 0x119a0, 0x119a7 }, - { 0x119aa, 0x119d0 }, + { 0x119aa, 0x119d7 }, + { 0x119da, 0x119df }, { 0x119e1, 0x119e1 }, - { 0x119e3, 0x119e3 }, - { 0x11a00, 0x11a00 }, - { 0x11a0b, 0x11a32 }, - { 0x11a3a, 0x11a3a }, - { 0x11a50, 0x11a50 }, - { 0x11a5c, 0x11a89 }, + { 0x119e3, 0x119e4 }, + { 0x11a00, 0x11a32 }, + { 0x11a35, 0x11a3e }, + { 0x11a50, 0x11a97 }, { 0x11a9d, 0x11a9d }, { 0x11ab0, 0x11af8 }, + { 0x11bc0, 0x11be0 }, { 0x11c00, 0x11c08 }, - { 0x11c0a, 0x11c2e }, + { 0x11c0a, 0x11c36 }, + { 0x11c38, 0x11c3e }, { 0x11c40, 0x11c40 }, { 0x11c72, 0x11c8f }, + { 0x11c92, 0x11ca7 }, + { 0x11ca9, 0x11cb6 }, { 0x11d00, 0x11d06 }, { 0x11d08, 0x11d09 }, - { 0x11d0b, 0x11d30 }, - { 0x11d46, 0x11d46 }, + { 0x11d0b, 0x11d36 }, + { 0x11d3a, 0x11d3a }, + { 0x11d3c, 0x11d3d }, + { 0x11d3f, 0x11d41 }, + { 0x11d43, 0x11d43 }, + { 0x11d46, 0x11d47 }, { 0x11d60, 0x11d65 }, { 0x11d67, 0x11d68 }, - { 0x11d6a, 0x11d89 }, + { 0x11d6a, 0x11d8e }, + { 0x11d90, 0x11d91 }, + { 0x11d93, 0x11d96 }, { 0x11d98, 0x11d98 }, - { 0x11ee0, 0x11ef2 }, - { 0x11f02, 0x11f02 }, - { 0x11f04, 0x11f10 }, - { 0x11f12, 0x11f33 }, + { 0x11ee0, 0x11ef6 }, + { 0x11f00, 0x11f10 }, + { 0x11f12, 0x11f3a }, + { 0x11f3e, 0x11f40 }, { 0x11fb0, 0x11fb0 }, { 0x12000, 0x12399 }, + { 0x12400, 0x1246e }, { 0x12480, 0x12543 }, { 0x12f90, 0x12ff0 }, { 0x13000, 0x1342f }, { 0x13441, 0x13446 }, + { 0x13460, 0x143fa }, { 0x14400, 0x14646 }, + { 0x16100, 0x1612e }, { 0x16800, 0x16a38 }, { 0x16a40, 0x16a5e }, { 0x16a70, 0x16abe }, @@ -3312,15 +3502,17 @@ inline constexpr CharRange unicode_letter[] = { { 0x16b40, 0x16b43 }, { 0x16b63, 0x16b77 }, { 0x16b7d, 0x16b8f }, + { 0x16d40, 0x16d6c }, { 0x16e40, 0x16e7f }, { 0x16f00, 0x16f4a }, - { 0x16f50, 0x16f50 }, - { 0x16f93, 0x16f9f }, + { 0x16f4f, 0x16f87 }, + { 0x16f8f, 0x16f9f }, { 0x16fe0, 0x16fe1 }, { 0x16fe3, 0x16fe3 }, + { 0x16ff0, 0x16ff1 }, { 0x17000, 0x187f7 }, { 0x18800, 0x18cd5 }, - { 0x18d00, 0x18d08 }, + { 0x18cff, 0x18d08 }, { 0x1aff0, 0x1aff3 }, { 0x1aff5, 0x1affb }, { 0x1affd, 0x1affe }, @@ -3334,6 +3526,7 @@ inline constexpr CharRange unicode_letter[] = { { 0x1bc70, 0x1bc7c }, { 0x1bc80, 0x1bc88 }, { 0x1bc90, 0x1bc99 }, + { 0x1bc9e, 0x1bc9e }, { 0x1d400, 0x1d454 }, { 0x1d456, 0x1d49c }, { 0x1d49e, 0x1d49f }, @@ -3366,19 +3559,28 @@ inline constexpr CharRange unicode_letter[] = { { 0x1d7c4, 0x1d7cb }, { 0x1df00, 0x1df1e }, { 0x1df25, 0x1df2a }, + { 0x1e000, 0x1e006 }, + { 0x1e008, 0x1e018 }, + { 0x1e01b, 0x1e021 }, + { 0x1e023, 0x1e024 }, + { 0x1e026, 0x1e02a }, { 0x1e030, 0x1e06d }, + { 0x1e08f, 0x1e08f }, { 0x1e100, 0x1e12c }, { 0x1e137, 0x1e13d }, { 0x1e14e, 0x1e14e }, { 0x1e290, 0x1e2ad }, { 0x1e2c0, 0x1e2eb }, { 0x1e4d0, 0x1e4eb }, + { 0x1e5d0, 0x1e5ed }, + { 0x1e5f0, 0x1e5f0 }, { 0x1e7e0, 0x1e7e6 }, { 0x1e7e8, 0x1e7eb }, { 0x1e7ed, 0x1e7ee }, { 0x1e7f0, 0x1e7fe }, { 0x1e800, 0x1e8c4 }, { 0x1e900, 0x1e943 }, + { 0x1e947, 0x1e947 }, { 0x1e94b, 0x1e94b }, { 0x1ee00, 0x1ee03 }, { 0x1ee05, 0x1ee1f }, @@ -3413,6 +3615,9 @@ inline constexpr CharRange unicode_letter[] = { { 0x1eea1, 0x1eea3 }, { 0x1eea5, 0x1eea9 }, { 0x1eeab, 0x1eebb }, + { 0x1f130, 0x1f149 }, + { 0x1f150, 0x1f169 }, + { 0x1f170, 0x1f189 }, { 0x20000, 0x2a6df }, { 0x2a700, 0x2b739 }, { 0x2b740, 0x2b81d }, diff --git a/godot/core/string/char_utils.h b/godot/core/string/char_utils.h index 4acb81253..62ab4e958 100644 --- a/godot/core/string/char_utils.h +++ b/godot/core/string/char_utils.h @@ -38,97 +38,97 @@ #define BSEARCH_CHAR_RANGE(m_array) \ int low = 0; \ int high = sizeof(m_array) / sizeof(m_array[0]) - 1; \ - int middle; \ + int middle = (low + high) / 2; \ \ while (low <= high) { \ - middle = (low + high) / 2; \ - \ - if (c < m_array[middle].start) { \ + if (p_char < m_array[middle].start) { \ high = middle - 1; \ - } else if (c > m_array[middle].end) { \ + } else if (p_char > m_array[middle].end) { \ low = middle + 1; \ } else { \ return true; \ } \ + \ + middle = (low + high) / 2; \ } \ \ return false -static _FORCE_INLINE_ bool is_unicode_identifier_start(char32_t c) { +constexpr bool is_unicode_identifier_start(char32_t p_char) { BSEARCH_CHAR_RANGE(xid_start); } -static _FORCE_INLINE_ bool is_unicode_identifier_continue(char32_t c) { +constexpr bool is_unicode_identifier_continue(char32_t p_char) { BSEARCH_CHAR_RANGE(xid_continue); } -static _FORCE_INLINE_ bool is_unicode_upper_case(char32_t c) { +constexpr bool is_unicode_upper_case(char32_t p_char) { BSEARCH_CHAR_RANGE(uppercase_letter); } -static _FORCE_INLINE_ bool is_unicode_lower_case(char32_t c) { +constexpr bool is_unicode_lower_case(char32_t p_char) { BSEARCH_CHAR_RANGE(lowercase_letter); } -static _FORCE_INLINE_ bool is_unicode_letter(char32_t c) { +constexpr bool is_unicode_letter(char32_t p_char) { BSEARCH_CHAR_RANGE(unicode_letter); } #undef BSEARCH_CHAR_RANGE -static _FORCE_INLINE_ bool is_ascii_upper_case(char32_t c) { - return (c >= 'A' && c <= 'Z'); +constexpr bool is_ascii_upper_case(char32_t p_char) { + return (p_char >= 'A' && p_char <= 'Z'); } -static _FORCE_INLINE_ bool is_ascii_lower_case(char32_t c) { - return (c >= 'a' && c <= 'z'); +constexpr bool is_ascii_lower_case(char32_t p_char) { + return (p_char >= 'a' && p_char <= 'z'); } -static _FORCE_INLINE_ bool is_digit(char32_t c) { - return (c >= '0' && c <= '9'); +constexpr bool is_digit(char32_t p_char) { + return (p_char >= '0' && p_char <= '9'); } -static _FORCE_INLINE_ bool is_hex_digit(char32_t c) { - return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +constexpr bool is_hex_digit(char32_t p_char) { + return (is_digit(p_char) || (p_char >= 'a' && p_char <= 'f') || (p_char >= 'A' && p_char <= 'F')); } -static _FORCE_INLINE_ bool is_binary_digit(char32_t c) { - return (c == '0' || c == '1'); +constexpr bool is_binary_digit(char32_t p_char) { + return (p_char == '0' || p_char == '1'); } -static _FORCE_INLINE_ bool is_ascii_alphabet_char(char32_t c) { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +constexpr bool is_ascii_alphabet_char(char32_t p_char) { + return (p_char >= 'a' && p_char <= 'z') || (p_char >= 'A' && p_char <= 'Z'); } -static _FORCE_INLINE_ bool is_ascii_alphanumeric_char(char32_t c) { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); +constexpr bool is_ascii_alphanumeric_char(char32_t p_char) { + return (p_char >= 'a' && p_char <= 'z') || (p_char >= 'A' && p_char <= 'Z') || (p_char >= '0' && p_char <= '9'); } -static _FORCE_INLINE_ bool is_ascii_identifier_char(char32_t c) { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; +constexpr bool is_ascii_identifier_char(char32_t p_char) { + return (p_char >= 'a' && p_char <= 'z') || (p_char >= 'A' && p_char <= 'Z') || (p_char >= '0' && p_char <= '9') || p_char == '_'; } -static _FORCE_INLINE_ bool is_symbol(char32_t c) { - return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' '); +constexpr bool is_symbol(char32_t p_char) { + return p_char != '_' && ((p_char >= '!' && p_char <= '/') || (p_char >= ':' && p_char <= '@') || (p_char >= '[' && p_char <= '`') || (p_char >= '{' && p_char <= '~') || p_char == '\t' || p_char == ' '); } -static _FORCE_INLINE_ bool is_control(char32_t p_char) { +constexpr bool is_control(char32_t p_char) { return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009f); } -static _FORCE_INLINE_ bool is_whitespace(char32_t p_char) { +constexpr bool is_whitespace(char32_t p_char) { return (p_char == ' ') || (p_char == 0x00a0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085); } -static _FORCE_INLINE_ bool is_linebreak(char32_t p_char) { +constexpr bool is_linebreak(char32_t p_char) { return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029); } -static _FORCE_INLINE_ bool is_punct(char32_t p_char) { +constexpr bool is_punct(char32_t p_char) { return (p_char >= ' ' && p_char <= '/') || (p_char >= ':' && p_char <= '@') || (p_char >= '[' && p_char <= '^') || (p_char == '`') || (p_char >= '{' && p_char <= '~') || (p_char >= 0x2000 && p_char <= 0x206f) || (p_char >= 0x3000 && p_char <= 0x303f); } -static _FORCE_INLINE_ bool is_underscore(char32_t p_char) { +constexpr bool is_underscore(char32_t p_char) { return (p_char == '_'); } diff --git a/godot/core/templates/a_hash_map.cpp b/godot/core/templates/a_hash_map.cpp new file mode 100644 index 000000000..04a14c261 --- /dev/null +++ b/godot/core/templates/a_hash_map.cpp @@ -0,0 +1,39 @@ +/**************************************************************************/ +/* a_hash_map.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "a_hash_map.h" +#include "core/variant/variant.h" + +// Explicit instantiation. +template class AHashMap; +template class AHashMap; +template class AHashMap; +template class AHashMap; +template class AHashMap; diff --git a/godot/core/templates/a_hash_map.h b/godot/core/templates/a_hash_map.h new file mode 100644 index 000000000..29983ea26 --- /dev/null +++ b/godot/core/templates/a_hash_map.h @@ -0,0 +1,732 @@ +/**************************************************************************/ +/* a_hash_map.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef A_HASH_MAP_H +#define A_HASH_MAP_H + +#include "core/templates/hash_map.h" + +struct HashMapData { + union { + struct + { + uint32_t hash; + uint32_t hash_to_key; + }; + uint64_t data; + }; +}; + +static_assert(sizeof(HashMapData) == 8); + +/** + * An array-based implementation of a hash map. It is very efficient in terms of performance and + * memory usage. Works like a dynamic array, adding elements to the end of the array, and + * allows you to access array elements by their index by using `get_by_index` method. + * Example: + * ``` + * AHashMap map; + * + * int get_object_id_by_number(int p_number) { + * int id = map.get_index(p_number); + * return id; + * } + * + * Object *get_object_by_id(int p_id) { + * map.get_by_index(p_id).value; + * } + * ``` + * Still, don`t erase the elements because ID can break. + * + * When an element erase, its place is taken by the element from the end. + * + * <------------- + * | | + * 6 8 X 9 32 -1 5 -10 7 X X X + * 6 8 7 9 32 -1 5 -10 X X X X + * + * + * Use RBMap if you need to iterate over sorted elements. + * + * Use HashMap if: + * - You need to keep an iterator or const pointer to Key and you intend to add/remove elements in the meantime. + * - You need to preserve the insertion order when using erase. + * + * It is recommended to use `HashMap` if `KeyValue` size is very large. + */ +template > +class AHashMap { +public: + // Must be a power of two. + static constexpr uint32_t INITIAL_CAPACITY = 16; + static constexpr uint32_t EMPTY_HASH = 0; + static_assert(EMPTY_HASH == 0, "EMPTY_HASH must always be 0 for the memcpy() optimization."); + +private: + typedef KeyValue MapKeyValue; + MapKeyValue *elements = nullptr; + HashMapData *map_data = nullptr; + + // Due to optimization, this is `capacity - 1`. Use + 1 to get normal capacity. + uint32_t capacity = 0; + uint32_t num_elements = 0; + + uint32_t _hash(const TKey &p_key) const { + uint32_t hash = Hasher::hash(p_key); + + if (unlikely(hash == EMPTY_HASH)) { + hash = EMPTY_HASH + 1; + } + + return hash; + } + + static _FORCE_INLINE_ uint32_t _get_resize_count(uint32_t p_capacity) { + return p_capacity ^ (p_capacity + 1) >> 2; // = get_capacity() * 0.75 - 1; Works only if p_capacity = 2^n - 1. + } + + static _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash, uint32_t p_local_capacity) { + const uint32_t original_pos = p_hash & p_local_capacity; + return (p_pos - original_pos + p_local_capacity + 1) & p_local_capacity; + } + + bool _lookup_pos(const TKey &p_key, uint32_t &r_pos, uint32_t &r_hash_pos) const { + if (unlikely(elements == nullptr)) { + return false; // Failed lookups, no elements. + } + return _lookup_pos_with_hash(p_key, r_pos, r_hash_pos, _hash(p_key)); + } + + bool _lookup_pos_with_hash(const TKey &p_key, uint32_t &r_pos, uint32_t &r_hash_pos, uint32_t p_hash) const { + if (unlikely(elements == nullptr)) { + return false; // Failed lookups, no elements. + } + + uint32_t pos = p_hash & capacity; + HashMapData data = map_data[pos]; + if (data.hash == p_hash && Comparator::compare(elements[data.hash_to_key].key, p_key)) { + r_pos = data.hash_to_key; + r_hash_pos = pos; + return true; + } + + if (data.data == EMPTY_HASH) { + return false; + } + + // A collision occurred. + pos = (pos + 1) & capacity; + uint32_t distance = 1; + while (true) { + data = map_data[pos]; + if (data.hash == p_hash && Comparator::compare(elements[data.hash_to_key].key, p_key)) { + r_pos = data.hash_to_key; + r_hash_pos = pos; + return true; + } + + if (data.data == EMPTY_HASH) { + return false; + } + + if (distance > _get_probe_length(pos, data.hash, capacity)) { + return false; + } + + pos = (pos + 1) & capacity; + distance++; + } + } + + uint32_t _insert_with_hash(uint32_t p_hash, uint32_t p_index) { + uint32_t pos = p_hash & capacity; + + if (map_data[pos].data == EMPTY_HASH) { + uint64_t data = ((uint64_t)p_index << 32) | p_hash; + map_data[pos].data = data; + return pos; + } + + uint32_t distance = 1; + pos = (pos + 1) & capacity; + HashMapData c_data; + c_data.hash = p_hash; + c_data.hash_to_key = p_index; + + while (true) { + if (map_data[pos].data == EMPTY_HASH) { +#ifdef DEV_ENABLED + if (unlikely(distance > 12)) { + WARN_PRINT("Excessive collision count (" + + itos(distance) + "), is the right hash function being used?"); + } +#endif + map_data[pos] = c_data; + return pos; + } + + // Not an empty slot, let's check the probing length of the existing one. + uint32_t existing_probe_len = _get_probe_length(pos, map_data[pos].hash, capacity); + if (existing_probe_len < distance) { + SWAP(c_data, map_data[pos]); + distance = existing_probe_len; + } + + pos = (pos + 1) & capacity; + distance++; + } + } + + void _resize_and_rehash(uint32_t p_new_capacity) { + uint32_t real_old_capacity = capacity + 1; + // Capacity can't be 0 and must be 2^n - 1. + capacity = MAX(4u, p_new_capacity); + uint32_t real_capacity = next_power_of_2(capacity); + capacity = real_capacity - 1; + + HashMapData *old_map_data = map_data; + + map_data = reinterpret_cast(Memory::alloc_static(sizeof(HashMapData) * real_capacity)); + elements = reinterpret_cast(Memory::realloc_static(elements, sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1))); + + memset(map_data, EMPTY_HASH, real_capacity * sizeof(HashMapData)); + + if (num_elements != 0) { + for (uint32_t i = 0; i < real_old_capacity; i++) { + HashMapData data = old_map_data[i]; + if (data.data != EMPTY_HASH) { + _insert_with_hash(data.hash, data.hash_to_key); + } + } + } + + Memory::free_static(old_map_data); + } + + int32_t _insert_element(const TKey &p_key, const TValue &p_value, uint32_t p_hash) { + if (unlikely(elements == nullptr)) { + // Allocate on demand to save memory. + + uint32_t real_capacity = capacity + 1; + map_data = reinterpret_cast(Memory::alloc_static(sizeof(HashMapData) * real_capacity)); + elements = reinterpret_cast(Memory::alloc_static(sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1))); + + memset(map_data, EMPTY_HASH, real_capacity * sizeof(HashMapData)); + } + + if (unlikely(num_elements > _get_resize_count(capacity))) { + _resize_and_rehash(capacity * 2); + } + + memnew_placement(&elements[num_elements], MapKeyValue(p_key, p_value)); + + _insert_with_hash(p_hash, num_elements); + num_elements++; + return num_elements - 1; + } + + void _init_from(const AHashMap &p_other) { + capacity = p_other.capacity; + uint32_t real_capacity = capacity + 1; + num_elements = p_other.num_elements; + + if (p_other.num_elements == 0) { + return; + } + + map_data = reinterpret_cast(Memory::alloc_static(sizeof(HashMapData) * real_capacity)); + elements = reinterpret_cast(Memory::alloc_static(sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1))); + + if constexpr (std::is_trivially_copyable_v && std::is_trivially_copyable_v) { + void *destination = elements; + const void *source = p_other.elements; + memcpy(destination, source, sizeof(MapKeyValue) * num_elements); + } else { + for (uint32_t i = 0; i < num_elements; i++) { + memnew_placement(&elements[i], MapKeyValue(p_other.elements[i])); + } + } + + memcpy(map_data, p_other.map_data, sizeof(HashMapData) * real_capacity); + } + +public: + /* Standard Godot Container API */ + + _FORCE_INLINE_ uint32_t get_capacity() const { return capacity + 1; } + _FORCE_INLINE_ uint32_t size() const { return num_elements; } + + _FORCE_INLINE_ bool is_empty() const { + return num_elements == 0; + } + + void clear() { + if (elements == nullptr || num_elements == 0) { + return; + } + + memset(map_data, EMPTY_HASH, (capacity + 1) * sizeof(HashMapData)); + if constexpr (!(std::is_trivially_destructible_v && std::is_trivially_destructible_v)) { + for (uint32_t i = 0; i < num_elements; i++) { + elements[i].key.~TKey(); + elements[i].value.~TValue(); + } + } + + num_elements = 0; + } + + TValue &get(const TKey &p_key) { + uint32_t pos = 0; + uint32_t hash_pos = 0; + bool exists = _lookup_pos(p_key, pos, hash_pos); + CRASH_COND_MSG(!exists, "AHashMap key not found."); + return elements[pos].value; + } + + const TValue &get(const TKey &p_key) const { + uint32_t pos = 0; + uint32_t hash_pos = 0; + bool exists = _lookup_pos(p_key, pos, hash_pos); + CRASH_COND_MSG(!exists, "AHashMap key not found."); + return elements[pos].value; + } + + const TValue *getptr(const TKey &p_key) const { + uint32_t pos = 0; + uint32_t hash_pos = 0; + bool exists = _lookup_pos(p_key, pos, hash_pos); + + if (exists) { + return &elements[pos].value; + } + return nullptr; + } + + TValue *getptr(const TKey &p_key) { + uint32_t pos = 0; + uint32_t hash_pos = 0; + bool exists = _lookup_pos(p_key, pos, hash_pos); + + if (exists) { + return &elements[pos].value; + } + return nullptr; + } + + bool has(const TKey &p_key) const { + uint32_t _pos = 0; + uint32_t h_pos = 0; + return _lookup_pos(p_key, _pos, h_pos); + } + + bool erase(const TKey &p_key) { + uint32_t pos = 0; + uint32_t element_pos = 0; + bool exists = _lookup_pos(p_key, element_pos, pos); + + if (!exists) { + return false; + } + + uint32_t next_pos = (pos + 1) & capacity; + while (map_data[next_pos].hash != EMPTY_HASH && _get_probe_length(next_pos, map_data[next_pos].hash, capacity) != 0) { + SWAP(map_data[next_pos], map_data[pos]); + + pos = next_pos; + next_pos = (next_pos + 1) & capacity; + } + + map_data[pos].data = EMPTY_HASH; + elements[element_pos].key.~TKey(); + elements[element_pos].value.~TValue(); + num_elements--; + + if (element_pos < num_elements) { + void *destination = &elements[element_pos]; + const void *source = &elements[num_elements]; + memcpy(destination, source, sizeof(MapKeyValue)); + uint32_t h_pos = 0; + _lookup_pos(elements[num_elements].key, pos, h_pos); + map_data[h_pos].hash_to_key = element_pos; + } + + return true; + } + + // Replace the key of an entry in-place, without invalidating iterators or changing the entries position during iteration. + // p_old_key must exist in the map and p_new_key must not, unless it is equal to p_old_key. + bool replace_key(const TKey &p_old_key, const TKey &p_new_key) { + if (p_old_key == p_new_key) { + return true; + } + uint32_t pos = 0; + uint32_t element_pos = 0; + ERR_FAIL_COND_V(_lookup_pos(p_new_key, element_pos, pos), false); + ERR_FAIL_COND_V(!_lookup_pos(p_old_key, element_pos, pos), false); + MapKeyValue &element = elements[element_pos]; + const_cast(element.key) = p_new_key; + + uint32_t next_pos = (pos + 1) & capacity; + while (map_data[next_pos].hash != EMPTY_HASH && _get_probe_length(next_pos, map_data[next_pos].hash, capacity) != 0) { + SWAP(map_data[next_pos], map_data[pos]); + + pos = next_pos; + next_pos = (next_pos + 1) & capacity; + } + + map_data[pos].data = EMPTY_HASH; + + uint32_t hash = _hash(p_new_key); + _insert_with_hash(hash, element_pos); + + return true; + } + + // 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_MSG(p_new_capacity < get_capacity(), "It is impossible to reserve less capacity than is currently available."); + if (elements == nullptr) { + capacity = MAX(4u, p_new_capacity); + capacity = next_power_of_2(capacity) - 1; + return; // Unallocated yet. + } + _resize_and_rehash(p_new_capacity); + } + + /** Iterator API **/ + + struct ConstIterator { + _FORCE_INLINE_ const MapKeyValue &operator*() const { + return *pair; + } + _FORCE_INLINE_ const MapKeyValue *operator->() const { + return pair; + } + _FORCE_INLINE_ ConstIterator &operator++() { + pair++; + return *this; + } + + _FORCE_INLINE_ ConstIterator &operator--() { + pair--; + if (pair < begin) { + pair = end; + } + return *this; + } + + _FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return pair == b.pair; } + _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return pair != b.pair; } + + _FORCE_INLINE_ explicit operator bool() const { + return pair != end; + } + + _FORCE_INLINE_ ConstIterator(MapKeyValue *p_key, MapKeyValue *p_begin, MapKeyValue *p_end) { + pair = p_key; + begin = p_begin; + end = p_end; + } + _FORCE_INLINE_ ConstIterator() {} + _FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) { + pair = p_it.pair; + begin = p_it.begin; + end = p_it.end; + } + _FORCE_INLINE_ void operator=(const ConstIterator &p_it) { + pair = p_it.pair; + begin = p_it.begin; + end = p_it.end; + } + + private: + MapKeyValue *pair = nullptr; + MapKeyValue *begin = nullptr; + MapKeyValue *end = nullptr; + }; + + struct Iterator { + _FORCE_INLINE_ MapKeyValue &operator*() const { + return *pair; + } + _FORCE_INLINE_ MapKeyValue *operator->() const { + return pair; + } + _FORCE_INLINE_ Iterator &operator++() { + pair++; + return *this; + } + _FORCE_INLINE_ Iterator &operator--() { + pair--; + if (pair < begin) { + pair = end; + } + return *this; + } + + _FORCE_INLINE_ bool operator==(const Iterator &b) const { return pair == b.pair; } + _FORCE_INLINE_ bool operator!=(const Iterator &b) const { return pair != b.pair; } + + _FORCE_INLINE_ explicit operator bool() const { + return pair != end; + } + + _FORCE_INLINE_ Iterator(MapKeyValue *p_key, MapKeyValue *p_begin, MapKeyValue *p_end) { + pair = p_key; + begin = p_begin; + end = p_end; + } + _FORCE_INLINE_ Iterator() {} + _FORCE_INLINE_ Iterator(const Iterator &p_it) { + pair = p_it.pair; + begin = p_it.begin; + end = p_it.end; + } + _FORCE_INLINE_ void operator=(const Iterator &p_it) { + pair = p_it.pair; + begin = p_it.begin; + end = p_it.end; + } + + operator ConstIterator() const { + return ConstIterator(pair, begin, end); + } + + private: + MapKeyValue *pair = nullptr; + MapKeyValue *begin = nullptr; + MapKeyValue *end = nullptr; + }; + + _FORCE_INLINE_ Iterator begin() { + return Iterator(elements, elements, elements + num_elements); + } + _FORCE_INLINE_ Iterator end() { + return Iterator(elements + num_elements, elements, elements + num_elements); + } + _FORCE_INLINE_ Iterator last() { + if (unlikely(num_elements == 0)) { + return Iterator(nullptr, nullptr, nullptr); + } + return Iterator(elements + num_elements - 1, elements, elements + num_elements); + } + + Iterator find(const TKey &p_key) { + uint32_t pos = 0; + uint32_t h_pos = 0; + bool exists = _lookup_pos(p_key, pos, h_pos); + if (!exists) { + return end(); + } + return Iterator(elements + pos, elements, elements + num_elements); + } + + void remove(const Iterator &p_iter) { + if (p_iter) { + erase(p_iter->key); + } + } + + _FORCE_INLINE_ ConstIterator begin() const { + return ConstIterator(elements, elements, elements + num_elements); + } + _FORCE_INLINE_ ConstIterator end() const { + return ConstIterator(elements + num_elements, elements, elements + num_elements); + } + _FORCE_INLINE_ ConstIterator last() const { + if (unlikely(num_elements == 0)) { + return ConstIterator(nullptr, nullptr, nullptr); + } + return ConstIterator(elements + num_elements - 1, elements, elements + num_elements); + } + + ConstIterator find(const TKey &p_key) const { + uint32_t pos = 0; + uint32_t h_pos = 0; + bool exists = _lookup_pos(p_key, pos, h_pos); + if (!exists) { + return end(); + } + return ConstIterator(elements + pos, elements, elements + num_elements); + } + + /* Indexing */ + + const TValue &operator[](const TKey &p_key) const { + uint32_t pos = 0; + uint32_t h_pos = 0; + bool exists = _lookup_pos(p_key, pos, h_pos); + CRASH_COND(!exists); + return elements[pos].value; + } + + TValue &operator[](const TKey &p_key) { + uint32_t pos = 0; + uint32_t h_pos = 0; + uint32_t hash = _hash(p_key); + bool exists = _lookup_pos_with_hash(p_key, pos, h_pos, hash); + + if (exists) { + return elements[pos].value; + } else { + pos = _insert_element(p_key, TValue(), hash); + return elements[pos].value; + } + } + + /* Insert */ + + Iterator insert(const TKey &p_key, const TValue &p_value) { + uint32_t pos = 0; + uint32_t h_pos = 0; + uint32_t hash = _hash(p_key); + bool exists = _lookup_pos_with_hash(p_key, pos, h_pos, hash); + + if (!exists) { + pos = _insert_element(p_key, p_value, hash); + } else { + elements[pos].value = p_value; + } + return Iterator(elements + pos, elements, elements + num_elements); + } + + // Inserts an element without checking if it already exists. + void insert_new(const TKey &p_key, const TValue &p_value) { + DEV_ASSERT(!has(p_key)); + uint32_t hash = _hash(p_key); + _insert_element(p_key, p_value, hash); + } + + /* Array methods. */ + + // Unsafe. Changing keys and going outside the bounds of an array can lead to undefined behavior. + KeyValue *get_elements_ptr() { + return elements; + } + + // Returns the element index. If not found, returns -1. + int get_index(const TKey &p_key) { + uint32_t pos = 0; + uint32_t h_pos = 0; + bool exists = _lookup_pos(p_key, pos, h_pos); + if (!exists) { + return -1; + } + return pos; + } + + KeyValue &get_by_index(uint32_t p_index) { + CRASH_BAD_UNSIGNED_INDEX(p_index, num_elements); + return elements[p_index]; + } + + bool erase_by_index(uint32_t p_index) { + if (p_index >= size()) { + return false; + } + return erase(elements[p_index].key); + } + + /* Constructors */ + + AHashMap(const AHashMap &p_other) { + _init_from(p_other); + } + + AHashMap(const HashMap &p_other) { + reserve(p_other.size()); + for (const KeyValue &E : p_other) { + uint32_t hash = _hash(E.key); + _insert_element(E.key, E.value, hash); + } + } + + void operator=(const AHashMap &p_other) { + if (this == &p_other) { + return; // Ignore self assignment. + } + + reset(); + + _init_from(p_other); + } + + void operator=(const HashMap &p_other) { + reset(); + if (p_other.size() > get_capacity()) { + reserve(p_other.size()); + } + for (const KeyValue &E : p_other) { + uint32_t hash = _hash(E.key); + _insert_element(E.key, E.value, hash); + } + } + + AHashMap(uint32_t p_initial_capacity) { + // Capacity can't be 0 and must be 2^n - 1. + capacity = MAX(4u, p_initial_capacity); + capacity = next_power_of_2(capacity) - 1; + } + AHashMap() : + capacity(INITIAL_CAPACITY - 1) { + } + + void reset() { + if (elements != nullptr) { + if constexpr (!(std::is_trivially_destructible_v && std::is_trivially_destructible_v)) { + for (uint32_t i = 0; i < num_elements; i++) { + elements[i].key.~TKey(); + elements[i].value.~TValue(); + } + } + Memory::free_static(elements); + Memory::free_static(map_data); + elements = nullptr; + } + capacity = INITIAL_CAPACITY - 1; + num_elements = 0; + } + + ~AHashMap() { + reset(); + } +}; + +extern template class AHashMap; +extern template class AHashMap; +extern template class AHashMap; +extern template class AHashMap; +extern template class AHashMap; + +#endif // A_HASH_MAP_H diff --git a/godot/core/templates/hashfuncs.h b/godot/core/templates/hashfuncs.h index 21eef1029..7818ed070 100644 --- a/godot/core/templates/hashfuncs.h +++ b/godot/core/templates/hashfuncs.h @@ -393,6 +393,13 @@ struct HashMapHasherDefault { } }; +struct HashHasher { + static _FORCE_INLINE_ uint32_t hash(const int32_t hash) { return hash; } + static _FORCE_INLINE_ uint32_t hash(const uint32_t hash) { return hash; } + static _FORCE_INLINE_ uint64_t hash(const int64_t hash) { return hash; } + static _FORCE_INLINE_ uint64_t hash(const uint64_t hash) { return hash; } +}; + // TODO: Fold this into HashMapHasherDefault once C++20 concepts are allowed template struct HashableHasher { diff --git a/godot/doc/classes/AudioServer.xml b/godot/doc/classes/AudioServer.xml index 6830c632c..4a2073616 100644 --- a/godot/doc/classes/AudioServer.xml +++ b/godot/doc/classes/AudioServer.xml @@ -110,6 +110,12 @@ Returns the volume of the bus at index [param bus_idx] in dB. + + + + Returns the name of the current audio driver. The default usually depends on the operating system, but may be overridden via the [code]--audio-driver[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. [code]--headless[/code] also automatically sets the audio driver to [code]Dummy[/code]. See also [member ProjectSettings.audio/driver/driver]. + + diff --git a/godot/doc/classes/BackBufferCopy.xml b/godot/doc/classes/BackBufferCopy.xml index 23d6bc385..eb6576531 100644 --- a/godot/doc/classes/BackBufferCopy.xml +++ b/godot/doc/classes/BackBufferCopy.xml @@ -8,6 +8,7 @@ [b]Note:[/b] Since this node inherits from [Node2D] (and not [Control]), anchors and margins won't apply to child [Control]-derived nodes. This can be problematic when resizing the window. To avoid this, add [Control]-derived nodes as [i]siblings[/i] to the [BackBufferCopy] node instead of adding them as children. + $DOCS_URL/tutorials/shaders/screen-reading_shaders.html diff --git a/godot/doc/classes/Control.xml b/godot/doc/classes/Control.xml index 516b01bd7..d455bd7d6 100644 --- a/godot/doc/classes/Control.xml +++ b/godot/doc/classes/Control.xml @@ -65,7 +65,7 @@ [csharp] public override bool _CanDropData(Vector2 atPosition, Variant data) { - return data.VariantType == Variant.Type.Dictionary && dict.AsGodotDictionary().ContainsKey("color"); + return data.VariantType == Variant.Type.Dictionary && data.AsGodotDictionary().ContainsKey("color"); } public override void _DropData(Vector2 atPosition, Variant data) @@ -1059,7 +1059,7 @@ Defines if tooltip text should automatically change to its translated version depending on the current locale. Uses the same auto translate mode as this control when set to [constant Node.AUTO_TRANSLATE_MODE_INHERIT]. - [b]Note:[/b] When the tooltip is customized using [method _make_custom_tooltip], this auto translate mode is applied automatically to the returned control. + [b]Note:[/b] Tooltips customized using [method _make_custom_tooltip] do not use this auto translate mode automatically. The default tooltip text. The tooltip appears when the user's mouse cursor stays idle over this control for a few moments, provided that the [member mouse_filter] property is not [constant MOUSE_FILTER_IGNORE]. The time required for the tooltip to appear can be changed with the [member ProjectSettings.gui/timers/tooltip_delay_sec] option. See also [method get_tooltip]. diff --git a/godot/doc/classes/DisplayServer.xml b/godot/doc/classes/DisplayServer.xml index 674aa148a..45e802519 100644 --- a/godot/doc/classes/DisplayServer.xml +++ b/godot/doc/classes/DisplayServer.xml @@ -2152,12 +2152,14 @@ Display handle: - Linux (X11): [code]X11::Display*[/code] for the display. + - Linux (Wayland): [code]wl_display[/code] for the display. - Android: [code]EGLDisplay[/code] for the display. Window handle: - Windows: [code]HWND[/code] for the window. - Linux (X11): [code]X11::Window*[/code] for the window. + - Linux (Wayland): [code]wl_surface[/code] for the window. - macOS: [code]NSWindow*[/code] for the window. - iOS: [code]UIViewController*[/code] for the view controller. - Android: [code]jObject[/code] for the activity. @@ -2172,9 +2174,20 @@ OpenGL context (only with the GL Compatibility renderer): - Windows: [code]HGLRC[/code] for the window (native GL), or [code]EGLContext[/code] for the window (ANGLE). - Linux (X11): [code]GLXContext*[/code] for the window. + - Linux (Wayland): [code]EGLContext[/code] for the window. - macOS: [code]NSOpenGLContext*[/code] for the window (native GL), or [code]EGLContext[/code] for the window (ANGLE). - Android: [code]EGLContext[/code] for the window. + + - Windows: [code]EGLDisplay[/code] for the window (ANGLE). + - macOS: [code]EGLDisplay[/code] for the window (ANGLE). + - Linux (Wayland): [code]EGLDisplay[/code] for the window. + + + - Windows: [code]EGLConfig[/code] for the window (ANGLE). + - macOS: [code]EGLConfig[/code] for the window (ANGLE). + - Linux (Wayland): [code]EGLConfig[/code] for the window. + Utterance has begun to be spoken. diff --git a/godot/doc/classes/EditorDebuggerPlugin.xml b/godot/doc/classes/EditorDebuggerPlugin.xml index a519e43bc..d513a4fce 100644 --- a/godot/doc/classes/EditorDebuggerPlugin.xml +++ b/godot/doc/classes/EditorDebuggerPlugin.xml @@ -15,18 +15,20 @@ class ExampleEditorDebugger extends EditorDebuggerPlugin: - func _has_capture(prefix): - # Return true if you wish to handle message with this prefix. - return prefix == "my_plugin" + func _has_capture(capture): + # Return true if you wish to handle messages with the prefix "my_plugin:". + return capture == "my_plugin" func _capture(message, data, session_id): if message == "my_plugin:ping": get_session(session_id).send_message("my_plugin:echo", data) + return true + return false func _setup_session(session_id): # Add a new tab in the debugger session UI containing a label. var label = Label.new() - label.name = "Example plugin" + label.name = "Example plugin" # Will be used as the tab title. label.text = "Example plugin" var session = get_session(session_id) # Listens to the session started and stopped signals. @@ -43,6 +45,24 @@ remove_debugger_plugin(debugger) [/gdscript] [/codeblocks] + To connect on the running game side, use the [EngineDebugger] singleton: + [codeblocks] + [gdscript] + extends Node + + func _ready(): + EngineDebugger.register_message_capture("my_plugin", _capture) + EngineDebugger.send_message("my_plugin:ping", ["test"]) + + func _capture(message, data): + # Note that the "my_plugin:" prefix is not used here. + if message == "echo": + prints("Echo received:", data) + return true + return false + [/gdscript] + [/codeblocks] + [b]Note:[/b] While the game is running, [method @GlobalScope.print] and similar functions [i]called in the editor[/i] do not print anything, the Output Log prints only game messages. @@ -68,7 +88,7 @@ - Override this method to process incoming messages. The [param session_id] is the ID of the [EditorDebuggerSession] that received the message (which you can retrieve via [method get_session]). + Override this method to process incoming messages. The [param session_id] is the ID of the [EditorDebuggerSession] that received the [param message]. Use [method get_session] to retrieve the session. This method should return [code]true[/code] if the message is recognized. @@ -90,7 +110,7 @@ - Override this method to be notified whenever a new [EditorDebuggerSession] is created (the session may be inactive during this stage). + Override this method to be notified whenever a new [EditorDebuggerSession] is created. Note that the session may be inactive during this stage. diff --git a/godot/doc/classes/EditorDebuggerSession.xml b/godot/doc/classes/EditorDebuggerSession.xml index b4e754cc7..f5fa3e34d 100644 --- a/godot/doc/classes/EditorDebuggerSession.xml +++ b/godot/doc/classes/EditorDebuggerSession.xml @@ -14,7 +14,7 @@ - Adds the given [param control] to the debug session UI in the debugger bottom panel. + Adds the given [param control] to the debug session UI in the debugger bottom panel. The [param control]'s node name will be used as the tab title. diff --git a/godot/doc/classes/EngineDebugger.xml b/godot/doc/classes/EngineDebugger.xml index bcc1ac529..3540f098b 100644 --- a/godot/doc/classes/EngineDebugger.xml +++ b/godot/doc/classes/EngineDebugger.xml @@ -113,7 +113,8 @@ Registers a message capture with given [param name]. If [param name] is "my_message" then messages starting with "my_message:" will be called with the given callable. - Callable must accept a message string and a data array as argument. If the message and data are valid then callable must return [code]true[/code] otherwise [code]false[/code]. + The callable must accept a message string and a data array as argument. The callable should return [code]true[/code] if the message is recognized. + [b]Note:[/b] The callable will receive the message with the prefix stripped, unlike [method EditorDebuggerPlugin._capture]. See the [EditorDebuggerPlugin] description for an example. diff --git a/godot/doc/classes/Geometry2D.xml b/godot/doc/classes/Geometry2D.xml index 2dd76ad98..71e6cf93a 100644 --- a/godot/doc/classes/Geometry2D.xml +++ b/godot/doc/classes/Geometry2D.xml @@ -9,6 +9,20 @@ + + + + + + Returns the [url=https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm]Bresenham line[/url] between the [param from] and [param to] points. A Bresenham line is a series of pixels that draws a line and is always 1-pixel thick on every row and column of the drawing (never more, never less). + Example code to draw a line between two [Marker2D] nodes using a series of [method CanvasItem.draw_rect] calls: + [codeblock] + func _draw(): + for pixel in Geometry2D.bresenham_line($MarkerA.position, $MarkerB.position): + draw_rect(Rect2(pixel, Vector2.ONE), Color.WHITE) + [/codeblock] + + diff --git a/godot/doc/classes/HTTPRequest.xml b/godot/doc/classes/HTTPRequest.xml index 6efa675a7..a4adf4d1b 100644 --- a/godot/doc/classes/HTTPRequest.xml +++ b/godot/doc/classes/HTTPRequest.xml @@ -275,6 +275,7 @@ Request successful. + Request failed due to a mismatch between the expected and actual chunked body size during transfer. Possible causes include network errors, server misconfiguration, or issues with chunked encoding. Request failed while connecting. @@ -295,6 +296,7 @@ Request exceeded its maximum size limit, see [member body_size_limit]. + Request failed due to an error while decompressing the response body. Possible causes include unsupported or incorrect compression format, corrupted data, or incomplete transfer. Request failed (currently unused). diff --git a/godot/doc/classes/InputEventMouseMotion.xml b/godot/doc/classes/InputEventMouseMotion.xml index bcfe5b70f..4c1461d03 100644 --- a/godot/doc/classes/InputEventMouseMotion.xml +++ b/godot/doc/classes/InputEventMouseMotion.xml @@ -5,7 +5,7 @@ Stores information about a mouse or a pen motion. This includes relative position, absolute position, and velocity. See [method Node._input]. - [b]Note:[/b] By default, this event is only emitted once per frame rendered at most. If you need more precise input reporting, set [member Input.use_accumulated_input] to [code]false[/code] to make events emitted as often as possible. If you use InputEventMouseMotion to draw lines, consider implementing [url=https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm]Bresenham's line algorithm[/url] as well to avoid visible gaps in lines if the user is moving the mouse quickly. + [b]Note:[/b] By default, this event is only emitted once per frame rendered at most. If you need more precise input reporting, set [member Input.use_accumulated_input] to [code]false[/code] to make events emitted as often as possible. If you use InputEventMouseMotion to draw lines, consider using [method Geometry2D.bresenham_line] as well to avoid visible gaps in lines if the user is moving the mouse quickly. [b]Note:[/b] This event may be emitted even when the mouse hasn't moved, either by the operating system or by Godot itself. If you really need to know if the mouse has moved (e.g. to suppress displaying a tooltip), you should check that [code]relative.is_zero_approx()[/code] is [code]false[/code]. diff --git a/godot/doc/classes/ProjectSettings.xml b/godot/doc/classes/ProjectSettings.xml index 4f7ca2a62..4d41d2e94 100644 --- a/godot/doc/classes/ProjectSettings.xml +++ b/godot/doc/classes/ProjectSettings.xml @@ -377,6 +377,7 @@ Specifies the audio driver to use. This setting is platform-dependent as each platform supports different audio drivers. If left empty, the default audio driver will be used. The [code]Dummy[/code] audio driver disables all audio playback and recording, which is useful for non-game applications as it reduces CPU usage. It also prevents the engine from appearing as an application playing audio in the OS' audio mixer. + To query the value that is being used at run-time (which may be overridden by command-line arguments or headless mode), use [method AudioServer.get_driver_name]. [b]Note:[/b] The driver in use can be overridden at runtime via the [code]--audio-driver[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. diff --git a/godot/doc/classes/ResourceImporterOBJ.xml b/godot/doc/classes/ResourceImporterOBJ.xml index a63dddb0e..084638f62 100644 --- a/godot/doc/classes/ResourceImporterOBJ.xml +++ b/godot/doc/classes/ResourceImporterOBJ.xml @@ -14,6 +14,19 @@ If [code]true[/code], mesh compression will not be used. Consider enabling if you notice blocky artifacts in your mesh normals or UVs, or if you have meshes that are larger than a few thousand meters in each direction. + + If [code]true[/code], generates UV2 on import for [LightmapGI] baking. + + + Controls the size of each texel on the baked lightmap. A smaller value results in more precise lightmaps, at the cost of larger lightmap sizes and longer bake times. + [b]Note:[/b] Only effective if [member generate_lightmap_uv2] is [code]true[/code]. + + + If [code]true[/code], generates lower detail variants of the mesh which will be displayed in the distance to improve rendering performance. Not all meshes benefit from LOD, especially if they are never rendered from far away. Disabling this can reduce output file size and speed up importing. See [url=$DOCS_URL/tutorials/3d/mesh_lod.html#doc-mesh-lod]Mesh level of detail (LOD)[/url] for more information. + + + If [code]true[/code], enables the generation of shadow meshes on import. This optimizes shadow rendering without reducing quality by welding vertices together when possible. This in turn reduces the memory bandwidth required to render shadows. Shadow mesh generation currently doesn't support using a lower detail level than the source mesh (but shadow rendering will make use of LODs when relevant). + If [code]true[/code], generate vertex tangents using [url=http://www.mikktspace.com/]Mikktspace[/url] if the source mesh doesn't have tangent data. When possible, it's recommended to let the 3D modeling software generate tangents on export instead on relying on this option. Tangents are required for correct display of normal and height maps, along with any material/shader features that require tangents. If you don't need material features that require tangents, disabling this can reduce output file size and speed up importing if the source 3D file doesn't contain tangents. diff --git a/godot/drivers/d3d12/rendering_device_driver_d3d12.cpp b/godot/drivers/d3d12/rendering_device_driver_d3d12.cpp index 479afbba9..7c546333c 100644 --- a/godot/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/godot/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -2461,8 +2461,6 @@ Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue, break; } - print_verbose("Using swap chain flags: " + itos(creation_flags) + ", sync interval: " + itos(sync_interval) + ", present flags: " + itos(present_flags)); - if (swap_chain->d3d_swap_chain != nullptr && creation_flags != swap_chain->creation_flags) { // The swap chain must be recreated if the creation flags are different. _swap_chain_release(swap_chain); diff --git a/godot/drivers/egl/egl_manager.cpp b/godot/drivers/egl/egl_manager.cpp index 603dfadd4..8ca0aa594 100644 --- a/godot/drivers/egl/egl_manager.cpp +++ b/godot/drivers/egl/egl_manager.cpp @@ -414,6 +414,30 @@ EGLContext EGLManager::get_context(DisplayServer::WindowID p_window_id) { return display.egl_context; } +EGLDisplay EGLManager::get_display(DisplayServer::WindowID p_window_id) { + GLWindow &glwindow = windows[p_window_id]; + + if (!glwindow.initialized) { + return EGL_NO_CONTEXT; + } + + GLDisplay &display = displays[glwindow.gldisplay_id]; + + return display.egl_display; +} + +EGLConfig EGLManager::get_config(DisplayServer::WindowID p_window_id) { + GLWindow &glwindow = windows[p_window_id]; + + if (!glwindow.initialized) { + return nullptr; + } + + GLDisplay &display = displays[glwindow.gldisplay_id]; + + return display.egl_config; +} + Error EGLManager::initialize(void *p_native_display) { #if defined(GLAD_ENABLED) && !defined(EGL_STATIC) // Loading EGL with a new display gets us just the bare minimum API. We'll then diff --git a/godot/drivers/egl/egl_manager.h b/godot/drivers/egl/egl_manager.h index f1b3dc99b..2e1ae6ec5 100644 --- a/godot/drivers/egl/egl_manager.h +++ b/godot/drivers/egl/egl_manager.h @@ -113,6 +113,8 @@ class EGLManager { bool is_using_vsync() const; EGLContext get_context(DisplayServer::WindowID p_window_id); + EGLDisplay get_display(DisplayServer::WindowID p_window_id); + EGLConfig get_config(DisplayServer::WindowID p_window_id); Error initialize(void *p_native_display = nullptr); diff --git a/godot/drivers/vulkan/rendering_device_driver_vulkan.cpp b/godot/drivers/vulkan/rendering_device_driver_vulkan.cpp index 32086515d..0d908f4ac 100644 --- a/godot/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/godot/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -43,6 +43,10 @@ /**** GENERIC ****/ /*****************/ +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) +static const uint32_t BREADCRUMB_BUFFER_ENTRIES = 512u; +#endif + static const VkFormat RD_TO_VK_FORMAT[RDD::DATA_FORMAT_MAX] = { VK_FORMAT_R4G4_UNORM_PACK8, VK_FORMAT_R4G4B4A4_UNORM_PACK16, @@ -1370,7 +1374,10 @@ Error RenderingDeviceDriverVulkan::initialize(uint32_t p_device_index, uint32_t ERR_FAIL_COND_V(err != OK, err); max_descriptor_sets_per_pool = GLOBAL_GET("rendering/rendering_device/vulkan/max_descriptors_per_pool"); - breadcrumb_buffer = buffer_create(sizeof(uint32_t), BufferUsageBits::BUFFER_USAGE_TRANSFER_TO_BIT, MemoryAllocationType::MEMORY_ALLOCATION_TYPE_CPU); + +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) + breadcrumb_buffer = buffer_create(2u * sizeof(uint32_t) * BREADCRUMB_BUFFER_ENTRIES, BufferUsageBits::BUFFER_USAGE_TRANSFER_TO_BIT, MemoryAllocationType::MEMORY_ALLOCATION_TYPE_CPU); +#endif return OK; } @@ -2850,9 +2857,7 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue, } bool present_mode_available = present_modes.has(present_mode); - if (present_mode_available) { - print_verbose("Using present mode: " + present_mode_name); - } else { + if (!present_mode_available) { // Present mode is not available, fall back to FIFO which is guaranteed to be supported. WARN_PRINT(vformat("The requested V-Sync mode %s is not available. Falling back to V-Sync mode Enabled.", present_mode_name)); surface->vsync_mode = DisplayServer::VSYNC_ENABLED; @@ -5004,10 +5009,65 @@ void RenderingDeviceDriverVulkan::command_end_label(CommandBufferID p_cmd_buffer /**** DEBUG *****/ /****************/ void RenderingDeviceDriverVulkan::command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) { +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) if (p_data == BreadcrumbMarker::NONE) { return; } - vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, 0, sizeof(uint32_t), p_data); + + if (Engine::get_singleton()->is_accurate_breadcrumbs_enabled()) { + // Force a full barrier so commands are not executed in parallel. + // This will mean that the last breadcrumb to see was actually the + // last (group of) command to be executed (hence, the one causing the crash). + VkMemoryBarrier memoryBarrier; + memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + memoryBarrier.pNext = nullptr; + memoryBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT | + VK_ACCESS_INDEX_READ_BIT | + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_UNIFORM_READ_BIT | + VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | + VK_ACCESS_SHADER_READ_BIT | + VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_HOST_READ_BIT | + VK_ACCESS_HOST_WRITE_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT | + VK_ACCESS_INDEX_READ_BIT | + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_UNIFORM_READ_BIT | + VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | + VK_ACCESS_SHADER_READ_BIT | + VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_HOST_READ_BIT | + VK_ACCESS_HOST_WRITE_BIT; + + vkCmdPipelineBarrier( + (VkCommandBuffer)p_cmd_buffer.id, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + 0, 1u, &memoryBarrier, 0u, nullptr, 0u, nullptr); + } + + // We write to a circular buffer. If you're getting barrier sync errors here, + // increase the value of BREADCRUMB_BUFFER_ENTRIES. + vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, breadcrumb_offset, sizeof(uint32_t), breadcrumb_id++); + vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, breadcrumb_offset + sizeof(uint32_t), sizeof(uint32_t), p_data); + breadcrumb_offset += sizeof(uint32_t) * 2u; + if (breadcrumb_offset >= BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u) { + breadcrumb_offset = 0u; + } +#endif } void RenderingDeviceDriverVulkan::on_device_lost() const { @@ -5089,64 +5149,121 @@ void RenderingDeviceDriverVulkan::on_device_lost() const { void RenderingDeviceDriverVulkan::print_lost_device_info() { #if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) - void *breadcrumb_ptr; - vmaFlushAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, sizeof(uint32_t)); - vmaInvalidateAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, sizeof(uint32_t)); - - vmaMapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, &breadcrumb_ptr); - uint32_t last_breadcrumb = *(uint32_t *)breadcrumb_ptr; - vmaUnmapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle); - uint32_t phase = last_breadcrumb & uint32_t(~((1 << 16) - 1)); - uint32_t user_data = last_breadcrumb & ((1 << 16) - 1); - String error_msg = "Last known breadcrumb: "; - - switch (phase) { - case BreadcrumbMarker::ALPHA_PASS: - error_msg += "ALPHA_PASS"; - break; - case BreadcrumbMarker::BLIT_PASS: - error_msg += "BLIT_PASS"; - break; - case BreadcrumbMarker::DEBUG_PASS: - error_msg += "DEBUG_PASS"; - break; - case BreadcrumbMarker::LIGHTMAPPER_PASS: - error_msg += "LIGHTMAPPER_PASS"; - break; - case BreadcrumbMarker::OPAQUE_PASS: - error_msg += "OPAQUE_PASS"; - break; - case BreadcrumbMarker::POST_PROCESSING_PASS: - error_msg += "POST_PROCESSING_PASS"; - break; - case BreadcrumbMarker::REFLECTION_PROBES: - error_msg += "REFLECTION_PROBES"; - break; - case BreadcrumbMarker::SHADOW_PASS_CUBE: - error_msg += "SHADOW_PASS_CUBE"; - break; - case BreadcrumbMarker::SHADOW_PASS_DIRECTIONAL: - error_msg += "SHADOW_PASS_DIRECTIONAL"; - break; - case BreadcrumbMarker::SKY_PASS: - error_msg += "SKY_PASS"; - break; - case BreadcrumbMarker::TRANSPARENT_PASS: - error_msg += "TRANSPARENT_PASS"; - break; - case BreadcrumbMarker::UI_PASS: - error_msg += "UI_PASS"; - break; - default: - error_msg += "UNKNOWN_BREADCRUMB(" + itos((uint32_t)phase) + ')'; - break; + { + String error_msg = "Printing last known breadcrumbs in reverse order (last executed first)."; + if (!Engine::get_singleton()->is_accurate_breadcrumbs_enabled()) { + error_msg += "\nSome of them might be inaccurate. Try running with --accurate-breadcrumbs for precise information."; + } + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, error_msg); } - if (user_data != 0) { - error_msg += " | User data: " + itos(user_data); - } + uint8_t *breadcrumb_ptr = nullptr; + VkResult map_result = VK_SUCCESS; - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, error_msg); + vmaFlushAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u); + vmaInvalidateAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u); + { + void *ptr = nullptr; + map_result = vmaMapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, &ptr); + breadcrumb_ptr = reinterpret_cast(ptr); + } + + if (breadcrumb_ptr && map_result == VK_SUCCESS) { + uint32_t last_breadcrumb_offset = 0; + { + _err_print_error_asap("Searching last breadcrumb. We've sent up to ID: " + itos(breadcrumb_id - 1u)); + + // Scan the whole buffer to find the offset with the highest ID. + // That means that was the last one to be written. + // + // We use "breadcrumb_id - id" to account for wraparound. + // e.g. breadcrumb_id = 2 and id = 4294967294; then 2 - 4294967294 = 4. + // The one with the smallest difference is the closest to breadcrumb_id, which means it's + // the last written command. + uint32_t biggest_id = 0u; + uint32_t smallest_id_diff = std::numeric_limits::max(); + const uint32_t *breadcrumb_ptr32 = reinterpret_cast(breadcrumb_ptr); + for (size_t i = 0u; i < BREADCRUMB_BUFFER_ENTRIES; ++i) { + const uint32_t id = breadcrumb_ptr32[i * 2u]; + const uint32_t id_diff = breadcrumb_id - id; + if (id_diff < smallest_id_diff) { + biggest_id = i; + smallest_id_diff = id_diff; + } + } + + _err_print_error_asap("Last breadcrumb ID found: " + itos(breadcrumb_ptr32[biggest_id * 2u])); + + last_breadcrumb_offset = biggest_id * sizeof(uint32_t) * 2u; + } + + const size_t entries_to_print = 8u; // Note: The value is arbitrary. + for (size_t i = 0u; i < entries_to_print; ++i) { + const uint32_t last_breadcrumb = *reinterpret_cast(breadcrumb_ptr + last_breadcrumb_offset + sizeof(uint32_t)); + const uint32_t phase = last_breadcrumb & uint32_t(~((1 << 16) - 1)); + const uint32_t user_data = last_breadcrumb & ((1 << 16) - 1); + String error_msg = "Last known breadcrumb: "; + + switch (phase) { + case BreadcrumbMarker::ALPHA_PASS: + error_msg += "ALPHA_PASS"; + break; + case BreadcrumbMarker::BLIT_PASS: + error_msg += "BLIT_PASS"; + break; + case BreadcrumbMarker::DEBUG_PASS: + error_msg += "DEBUG_PASS"; + break; + case BreadcrumbMarker::LIGHTMAPPER_PASS: + error_msg += "LIGHTMAPPER_PASS"; + break; + case BreadcrumbMarker::OPAQUE_PASS: + error_msg += "OPAQUE_PASS"; + break; + case BreadcrumbMarker::POST_PROCESSING_PASS: + error_msg += "POST_PROCESSING_PASS"; + break; + case BreadcrumbMarker::REFLECTION_PROBES: + error_msg += "REFLECTION_PROBES"; + break; + case BreadcrumbMarker::SHADOW_PASS_CUBE: + error_msg += "SHADOW_PASS_CUBE"; + break; + case BreadcrumbMarker::SHADOW_PASS_DIRECTIONAL: + error_msg += "SHADOW_PASS_DIRECTIONAL"; + break; + case BreadcrumbMarker::SKY_PASS: + error_msg += "SKY_PASS"; + break; + case BreadcrumbMarker::TRANSPARENT_PASS: + error_msg += "TRANSPARENT_PASS"; + break; + case BreadcrumbMarker::UI_PASS: + error_msg += "UI_PASS"; + break; + default: + error_msg += "UNKNOWN_BREADCRUMB(" + itos((uint32_t)phase) + ')'; + break; + } + + if (user_data != 0) { + error_msg += " | User data: " + itos(user_data); + } + + _err_print_error_asap(error_msg); + + if (last_breadcrumb_offset == 0u) { + // Decrement last_breadcrumb_idx, wrapping underflow. + last_breadcrumb_offset = BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u; + } + last_breadcrumb_offset -= sizeof(uint32_t) * 2u; + } + + vmaUnmapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle); + breadcrumb_ptr = nullptr; + } else { + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Couldn't map breadcrumb buffer. VkResult = " + itos(map_result)); + } #endif on_device_lost(); } @@ -5417,7 +5534,9 @@ RenderingDeviceDriverVulkan::RenderingDeviceDriverVulkan(RenderingContextDriverV } RenderingDeviceDriverVulkan::~RenderingDeviceDriverVulkan() { +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) buffer_free(breadcrumb_buffer); +#endif while (small_allocs_pools.size()) { HashMap::Iterator E = small_allocs_pools.begin(); diff --git a/godot/drivers/vulkan/rendering_device_driver_vulkan.h b/godot/drivers/vulkan/rendering_device_driver_vulkan.h index 4d5de897c..a7ec22e37 100644 --- a/godot/drivers/vulkan/rendering_device_driver_vulkan.h +++ b/godot/drivers/vulkan/rendering_device_driver_vulkan.h @@ -172,7 +172,12 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver { VmaPool _find_or_create_small_allocs_pool(uint32_t p_mem_type_index); private: +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) + // It's a circular buffer. BufferID breadcrumb_buffer; + uint32_t breadcrumb_offset = 0u; + uint32_t breadcrumb_id = 0u; +#endif public: /*****************/ diff --git a/godot/editor/debugger/script_editor_debugger.cpp b/godot/editor/debugger/script_editor_debugger.cpp index cbe791051..73c59707d 100644 --- a/godot/editor/debugger/script_editor_debugger.cpp +++ b/godot/editor/debugger/script_editor_debugger.cpp @@ -827,7 +827,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread bool parsed = EditorDebuggerNode::get_singleton()->plugins_capture(this, p_msg, p_data); if (!parsed) { - WARN_PRINT("unknown message " + p_msg); + WARN_PRINT("Unknown message: " + p_msg); } } } diff --git a/godot/editor/editor_file_system.cpp b/godot/editor/editor_file_system.cpp index c6ed310a9..50595ec7a 100644 --- a/godot/editor/editor_file_system.cpp +++ b/godot/editor/editor_file_system.cpp @@ -1027,7 +1027,9 @@ void EditorFileSystem::scan() { void EditorFileSystem::ScanProgress::increment() { current++; float ratio = current / MAX(hi, 1.0f); - progress->step(ratio * 1000.0f); + if (progress) { + progress->step(ratio * 1000.0f); + } EditorFileSystem::singleton->scan_total = ratio; } @@ -1293,7 +1295,7 @@ void EditorFileSystem::_process_removed_files(const HashSet &p_processed } } -void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress) { +void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress, bool p_recursive) { uint64_t current_mtime = FileAccess::get_modified_time(p_dir->get_path()); bool updated_dir = false; @@ -1487,7 +1489,9 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanPr scan_actions.push_back(ia); continue; } - _scan_fs_changes(p_dir->get_subdir(i), p_progress); + if (p_recursive) { + _scan_fs_changes(p_dir->get_subdir(i), p_progress); + } } nb_files_total = MAX(nb_files_total + diff_nb_files, 0); @@ -2912,6 +2916,96 @@ void EditorFileSystem::reimport_file_with_custom_parameters(const String &p_file emit_signal(SNAME("resources_reimported"), reloads); } +Error EditorFileSystem::_copy_file(const String &p_from, const String &p_to) { + Ref da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (FileAccess::exists(p_from + ".import")) { + Error err = da->copy(p_from, p_to); + if (err != OK) { + return err; + } + + // Remove uid from .import file to avoid conflict. + Ref cfg; + cfg.instantiate(); + cfg->load(p_from + ".import"); + cfg->erase_section_key("remap", "uid"); + err = cfg->save(p_to + ".import"); + if (err != OK) { + return err; + } + } else if (ResourceLoader::get_resource_uid(p_from) == ResourceUID::INVALID_ID) { + // Files which do not use an uid can just be copied. + Error err = da->copy(p_from, p_to); + if (err != OK) { + return err; + } + } else { + // Load the resource and save it again in the new location (this generates a new UID). + Error err; + Ref res = ResourceLoader::load(p_from, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); + if (err == OK && res.is_valid()) { + err = ResourceSaver::save(res, p_to, ResourceSaver::FLAG_COMPRESS); + if (err != OK) { + return err; + } + } else if (err != OK) { + // When loading files like text files the error is OK but the resource is still null. + // We can ignore such files. + return err; + } + } + return OK; +} + +bool EditorFileSystem::_copy_directory(const String &p_from, const String &p_to, List *p_files) { + Ref old_dir = DirAccess::open(p_from); + ERR_FAIL_COND_V(old_dir.is_null(), false); + + Error err = make_dir_recursive(p_to); + if (err != OK && err != ERR_ALREADY_EXISTS) { + return false; + } + + bool success = true; + old_dir->set_include_navigational(false); + old_dir->list_dir_begin(); + + for (String F = old_dir->_get_next(); !F.is_empty(); F = old_dir->_get_next()) { + if (old_dir->current_is_dir()) { + success = _copy_directory(p_from.path_join(F), p_to.path_join(F), p_files) && success; + } else if (F.get_extension() != "import") { + CopiedFile copy; + copy.from = p_from.path_join(F); + copy.to = p_to.path_join(F); + p_files->push_back(copy); + } + } + return success; +} + +void EditorFileSystem::_queue_refresh_filesystem() { + if (refresh_queued) { + return; + } + refresh_queued = true; + get_tree()->connect(SNAME("process_frame"), callable_mp(this, &EditorFileSystem::_refresh_filesystem), CONNECT_ONE_SHOT); +} + +void EditorFileSystem::_refresh_filesystem() { + for (const ObjectID &id : folders_to_sort) { + EditorFileSystemDirectory *dir = Object::cast_to(ObjectDB::get_instance(id)); + if (dir) { + dir->subdirs.sort_custom(); + } + } + folders_to_sort.clear(); + + _update_scan_actions(); + + emit_signal(SNAME("filesystem_changed")); + refresh_queued = false; +} + void EditorFileSystem::_reimport_thread(uint32_t p_index, ImportThreadData *p_import_data) { int current_max = p_import_data->reimport_from + int(p_index); p_import_data->max_index.exchange_if_greater(current_max); @@ -3235,10 +3329,9 @@ Error EditorFileSystem::make_dir_recursive(const String &p_path, const String &p const String path = da->get_current_dir(); EditorFileSystemDirectory *parent = get_filesystem_path(path); ERR_FAIL_NULL_V(parent, ERR_FILE_NOT_FOUND); + folders_to_sort.insert(parent->get_instance_id()); const PackedStringArray folders = p_path.trim_prefix(path).trim_suffix("/").split("/"); - bool first = true; - for (const String &folder : folders) { const int current = parent->find_dir_index(folder); if (current > -1) { @@ -3250,18 +3343,59 @@ Error EditorFileSystem::make_dir_recursive(const String &p_path, const String &p efd->parent = parent; efd->name = folder; parent->subdirs.push_back(efd); - - if (first) { - parent->subdirs.sort_custom(); - first = false; - } parent = efd; } - emit_signal(SNAME("filesystem_changed")); + _queue_refresh_filesystem(); return OK; } +Error EditorFileSystem::copy_file(const String &p_from, const String &p_to) { + _copy_file(p_from, p_to); + + EditorFileSystemDirectory *parent = get_filesystem_path(p_to.get_base_dir()); + ERR_FAIL_NULL_V(parent, ERR_FILE_NOT_FOUND); + + ScanProgress sp; + _scan_fs_changes(parent, sp, false); + + _queue_refresh_filesystem(); + return OK; +} + +Error EditorFileSystem::copy_directory(const String &p_from, const String &p_to) { + List files; + bool success = _copy_directory(p_from, p_to, &files); + + EditorProgress *ep = nullptr; + if (files.size() > 10) { + ep = memnew(EditorProgress("_copy_files", TTR("Copying files..."), files.size())); + } + + int i = 0; + for (const CopiedFile &F : files) { + if (_copy_file(F.from, F.to) != OK) { + success = false; + } + if (ep) { + ep->step(F.from.get_file(), i++, false); + } + } + memdelete_notnull(ep); + + EditorFileSystemDirectory *efd = get_filesystem_path(p_to); + ERR_FAIL_NULL_V(efd, FAILED); + ERR_FAIL_NULL_V(efd->get_parent(), FAILED); + + folders_to_sort.insert(efd->get_parent()->get_instance_id()); + + ScanProgress sp; + _scan_fs_changes(efd, sp); + + _queue_refresh_filesystem(); + return success ? OK : FAILED; +} + ResourceUID::ID EditorFileSystem::_resource_saver_get_resource_id_for_path(const String &p_path, bool p_generate) { if (!p_path.is_resource_file() || p_path.begins_with(ProjectSettings::get_singleton()->get_project_data_path())) { // Saved externally (configuration file) or internal file, do not assign an ID. diff --git a/godot/editor/editor_file_system.h b/godot/editor/editor_file_system.h index 7120a68b3..1479c0e49 100644 --- a/godot/editor/editor_file_system.h +++ b/godot/editor/editor_file_system.h @@ -239,7 +239,7 @@ class EditorFileSystem : public Node { bool _find_file(const String &p_file, EditorFileSystemDirectory **r_d, int &r_file_pos) const; - void _scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress); + void _scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress, bool p_recursive = true); void _delete_internal_files(const String &p_file); int _insert_actions_delete_files_directory(EditorFileSystemDirectory *p_dir); @@ -324,6 +324,19 @@ class EditorFileSystem : public Node { HashSet group_file_cache; HashMap file_icon_cache; + struct CopiedFile { + String from; + String to; + }; + + bool refresh_queued = false; + HashSet folders_to_sort; + + Error _copy_file(const String &p_from, const String &p_to); + bool _copy_directory(const String &p_from, const String &p_to, List *p_files); + void _queue_refresh_filesystem(); + void _refresh_filesystem(); + struct ImportThreadData { const ImportFile *reimport_files; int reimport_from; @@ -378,6 +391,8 @@ class EditorFileSystem : public Node { void move_group_file(const String &p_path, const String &p_new_path); Error make_dir_recursive(const String &p_path, const String &p_base_path = String()); + Error copy_file(const String &p_from, const String &p_to); + Error copy_directory(const String &p_from, const String &p_to); static bool _should_skip_directory(const String &p_path); diff --git a/godot/editor/editor_properties.cpp b/godot/editor/editor_properties.cpp index 2e2a18b9d..158ea4b8a 100644 --- a/godot/editor/editor_properties.cpp +++ b/godot/editor/editor_properties.cpp @@ -2648,7 +2648,7 @@ Variant EditorPropertyNodePath::_get_cache_value(const StringName &p_prop, bool return Variant(); } -void EditorPropertyNodePath::_node_selected(const NodePath &p_path) { +void EditorPropertyNodePath::_node_selected(const NodePath &p_path, bool p_absolute) { NodePath path = p_path; Node *base_node = get_base_node(); @@ -2658,7 +2658,7 @@ void EditorPropertyNodePath::_node_selected(const NodePath &p_path) { path = get_tree()->get_edited_scene_root()->get_path_to(to_node); } - if (base_node) { // for AnimationTrackKeyEdit + if (p_absolute && base_node) { // for AnimationTrackKeyEdit path = base_node->get_path().rel_path_to(p_path); } @@ -2680,7 +2680,7 @@ void EditorPropertyNodePath::_node_assign() { scene_tree->get_scene_tree()->set_show_enabled_subscene(true); scene_tree->set_valid_types(valid_types); add_child(scene_tree); - scene_tree->connect("selected", callable_mp(this, &EditorPropertyNodePath::_node_selected)); + scene_tree->connect("selected", callable_mp(this, &EditorPropertyNodePath::_node_selected).bind(true)); } Variant val = get_edited_property_value(); @@ -2748,7 +2748,7 @@ void EditorPropertyNodePath::_accept_text() { void EditorPropertyNodePath::_text_submitted(const String &p_text) { NodePath np = p_text; - emit_changed(get_edited_property(), np); + _node_selected(np, false); edit->hide(); assign->show(); menu->show(); diff --git a/godot/editor/editor_properties.h b/godot/editor/editor_properties.h index 004630da3..9cc72cd5c 100644 --- a/godot/editor/editor_properties.h +++ b/godot/editor/editor_properties.h @@ -628,7 +628,7 @@ class EditorPropertyNodePath : public EditorProperty { bool editing_node = false; Vector valid_types; - void _node_selected(const NodePath &p_path); + void _node_selected(const NodePath &p_path, bool p_absolute = true); void _node_assign(); Node *get_base_node(); void _update_menu(); diff --git a/godot/editor/editor_property_name_processor.cpp b/godot/editor/editor_property_name_processor.cpp index e38ab456c..ca8854f79 100644 --- a/godot/editor/editor_property_name_processor.cpp +++ b/godot/editor/editor_property_name_processor.cpp @@ -198,6 +198,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["gi"] = "GI"; capitalize_string_remaps["gl"] = "GL"; capitalize_string_remaps["glb"] = "GLB"; + capitalize_string_remaps["gles"] = "GLES"; capitalize_string_remaps["gles2"] = "GLES2"; capitalize_string_remaps["gles3"] = "GLES3"; capitalize_string_remaps["gltf"] = "glTF"; @@ -231,6 +232,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["kb"] = "(KB)"; // Unit. capitalize_string_remaps["lcd"] = "LCD"; capitalize_string_remaps["ldr"] = "LDR"; + capitalize_string_remaps["linuxbsd"] = "Linux/*BSD"; capitalize_string_remaps["lod"] = "LOD"; capitalize_string_remaps["lods"] = "LODs"; capitalize_string_remaps["lowpass"] = "Low-pass"; @@ -248,6 +250,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["oidn"] = "OIDN"; capitalize_string_remaps["ok"] = "OK"; capitalize_string_remaps["opengl"] = "OpenGL"; + capitalize_string_remaps["opengl3"] = "OpenGL 3"; capitalize_string_remaps["opentype"] = "OpenType"; capitalize_string_remaps["openxr"] = "OpenXR"; capitalize_string_remaps["osslsigncode"] = "osslsigncode"; diff --git a/godot/editor/filesystem_dock.cpp b/godot/editor/filesystem_dock.cpp index 53982b37b..2df0b2eda 100644 --- a/godot/editor/filesystem_dock.cpp +++ b/godot/editor/filesystem_dock.cpp @@ -1491,76 +1491,22 @@ void FileSystemDock::_try_duplicate_item(const FileOrFolder &p_item, const Strin EditorNode::get_singleton()->add_io_error(TTR("Cannot move a folder into itself.") + "\n" + old_path + "\n"); return; } - Ref da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (p_item.is_file) { print_verbose("Duplicating " + old_path + " -> " + new_path); // Create the directory structure. - da->make_dir_recursive(new_path.get_base_dir()); - - if (FileAccess::exists(old_path + ".import")) { - Error err = da->copy(old_path, new_path); - if (err != OK) { - EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ": " + error_names[err] + "\n"); - return; - } - - // Remove uid from .import file to avoid conflict. - Ref cfg; - cfg.instantiate(); - cfg->load(old_path + ".import"); - cfg->erase_section_key("remap", "uid"); - err = cfg->save(new_path + ".import"); - if (err != OK) { - EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ".import: " + error_names[err] + "\n"); - return; - } - } else { - // Files which do not use an uid can just be copied. - if (ResourceLoader::get_resource_uid(old_path) == ResourceUID::INVALID_ID) { - Error err = da->copy(old_path, new_path); - if (err != OK) { - EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ": " + error_names[err] + "\n"); - } - return; - } + EditorFileSystem::get_singleton()->make_dir_recursive(p_new_path.get_base_dir()); - // Load the resource and save it again in the new location (this generates a new UID). - Error err; - Ref res = ResourceLoader::load(old_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); - if (err == OK && res.is_valid()) { - err = ResourceSaver::save(res, new_path, ResourceSaver::FLAG_COMPRESS); - if (err != OK) { - EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + " " + vformat(TTR("Failed to save resource at %s: %s"), new_path, error_names[err])); - } - } else if (err != OK) { - // When loading files like text files the error is OK but the resource is still null. - // We can ignore such files. - EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + " " + vformat(TTR("Failed to load resource at %s: %s"), new_path, error_names[err])); - } + Error err = EditorFileSystem::get_singleton()->copy_file(old_path, new_path); + if (err != OK) { + EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ": " + error_names[err] + "\n"); } } else { - da->make_dir(new_path); - - // Recursively duplicate all files inside the folder. - Ref old_dir = DirAccess::open(old_path); - ERR_FAIL_COND(old_dir.is_null()); - - Ref file_access = FileAccess::create(FileAccess::ACCESS_RESOURCES); - old_dir->set_include_navigational(false); - old_dir->list_dir_begin(); - for (String f = old_dir->_get_next(); !f.is_empty(); f = old_dir->_get_next()) { - if (f.get_extension() == "import") { - continue; - } - if (file_access->file_exists(old_path + f)) { - _try_duplicate_item(FileOrFolder(old_path + f, true), new_path + f); - } else if (da->dir_exists(old_path + f)) { - _try_duplicate_item(FileOrFolder(old_path + f, false), new_path + f); - } + Error err = EditorFileSystem::get_singleton()->copy_directory(old_path, new_path); + if (err != OK) { + EditorNode::get_singleton()->add_io_error(TTR("Error duplicating directory:") + "\n" + old_path + "\n"); } - old_dir->list_dir_end(); } } @@ -1866,21 +1812,15 @@ void FileSystemDock::_rename_operation_confirm() { } void FileSystemDock::_duplicate_operation_confirm(const String &p_path) { - String base_dir = p_path.trim_suffix("/").get_base_dir(); - Ref da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - if (!da->dir_exists(base_dir)) { - Error err = da->make_dir_recursive(base_dir); - + const String base_dir = p_path.trim_suffix("/").get_base_dir(); + if (!DirAccess::dir_exists_absolute(base_dir)) { + Error err = EditorFileSystem::get_singleton()->make_dir_recursive(base_dir); if (err != OK) { EditorNode::get_singleton()->show_warning(vformat(TTR("Could not create base directory: %s"), error_names[err])); return; } } _try_duplicate_item(to_duplicate, p_path); - - // Rescan everything. - print_verbose("FileSystem: calling rescan."); - _rescan(); } void FileSystemDock::_overwrite_dialog_action(bool p_overwrite) { @@ -3431,7 +3371,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect const bool is_directory = fpath.ends_with("/"); p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL); - p_popup->set_item_text(p_popup->get_item_index(FILE_OPEN_IN_TERMINAL), is_directory ? TTR("Open in Terminal") : TTR("Open Containing Folder in Terminal")); + p_popup->set_item_text(p_popup->get_item_index(FILE_OPEN_IN_TERMINAL), is_directory ? TTR("Open in Terminal") : TTR("Open Folder in Terminal")); if (!is_directory) { p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("ExternalLink")), ED_GET_SHORTCUT("filesystem_dock/open_in_external_program"), FILE_OPEN_EXTERNAL); @@ -4060,17 +4000,17 @@ FileSystemDock::FileSystemDock() { // `KeyModifierMask::CMD_OR_CTRL | Key::C` conflicts with other editor shortcuts. ED_SHORTCUT("filesystem_dock/copy_path", TTR("Copy Path"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::C); - ED_SHORTCUT("filesystem_dock/copy_absolute_path", TTR("Copy Absolute Path")); - ED_SHORTCUT("filesystem_dock/copy_uid", TTR("Copy UID")); + ED_SHORTCUT("filesystem_dock/copy_absolute_path", TTR("Copy Absolute Path"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::C); + ED_SHORTCUT("filesystem_dock/copy_uid", TTR("Copy UID"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | KeyModifierMask::SHIFT | Key::C); ED_SHORTCUT("filesystem_dock/duplicate", TTR("Duplicate..."), KeyModifierMask::CMD_OR_CTRL | Key::D); ED_SHORTCUT("filesystem_dock/delete", TTR("Delete"), Key::KEY_DELETE); ED_SHORTCUT("filesystem_dock/rename", TTR("Rename..."), Key::F2); ED_SHORTCUT_OVERRIDE("filesystem_dock/rename", "macos", Key::ENTER); #if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) // Opening the system file manager or opening in an external program is not supported on the Android and web editors. - ED_SHORTCUT("filesystem_dock/show_in_explorer", TTR("Open in File Manager")); - ED_SHORTCUT("filesystem_dock/open_in_external_program", TTR("Open in External Program")); - ED_SHORTCUT("filesystem_dock/open_in_terminal", TTR("Open in Terminal")); + ED_SHORTCUT("filesystem_dock/show_in_explorer", TTR("Open in File Manager"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::R); + ED_SHORTCUT("filesystem_dock/open_in_external_program", TTR("Open in External Program"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::E); + ED_SHORTCUT("filesystem_dock/open_in_terminal", TTR("Open in Terminal"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::T); #endif // Properly translating color names would require a separate HashMap, so for simplicity they are provided as comments. diff --git a/godot/editor/gui/editor_spin_slider.cpp b/godot/editor/gui/editor_spin_slider.cpp index a073a2338..27b6bbafb 100644 --- a/godot/editor/gui/editor_spin_slider.cpp +++ b/godot/editor/gui/editor_spin_slider.cpp @@ -37,10 +37,6 @@ #include "editor/themes/editor_scale.h" #include "scene/theme/theme_db.h" -bool EditorSpinSlider::is_text_field() const { - return true; -} - String EditorSpinSlider::get_tooltip(const Point2 &p_pos) const { if (!read_only && grabber->is_visible()) { Key key = (OS::get_singleton()->has_feature("macos") || OS::get_singleton()->has_feature("web_macos") || OS::get_singleton()->has_feature("web_ios")) ? Key::META : Key::CTRL; diff --git a/godot/editor/gui/editor_spin_slider.h b/godot/editor/gui/editor_spin_slider.h index 2476c2f71..dfc50878d 100644 --- a/godot/editor/gui/editor_spin_slider.h +++ b/godot/editor/gui/editor_spin_slider.h @@ -101,8 +101,6 @@ class EditorSpinSlider : public Range { void _focus_entered(); public: - virtual bool is_text_field() const override; - String get_tooltip(const Point2 &p_pos) const override; String get_text_value() const; diff --git a/godot/editor/import/3d/resource_importer_obj.cpp b/godot/editor/import/3d/resource_importer_obj.cpp index a579224ec..5cec366d6 100644 --- a/godot/editor/import/3d/resource_importer_obj.cpp +++ b/godot/editor/import/3d/resource_importer_obj.cpp @@ -202,12 +202,12 @@ static Error _parse_material_library(const String &p_path, HashMap> &r_meshes, bool p_single_mesh, bool p_generate_tangents, Vector3 p_scale_mesh, Vector3 p_offset_mesh, bool p_disable_compression, List *r_missing_deps) { +static Error _parse_obj(const String &p_path, List> &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_generate_lods, bool p_generate_shadow_mesh, bool p_generate_lightmap_uv2, float p_generate_lightmap_uv2_texel_size, const PackedByteArray &p_src_lightmap_cache, Vector3 p_scale_mesh, Vector3 p_offset_mesh, bool p_disable_compression, Vector> &r_lightmap_caches, List *r_missing_deps) { Ref f = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Couldn't open OBJ file '%s', it may not exist or not be readable.", p_path)); - // Avoid trying to load/interpret potential build artifacts from Visual Studio (e.g. when compiling native plugins inside the project tree) - // This should only match, if it's indeed a COFF file header + // Avoid trying to load/interpret potential build artifacts from Visual Studio (e.g. when compiling native plugins inside the project tree). + // This should only match if it's indeed a COFF file header. // https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types const int first_bytes = f->get_16(); static const Vector coff_header_machines{ @@ -445,6 +445,7 @@ static Error _parse_obj(const String &p_path, List> &r_meshes, } mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, array, TypedArray(), Dictionary(), material, name, mesh_flags); + print_verbose("OBJ: Added surface :" + mesh->get_surface_name(mesh->get_surface_count() - 1)); if (!current_material.is_empty()) { @@ -508,6 +509,43 @@ static Error _parse_obj(const String &p_path, List> &r_meshes, } } + if (p_generate_lightmap_uv2) { + Vector lightmap_cache; + mesh->lightmap_unwrap_cached(Transform3D(), p_generate_lightmap_uv2_texel_size, p_src_lightmap_cache, lightmap_cache); + + if (!lightmap_cache.is_empty()) { + if (r_lightmap_caches.is_empty()) { + r_lightmap_caches.push_back(lightmap_cache); + } else { + // MD5 is stored at the beginning of the cache data. + const String new_md5 = String::md5(lightmap_cache.ptr()); + + for (int i = 0; i < r_lightmap_caches.size(); i++) { + const String md5 = String::md5(r_lightmap_caches[i].ptr()); + if (new_md5 < md5) { + r_lightmap_caches.insert(i, lightmap_cache); + break; + } + + if (new_md5 == md5) { + break; + } + } + } + } + } + + mesh->optimize_indices_for_cache(); + + if (p_generate_lods) { + // Use normal merge/split angles that match the defaults used for 3D scene importing. + mesh->generate_lods(60.0f, 25.0f, {}); + } + + if (p_generate_shadow_mesh) { + mesh->create_shadow_mesh(); + } + if (p_single_mesh && mesh->get_surface_count() > 0) { r_meshes.push_back(mesh); } @@ -518,7 +556,10 @@ static Error _parse_obj(const String &p_path, List> &r_meshes, Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, const HashMap &p_options, List *r_missing_deps, Error *r_err) { List> meshes; - Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, Vector3(1, 1, 1), Vector3(0, 0, 0), p_flags & IMPORT_FORCE_DISABLE_MESH_COMPRESSION, r_missing_deps); + // LOD, shadow mesh and lightmap UV2 generation are handled by ResourceImporterScene in this case, + // so disable it within the OBJ mesh import. + Vector> mesh_lightmap_caches; + Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, false, false, false, 0.2, PackedByteArray(), Vector3(1, 1, 1), Vector3(0, 0, 0), p_flags & IMPORT_FORCE_DISABLE_MESH_COMPRESSION, mesh_lightmap_caches, r_missing_deps); if (err != OK) { if (r_err) { @@ -587,19 +628,51 @@ String ResourceImporterOBJ::get_preset_name(int p_idx) const { void ResourceImporterOBJ::get_import_options(const String &p_path, List *r_options, int p_preset) const { r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_tangents"), true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_lods"), true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_shadow_mesh"), true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_lightmap_uv2", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "generate_lightmap_uv2_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2)); r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "scale_mesh"), Vector3(1, 1, 1))); r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "offset_mesh"), Vector3(0, 0, 0))); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_disable_mesh_compression"), false)); } bool ResourceImporterOBJ::get_option_visibility(const String &p_path, const String &p_option, const HashMap &p_options) const { + if (p_option == "generate_lightmap_uv2_texel_size" && !p_options["generate_lightmap_uv2"]) { + // Only display the lightmap texel size import option when lightmap UV2 generation is enabled. + return false; + } + return true; } Error ResourceImporterOBJ::import(const String &p_source_file, const String &p_save_path, const HashMap &p_options, List *r_platform_variants, List *r_gen_files, Variant *r_metadata) { List> meshes; - Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["scale_mesh"], p_options["offset_mesh"], p_options["force_disable_mesh_compression"], nullptr); + Vector src_lightmap_cache; + Vector> mesh_lightmap_caches; + + Error err; + { + src_lightmap_cache = FileAccess::get_file_as_bytes(p_source_file + ".unwrap_cache", &err); + if (err != OK) { + src_lightmap_cache.clear(); + } + } + + err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["generate_lods"], p_options["generate_shadow_mesh"], p_options["generate_lightmap_uv2"], p_options["generate_lightmap_uv2_texel_size"], src_lightmap_cache, p_options["scale_mesh"], p_options["offset_mesh"], p_options["force_disable_mesh_compression"], mesh_lightmap_caches, nullptr); + + if (mesh_lightmap_caches.size()) { + Ref f = FileAccess::open(p_source_file + ".unwrap_cache", FileAccess::WRITE); + if (f.is_valid()) { + f->store_32(mesh_lightmap_caches.size()); + for (int i = 0; i < mesh_lightmap_caches.size(); i++) { + String md5 = String::md5(mesh_lightmap_caches[i].ptr()); + f->store_buffer(mesh_lightmap_caches[i].ptr(), mesh_lightmap_caches[i].size()); + } + } + } + err = OK; ERR_FAIL_COND_V(err != OK, err); ERR_FAIL_COND_V(meshes.size() != 1, ERR_BUG); diff --git a/godot/editor/import/resource_importer_wav.cpp b/godot/editor/import/resource_importer_wav.cpp index 7a6f39906..339e7921b 100644 --- a/godot/editor/import/resource_importer_wav.cpp +++ b/godot/editor/import/resource_importer_wav.cpp @@ -112,7 +112,15 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s } /* GET FILESIZE */ - file->get_32(); // filesize + + // The file size in header is 8 bytes less than the actual size. + // See https://docs.fileformat.com/audio/wav/ + const int FILE_SIZE_HEADER_OFFSET = 8; + uint32_t file_size_header = file->get_32() + FILE_SIZE_HEADER_OFFSET; + uint64_t file_size = file->get_length(); + if (file_size != file_size_header) { + WARN_PRINT(vformat("File size %d is %s than the expected size %d. (%s)", file_size, file_size > file_size_header ? "larger" : "smaller", file_size_header, p_source_file)); + } /* CHECK WAVE */ @@ -198,7 +206,12 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s break; } + uint64_t remaining_bytes = file_size - file_pos; frames = chunksize; + if (remaining_bytes < chunksize) { + WARN_PRINT(vformat("Data chunk size is smaller than expected. Proceeding with actual data size. (%s)", p_source_file)); + frames = remaining_bytes; + } if (format_channels == 0) { ERR_FAIL_COND_V(format_channels == 0, ERR_INVALID_DATA); diff --git a/godot/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp b/godot/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp index 01492c1dd..4a784be06 100644 --- a/godot/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp +++ b/godot/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp @@ -93,7 +93,7 @@ String CollisionShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_g } if (Object::cast_to(*s)) { - return p_id == 0 ? "Radius" : "Height"; + return helper->cylinder_get_handle_name(p_id); } if (Object::cast_to(*s)) { @@ -219,25 +219,15 @@ void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i } if (Object::cast_to(*s)) { - Vector3 axis; - axis[p_id == 0 ? 0 : 1] = 1.0; Ref cs2 = s; - Vector3 ra, rb; - Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); - float d = axis.dot(ra); - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - if (d < 0.001) { - d = 0.001; - } - - if (p_id == 0) { - cs2->set_radius(d); - } else if (p_id == 1) { - cs2->set_height(d * 2.0); - } + real_t height = cs2->get_height(); + real_t radius = cs2->get_radius(); + Vector3 position; + helper->cylinder_set_handle(sg, p_id, height, radius, position); + cs2->set_height(height); + cs2->set_radius(radius); + cs->set_global_position(position); } } @@ -293,31 +283,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo if (Object::cast_to(*s)) { Ref ss = s; - if (p_cancel) { - if (p_id == 0) { - ss->set_radius(p_restore); - } else { - ss->set_height(p_restore); - } - return; - } - - EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); - if (p_id == 0) { - ur->create_action(TTR("Change Cylinder Shape Radius")); - ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); - ur->add_undo_method(ss.ptr(), "set_radius", p_restore); - } else { - ur->create_action( - /// - - //////// - TTR("Change Cylinder Shape Height")); - ur->add_do_method(ss.ptr(), "set_height", ss->get_height()); - ur->add_undo_method(ss.ptr(), "set_height", p_restore); - } - - ur->commit_action(); + helper->cylinder_commit_handle(p_id, TTR("Change Cylinder Shape Radius"), TTR("Change Cylinder Shape Height"), p_cancel, cs, *ss, *ss); } if (Object::cast_to(*s)) { @@ -534,10 +500,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p_gizmo->add_collision_segments(collision_segments); - Vector handles = { - Vector3(cs2->get_radius(), 0, 0), - Vector3(0, cs2->get_height() * 0.5, 0) - }; + Vector handles = helper->cylinder_get_handles(cs2->get_height(), cs2->get_radius()); p_gizmo->add_handles(handles, handles_material); } diff --git a/godot/editor/plugins/gizmos/gizmo_3d_helper.cpp b/godot/editor/plugins/gizmos/gizmo_3d_helper.cpp index 1226be90c..ff1b67aa4 100644 --- a/godot/editor/plugins/gizmos/gizmo_3d_helper.cpp +++ b/godot/editor/plugins/gizmos/gizmo_3d_helper.cpp @@ -139,3 +139,98 @@ void Gizmo3DHelper::box_commit_handle(const String &p_action_name, bool p_cancel ur->add_undo_property(p_position_object, p_position_property, initial_transform.get_origin()); ur->commit_action(); } + +Vector Gizmo3DHelper::cylinder_get_handles(real_t p_height, real_t p_radius) { + Vector handles; + handles.push_back(Vector3(p_radius, 0, 0)); + handles.push_back(Vector3(0, p_height * 0.5, 0)); + handles.push_back(Vector3(0, p_height * -0.5, 0)); + return handles; +} + +String Gizmo3DHelper::cylinder_get_handle_name(int p_id) const { + if (p_id == 0) { + return "Radius"; + } else { + return "Height"; + } +} + +void Gizmo3DHelper::cylinder_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position) { + int sign = p_id == 2 ? -1 : 1; + int axis = p_id == 0 ? 0 : 1; + + Vector3 axis_vector; + axis_vector[axis] = sign; + Vector3 ra, rb; + Geometry3D::get_closest_points_between_segments(axis_vector * -4096, axis_vector * 4096, p_segment[0], p_segment[1], ra, rb); + float d = axis_vector.dot(ra); + + // Snap to grid. + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (p_id == 0) { + // Adjust radius. + if (d < 0.001) { + d = 0.001; + } + r_radius = d; + r_cylinder_position = initial_transform.get_origin(); + } else if (p_id == 1 || p_id == 2) { + real_t initial_height = initial_value; + + // Adjust height. + if (Input::get_singleton()->is_key_pressed(Key::ALT)) { + r_height = d * 2.0; + } else { + r_height = (initial_height * 0.5) + d; + } + + if (r_height < 0.001) { + r_height = 0.001; + } + + // Adjust position. + if (Input::get_singleton()->is_key_pressed(Key::ALT)) { + r_cylinder_position = initial_transform.get_origin(); + } else { + Vector3 offset; + offset[axis] = (r_height - initial_height) * 0.5 * sign; + r_cylinder_position = initial_transform.xform(offset); + } + } +} + +void Gizmo3DHelper::cylinder_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object, Object *p_radius_object, const StringName &p_position_property, const StringName &p_height_property, const StringName &p_radius_property) { + if (!p_height_object) { + p_height_object = p_position_object; + } + if (!p_radius_object) { + p_radius_object = p_position_object; + } + + if (p_cancel) { + if (p_id == 0) { + p_radius_object->set(p_radius_property, initial_value); + } else { + p_height_object->set(p_height_property, initial_value); + } + p_position_object->set(p_position_property, initial_transform.get_origin()); + return; + } + + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + ur->create_action(p_id == 0 ? p_radius_action_name : p_height_action_name); + if (p_id == 0) { + ur->add_do_property(p_radius_object, p_radius_property, p_radius_object->get(p_radius_property)); + ur->add_undo_property(p_radius_object, p_radius_property, initial_value); + } else { + ur->add_do_property(p_height_object, p_height_property, p_height_object->get(p_height_property)); + ur->add_do_property(p_position_object, p_position_property, p_position_object->get(p_position_property)); + ur->add_undo_property(p_height_object, p_height_property, initial_value); + ur->add_undo_property(p_position_object, p_position_property, initial_transform.get_origin()); + } + ur->commit_action(); +} diff --git a/godot/editor/plugins/gizmos/gizmo_3d_helper.h b/godot/editor/plugins/gizmos/gizmo_3d_helper.h index 387ea020b..6d27e5477 100644 --- a/godot/editor/plugins/gizmos/gizmo_3d_helper.h +++ b/godot/editor/plugins/gizmos/gizmo_3d_helper.h @@ -50,6 +50,11 @@ class Gizmo3DHelper : public RefCounted { String box_get_handle_name(int p_id) const; void box_set_handle(const Vector3 p_segment[2], int p_id, Vector3 &r_box_size, Vector3 &r_box_position); void box_commit_handle(const String &p_action_name, bool p_cancel, Object *p_position_object, Object *p_size_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_size_property = "size"); + + Vector cylinder_get_handles(real_t p_height, real_t p_radius); + String cylinder_get_handle_name(int p_id) const; + void cylinder_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position); + void cylinder_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object = nullptr, Object *p_radius_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_height_property = "height", const StringName &p_radius_property = "radius"); }; #endif // GIZMO_3D_HELPER_H diff --git a/godot/editor/scene_tree_dock.cpp b/godot/editor/scene_tree_dock.cpp index 11aa28283..0f359a3af 100644 --- a/godot/editor/scene_tree_dock.cpp +++ b/godot/editor/scene_tree_dock.cpp @@ -156,11 +156,12 @@ void SceneTreeDock::shortcut_input(const Ref &p_event) { } if (ED_IS_SHORTCUT("scene_tree/rename", p_event)) { - // Prevent renaming if a button is focused - // to avoid conflict with Enter shortcut on macOS - if (!focus_owner || !Object::cast_to(focus_owner)) { - _tool_selected(TOOL_RENAME); + // Prevent renaming if a button or a range is focused + // to avoid conflict with Enter shortcut on macOS. + if (focus_owner && (Object::cast_to(focus_owner) || Object::cast_to(focus_owner))) { + return; } + _tool_selected(TOOL_RENAME); #ifdef MODULE_REGEX_ENABLED } else if (ED_IS_SHORTCUT("scene_tree/batch_rename", p_event)) { _tool_selected(TOOL_BATCH_RENAME); diff --git a/godot/main/main.cpp b/godot/main/main.cpp index 743b67f89..e8086db9d 100644 --- a/godot/main/main.cpp +++ b/godot/main/main.cpp @@ -630,6 +630,7 @@ void Main::print_help(const char *p_binary) { print_help_option("--generate-spirv-debug-info", "Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n"); #if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) print_help_option("--extra-gpu-memory-tracking", "Enables additional memory tracking (see class reference for `RenderingDevice.get_driver_and_device_memory_report()` and linked methods). Currently only implemented for Vulkan. Enabling this feature may cause crashes on some systems due to buggy drivers or bugs in the Vulkan Loader. See https://github.com/godotengine/godot/issues/95967\n"); + print_help_option("--accurate-breadcrumbs", "Force barriers between breadcrumbs. Useful for narrowing down a command causing GPU resets. Currently only implemented for Vulkan.\n"); #endif print_help_option("--remote-debug ", "Remote debug (://[:], e.g. tcp://127.0.0.1:6007).\n"); print_help_option("--single-threaded-scene", "Force scene tree to run in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n"); @@ -1236,8 +1237,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph #endif } else if (arg == "--generate-spirv-debug-info") { Engine::singleton->generate_spirv_debug_info = true; +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) } else if (arg == "--extra-gpu-memory-tracking") { Engine::singleton->extra_gpu_memory_tracking = true; + } else if (arg == "--accurate-breadcrumbs") { + Engine::singleton->accurate_breadcrumbs = true; +#endif } else if (arg == "--tablet-driver") { if (N) { tablet_driver = N->get(); @@ -2499,7 +2504,14 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph GLOBAL_DEF_RST_NOVAL("audio/driver/driver", AudioDriverManager::get_driver(0)->get_name()); if (audio_driver.is_empty()) { // Specified in project.godot. - audio_driver = GLOBAL_GET("audio/driver/driver"); + if (project_manager) { + // The project manager doesn't need to play sound (TTS audio output is not emitted by Godot, but by the system itself). + // Disable audio output so it doesn't appear in the list of applications outputting sound in the OS. + // On macOS, this also prevents the project manager from inhibiting suspend. + audio_driver = "Dummy"; + } else { + audio_driver = GLOBAL_GET("audio/driver/driver"); + } } // Make sure that dummy is the last one, which it is assumed to be by design. diff --git a/godot/modules/csg/editor/csg_gizmos.cpp b/godot/modules/csg/editor/csg_gizmos.cpp index 95ffeed6c..e5d33dd17 100644 --- a/godot/modules/csg/editor/csg_gizmos.cpp +++ b/godot/modules/csg/editor/csg_gizmos.cpp @@ -278,24 +278,13 @@ void CSGShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_i if (Object::cast_to(cs)) { CSGCylinder3D *s = Object::cast_to(cs); - Vector3 axis; - axis[p_id == 0 ? 0 : 1] = 1.0; - Vector3 ra, rb; - Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); - float d = axis.dot(ra); - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - if (p_id == 0) { - s->set_radius(d); - } else if (p_id == 1) { - s->set_height(d * 2.0); - } + real_t height = s->get_height(); + real_t radius = s->get_radius(); + Vector3 position; + helper->cylinder_set_handle(sg, p_id, height, radius, position); + s->set_height(height); + s->set_radius(radius); + s->set_global_position(position); } if (Object::cast_to(cs)) { @@ -340,32 +329,11 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int } if (Object::cast_to(cs)) { - helper->box_commit_handle(TTR("Change Box Shape Size"), p_cancel, cs); + helper->box_commit_handle(TTR("Change CSG Box Size"), p_cancel, cs); } if (Object::cast_to(cs)) { - CSGCylinder3D *s = Object::cast_to(cs); - if (p_cancel) { - if (p_id == 0) { - s->set_radius(p_restore); - } else { - s->set_height(p_restore); - } - return; - } - - EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); - if (p_id == 0) { - ur->create_action(TTR("Change Cylinder Radius")); - ur->add_do_method(s, "set_radius", s->get_radius()); - ur->add_undo_method(s, "set_radius", p_restore); - } else { - ur->create_action(TTR("Change Cylinder Height")); - ur->add_do_method(s, "set_height", s->get_height()); - ur->add_undo_method(s, "set_height", p_restore); - } - - ur->commit_action(); + helper->cylinder_commit_handle(p_id, TTR("Change CSG Cylinder Radius"), TTR("Change CSG Cylinder Height"), p_cancel, cs); } if (Object::cast_to(cs)) { @@ -506,9 +474,7 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { if (Object::cast_to(cs)) { CSGCylinder3D *s = Object::cast_to(cs); - Vector handles; - handles.push_back(Vector3(s->get_radius(), 0, 0)); - handles.push_back(Vector3(0, s->get_height() * 0.5, 0)); + Vector handles = helper->cylinder_get_handles(s->get_height(), s->get_radius()); p_gizmo->add_handles(handles, handles_material); } diff --git a/godot/modules/gdscript/doc_classes/@GDScript.xml b/godot/modules/gdscript/doc_classes/@GDScript.xml index 21365da7c..ede4ce661 100644 --- a/godot/modules/gdscript/doc_classes/@GDScript.xml +++ b/godot/modules/gdscript/doc_classes/@GDScript.xml @@ -61,7 +61,7 @@ - + Converts [param what] to [param type] in the best way possible. The [param type] uses the [enum Variant.Type] values. [codeblock] diff --git a/godot/modules/gdscript/gdscript.cpp b/godot/modules/gdscript/gdscript.cpp index 64287cce9..aab9a5acf 100644 --- a/godot/modules/gdscript/gdscript.cpp +++ b/godot/modules/gdscript/gdscript.cpp @@ -1075,6 +1075,26 @@ void GDScript::_bind_methods() { ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &GDScript::_new, MethodInfo("new")); } +void GDScript::set_path_cache(const String &p_path) { + if (ResourceCache::has(p_path)) { + set_path(p_path, true); + return; + } + + if (is_root_script()) { + Script::set_path_cache(p_path); + } + + String old_path = path; + path = p_path; + path_valid = true; + GDScriptCache::move_script(old_path, p_path); + + for (KeyValue> &kv : subclasses) { + kv.value->set_path_cache(p_path); + } +} + void GDScript::set_path(const String &p_path, bool p_take_over) { if (is_root_script()) { Script::set_path(p_path, p_take_over); diff --git a/godot/modules/gdscript/gdscript.h b/godot/modules/gdscript/gdscript.h index 9bb39aac0..006a09deb 100644 --- a/godot/modules/gdscript/gdscript.h +++ b/godot/modules/gdscript/gdscript.h @@ -300,6 +300,7 @@ class GDScript : public Script { virtual Error reload(bool p_keep_state = false) override; + virtual void set_path_cache(const String &p_path) override; virtual void set_path(const String &p_path, bool p_take_over = false) override; String get_script_path() const; Error load_source_code(const String &p_path); diff --git a/godot/modules/gdscript/gdscript_cache.cpp b/godot/modules/gdscript/gdscript_cache.cpp index 3c022412b..fa22798ed 100644 --- a/godot/modules/gdscript/gdscript_cache.cpp +++ b/godot/modules/gdscript/gdscript_cache.cpp @@ -312,7 +312,7 @@ Ref GDScriptCache::get_shallow_script(const String &p_path, Error &r_e Ref script; script.instantiate(); - script->set_path(p_path, true); + script->set_path_cache(p_path); if (remapped_path.get_extension().to_lower() == "gdc") { Vector buffer = get_binary_tokens(remapped_path); if (buffer.is_empty()) { @@ -360,6 +360,7 @@ Ref GDScriptCache::get_full_script(const String &p_path, Error &r_erro return script; } } + script->set_path(p_path, true); const String remapped_path = ResourceLoader::path_remap(p_path); diff --git a/godot/modules/gdscript/gdscript_editor.cpp b/godot/modules/gdscript/gdscript_editor.cpp index b59c071ae..951ae6ce9 100644 --- a/godot/modules/gdscript/gdscript_editor.cpp +++ b/godot/modules/gdscript/gdscript_editor.cpp @@ -3164,7 +3164,9 @@ ::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_pa HashMap options; GDScriptParser::CompletionContext completion_context = parser.get_completion_context(); - completion_context.base = p_owner; + if (completion_context.current_class != nullptr && completion_context.current_class->outer == nullptr) { + completion_context.base = p_owner; + } bool is_function = false; switch (completion_context.type) { diff --git a/godot/modules/gdscript/gdscript_utility_functions.cpp b/godot/modules/gdscript/gdscript_utility_functions.cpp index 7dc586186..824606969 100644 --- a/godot/modules/gdscript/gdscript_utility_functions.cpp +++ b/godot/modules/gdscript/gdscript_utility_functions.cpp @@ -34,7 +34,6 @@ #include "core/io/resource_loader.h" #include "core/object/class_db.h" -#include "core/object/method_bind.h" #include "core/object/object.h" #include "core/templates/oa_hash_map.h" #include "core/templates/vector.h" @@ -42,101 +41,105 @@ #ifdef DEBUG_ENABLED -#define VALIDATE_ARG_COUNT(m_count) \ - if (p_arg_count < m_count) { \ - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \ - r_error.expected = m_count; \ +#define DEBUG_VALIDATE_ARG_COUNT(m_min_count, m_max_count) \ + if (unlikely(p_arg_count < m_min_count)) { \ *r_ret = Variant(); \ + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \ + r_error.expected = m_min_count; \ return; \ } \ - if (p_arg_count > m_count) { \ - r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \ - r_error.expected = m_count; \ + if (unlikely(p_arg_count > m_max_count)) { \ *r_ret = Variant(); \ + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \ + r_error.expected = m_max_count; \ return; \ } -#define VALIDATE_ARG_INT(m_arg) \ - if (p_args[m_arg]->get_type() != Variant::INT) { \ - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \ - r_error.argument = m_arg; \ - r_error.expected = Variant::INT; \ - *r_ret = Variant(); \ - return; \ +#define DEBUG_VALIDATE_ARG_TYPE(m_arg, m_type) \ + if (unlikely(!Variant::can_convert_strict(p_args[m_arg]->get_type(), m_type))) { \ + *r_ret = Variant(); \ + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \ + r_error.argument = m_arg; \ + r_error.expected = m_type; \ + return; \ } -#define VALIDATE_ARG_NUM(m_arg) \ - if (!p_args[m_arg]->is_num()) { \ +#define DEBUG_VALIDATE_ARG_CUSTOM(m_arg, m_type, m_cond, m_msg) \ + if (unlikely(m_cond)) { \ + *r_ret = m_msg; \ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \ r_error.argument = m_arg; \ - r_error.expected = Variant::FLOAT; \ - *r_ret = Variant(); \ + r_error.expected = m_type; \ return; \ } #else // !DEBUG_ENABLED -#define VALIDATE_ARG_COUNT(m_count) -#define VALIDATE_ARG_INT(m_arg) -#define VALIDATE_ARG_NUM(m_arg) +#define DEBUG_VALIDATE_ARG_COUNT(m_min_count, m_max_count) +#define DEBUG_VALIDATE_ARG_TYPE(m_arg, m_type) +#define DEBUG_VALIDATE_ARG_CUSTOM(m_arg, m_type, m_cond, m_msg) #endif // DEBUG_ENABLED +#define VALIDATE_ARG_CUSTOM(m_arg, m_type, m_cond, m_msg) \ + if (unlikely(m_cond)) { \ + *r_ret = m_msg; \ + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \ + r_error.argument = m_arg; \ + r_error.expected = m_type; \ + return; \ + } + +#define GDFUNC_FAIL_COND_MSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ + *r_ret = m_msg; \ + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; \ + return; \ + } + struct GDScriptUtilityFunctionsDefinitions { #ifndef DISABLE_DEPRECATED static inline void convert(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - VALIDATE_ARG_COUNT(2); - VALIDATE_ARG_INT(1); + DEBUG_VALIDATE_ARG_COUNT(2, 2); + DEBUG_VALIDATE_ARG_TYPE(1, Variant::INT); + int type = *p_args[1]; - if (type < 0 || type >= Variant::VARIANT_MAX) { - *r_ret = RTR("Invalid type argument to convert(), use TYPE_* constants."); - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::INT; - return; + DEBUG_VALIDATE_ARG_CUSTOM(1, Variant::INT, type < 0 || type >= Variant::VARIANT_MAX, + RTR("Invalid type argument to convert(), use TYPE_* constants.")); - } else { - Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error); - if (r_error.error != Callable::CallError::CALL_OK) { - *r_ret = vformat(RTR(R"(Cannot convert "%s" to "%s".)"), Variant::get_type_name(p_args[0]->get_type()), Variant::get_type_name(Variant::Type(type))); - } - } + Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error); } #endif // DISABLE_DEPRECATED static inline void type_exists(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - VALIDATE_ARG_COUNT(1); + DEBUG_VALIDATE_ARG_COUNT(1, 1); + DEBUG_VALIDATE_ARG_TYPE(0, Variant::STRING_NAME); *r_ret = ClassDB::class_exists(*p_args[0]); } static inline void _char(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_INT(0); + DEBUG_VALIDATE_ARG_COUNT(1, 1); + DEBUG_VALIDATE_ARG_TYPE(0, Variant::INT); char32_t result[2] = { *p_args[0], 0 }; *r_ret = String(result); } static inline void range(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + DEBUG_VALIDATE_ARG_COUNT(1, 3); switch (p_arg_count) { - case 0: { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.expected = 1; - *r_ret = Variant(); - } break; case 1: { - VALIDATE_ARG_NUM(0); + DEBUG_VALIDATE_ARG_TYPE(0, Variant::INT); + int count = *p_args[0]; + Array arr; if (count <= 0) { *r_ret = arr; return; } + Error err = arr.resize(count); - if (err != OK) { - *r_ret = RTR("Cannot resize array."); - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return; - } + GDFUNC_FAIL_COND_MSG(err != OK, RTR("Cannot resize array.")); for (int i = 0; i < count; i++) { arr[i] = i; @@ -145,8 +148,8 @@ struct GDScriptUtilityFunctionsDefinitions { *r_ret = arr; } break; case 2: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); + DEBUG_VALIDATE_ARG_TYPE(0, Variant::INT); + DEBUG_VALIDATE_ARG_TYPE(1, Variant::INT); int from = *p_args[0]; int to = *p_args[1]; @@ -156,30 +159,26 @@ struct GDScriptUtilityFunctionsDefinitions { *r_ret = arr; return; } + Error err = arr.resize(to - from); - if (err != OK) { - *r_ret = RTR("Cannot resize array."); - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return; - } + GDFUNC_FAIL_COND_MSG(err != OK, RTR("Cannot resize array.")); + for (int i = from; i < to; i++) { arr[i - from] = i; } + *r_ret = arr; } break; case 3: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); + DEBUG_VALIDATE_ARG_TYPE(0, Variant::INT); + DEBUG_VALIDATE_ARG_TYPE(1, Variant::INT); + DEBUG_VALIDATE_ARG_TYPE(2, Variant::INT); int from = *p_args[0]; int to = *p_args[1]; int incr = *p_args[2]; - if (incr == 0) { - *r_ret = RTR("Step argument is zero!"); - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return; - } + + VALIDATE_ARG_CUSTOM(2, Variant::INT, incr == 0, RTR("Step argument is zero!")); Array arr; if (from >= to && incr > 0) { @@ -200,12 +199,7 @@ struct GDScriptUtilityFunctionsDefinitions { } Error err = arr.resize(count); - - if (err != OK) { - *r_ret = RTR("Cannot resize array."); - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return; - } + GDFUNC_FAIL_COND_MSG(err != OK, RTR("Cannot resize array.")); if (incr > 0) { int idx = 0; @@ -221,138 +215,79 @@ struct GDScriptUtilityFunctionsDefinitions { *r_ret = arr; } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.expected = 3; - *r_ret = Variant(); - - } break; } } static inline void load(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - VALIDATE_ARG_COUNT(1); - if (!p_args[0]->is_string()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - *r_ret = Variant(); - } else { - *r_ret = ResourceLoader::load(*p_args[0]); - } + DEBUG_VALIDATE_ARG_COUNT(1, 1); + DEBUG_VALIDATE_ARG_TYPE(0, Variant::STRING); + *r_ret = ResourceLoader::load(*p_args[0]); } static inline void inst_to_dict(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - VALIDATE_ARG_COUNT(1); + DEBUG_VALIDATE_ARG_COUNT(1, 1); + DEBUG_VALIDATE_ARG_TYPE(0, Variant::OBJECT); if (p_args[0]->get_type() == Variant::NIL) { *r_ret = Variant(); - } else if (p_args[0]->get_type() != Variant::OBJECT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; + return; + } + + Object *obj = *p_args[0]; + if (!obj) { *r_ret = Variant(); - } else { - Object *obj = *p_args[0]; - if (!obj) { - *r_ret = Variant(); + return; + } - } else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::DICTIONARY; - *r_ret = RTR("Not a script with an instance"); - return; - } else { - GDScriptInstance *ins = static_cast(obj->get_script_instance()); - Ref base = ins->get_script(); - if (base.is_null()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::DICTIONARY; - *r_ret = RTR("Not based on a script"); - return; - } + VALIDATE_ARG_CUSTOM(0, Variant::OBJECT, + !obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton(), + RTR("Not a script with an instance.")); - GDScript *p = base.ptr(); - String path = p->get_script_path(); - Vector sname; + GDScriptInstance *inst = static_cast(obj->get_script_instance()); - while (p->_owner) { - sname.push_back(p->local_name); - p = p->_owner; - } - sname.reverse(); + Ref base = inst->get_script(); + VALIDATE_ARG_CUSTOM(0, Variant::OBJECT, base.is_null(), RTR("Not based on a script.")); - if (!path.is_resource_file()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::DICTIONARY; - *r_ret = Variant(); + GDScript *p = base.ptr(); + String path = p->get_script_path(); + Vector sname; - *r_ret = RTR("Not based on a resource file"); + while (p->_owner) { + sname.push_back(p->local_name); + p = p->_owner; + } + sname.reverse(); - return; - } + VALIDATE_ARG_CUSTOM(0, Variant::OBJECT, !path.is_resource_file(), RTR("Not based on a resource file.")); - NodePath cp(sname, Vector(), false); + NodePath cp(sname, Vector(), false); - Dictionary d; - d["@subpath"] = cp; - d["@path"] = path; + Dictionary d; + d["@subpath"] = cp; + d["@path"] = path; - for (const KeyValue &E : base->member_indices) { - if (!d.has(E.key)) { - d[E.key] = ins->members[E.value.index]; - } - } - *r_ret = d; + for (const KeyValue &E : base->member_indices) { + if (!d.has(E.key)) { + d[E.key] = inst->members[E.value.index]; } } + + *r_ret = d; } static inline void dict_to_inst(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - VALIDATE_ARG_COUNT(1); - - if (p_args[0]->get_type() != Variant::DICTIONARY) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::DICTIONARY; - *r_ret = Variant(); - - return; - } + DEBUG_VALIDATE_ARG_COUNT(1, 1); + DEBUG_VALIDATE_ARG_TYPE(0, Variant::DICTIONARY); Dictionary d = *p_args[0]; - if (!d.has("@path")) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - *r_ret = RTR("Invalid instance dictionary format (missing @path)"); - - return; - } + VALIDATE_ARG_CUSTOM(0, Variant::DICTIONARY, !d.has("@path"), RTR("Invalid instance dictionary format (missing @path).")); Ref