diff options
Diffstat (limited to 'core')
32 files changed, 399 insertions, 284 deletions
diff --git a/core/SCsub b/core/SCsub index 43deff3ad5..a0176f6c33 100644 --- a/core/SCsub +++ b/core/SCsub @@ -64,6 +64,31 @@ thirdparty_misc_sources = [ thirdparty_misc_sources = [thirdparty_misc_dir + file for file in thirdparty_misc_sources] env_thirdparty.add_source_files(thirdparty_obj, thirdparty_misc_sources) +# Brotli +if env["brotli"]: + thirdparty_brotli_dir = "#thirdparty/brotli/" + thirdparty_brotli_sources = [ + "common/constants.c", + "common/context.c", + "common/dictionary.c", + "common/platform.c", + "common/shared_dictionary.c", + "common/transform.c", + "dec/bit_reader.c", + "dec/decode.c", + "dec/huffman.c", + "dec/state.c", + ] + thirdparty_brotli_sources = [thirdparty_brotli_dir + file for file in thirdparty_brotli_sources] + + env_thirdparty.Prepend(CPPPATH=[thirdparty_brotli_dir + "include"]) + env.Prepend(CPPPATH=[thirdparty_brotli_dir + "include"]) + + if env.get("use_ubsan") or env.get("use_asan") or env.get("use_tsan") or env.get("use_lsan") or env.get("use_msan"): + env_thirdparty.Append(CPPDEFINES=["BROTLI_BUILD_PORTABLE"]) + + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_brotli_sources) + # Zlib library, can be unbundled if env["builtin_zlib"]: thirdparty_zlib_dir = "#thirdparty/zlib/" diff --git a/core/core_bind.cpp b/core/core_bind.cpp index f2eb7823e2..50587bb402 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -257,6 +257,15 @@ Error OS::shell_open(String p_uri) { return ::OS::get_singleton()->shell_open(p_uri); } +Error OS::shell_show_in_file_manager(String p_path, bool p_open_folder) { + if (p_path.begins_with("res://")) { + WARN_PRINT("Attempting to explore file path with the \"res://\" protocol. Use `ProjectSettings.globalize_path()` to convert a Godot-specific path to a system path before opening it with `OS.shell_show_in_file_manager()`."); + } else if (p_path.begins_with("user://")) { + WARN_PRINT("Attempting to explore file path with the \"user://\" protocol. Use `ProjectSettings.globalize_path()` to convert a Godot-specific path to a system path before opening it with `OS.shell_show_in_file_manager()`."); + } + return ::OS::get_singleton()->shell_show_in_file_manager(p_path, p_open_folder); +} + String OS::read_string_from_stdin() { return ::OS::get_singleton()->get_stdin_string(); } @@ -425,6 +434,10 @@ uint64_t OS::get_static_memory_peak_usage() const { return ::OS::get_singleton()->get_static_memory_peak_usage(); } +Dictionary OS::get_memory_info() const { + return ::OS::get_singleton()->get_memory_info(); +} + /** This method uses a signed argument for better error reporting as it's used from the scripting API. */ void OS::delay_usec(int p_usec) const { ERR_FAIL_COND_MSG( @@ -549,6 +562,7 @@ void OS::_bind_methods() { ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance); ClassDB::bind_method(D_METHOD("kill", "pid"), &OS::kill); ClassDB::bind_method(D_METHOD("shell_open", "uri"), &OS::shell_open); + ClassDB::bind_method(D_METHOD("shell_show_in_file_manager", "file_or_dir_path", "open_folder"), &OS::shell_show_in_file_manager, DEFVAL(true)); ClassDB::bind_method(D_METHOD("is_process_running", "pid"), &OS::is_process_running); ClassDB::bind_method(D_METHOD("get_process_id"), &OS::get_process_id); @@ -582,6 +596,7 @@ void OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_static_memory_usage"), &OS::get_static_memory_usage); ClassDB::bind_method(D_METHOD("get_static_memory_peak_usage"), &OS::get_static_memory_peak_usage); + ClassDB::bind_method(D_METHOD("get_memory_info"), &OS::get_memory_info); ClassDB::bind_method(D_METHOD("move_to_trash", "path"), &OS::move_to_trash); ClassDB::bind_method(D_METHOD("get_user_data_dir"), &OS::get_user_data_dir); diff --git a/core/core_bind.h b/core/core_bind.h index 675da48591..4138a85440 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -152,6 +152,7 @@ public: int create_instance(const Vector<String> &p_arguments); Error kill(int p_pid); Error shell_open(String p_uri); + Error shell_show_in_file_manager(String p_path, bool p_open_folder = true); bool is_process_running(int p_pid) const; int get_process_id() const; @@ -190,6 +191,7 @@ public: uint64_t get_static_memory_usage() const; uint64_t get_static_memory_peak_usage() const; + Dictionary get_memory_info() const; void delay_usec(int p_usec) const; void delay_msec(int p_msec) const; diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp index f82600a9a2..81ee09f515 100644 --- a/core/debugger/remote_debugger_peer.cpp +++ b/core/debugger/remote_debugger_peer.cpp @@ -66,7 +66,9 @@ int RemoteDebuggerPeerTCP::get_max_message_size() const { void RemoteDebuggerPeerTCP::close() { running = false; - thread.wait_to_finish(); + if (thread.is_started()) { + thread.wait_to_finish(); + } tcp_client->disconnect_from_host(); out_buf.clear(); in_buf.clear(); diff --git a/core/doc_data.h b/core/doc_data.h index d064818cd5..4e0db89984 100644 --- a/core/doc_data.h +++ b/core/doc_data.h @@ -315,61 +315,6 @@ public: } }; - struct EnumDoc { - String name = "@unnamed_enum"; - bool is_bitfield = false; - String description; - Vector<DocData::ConstantDoc> values; - static EnumDoc from_dict(const Dictionary &p_dict) { - EnumDoc doc; - - if (p_dict.has("name")) { - doc.name = p_dict["name"]; - } - - if (p_dict.has("is_bitfield")) { - doc.is_bitfield = p_dict["is_bitfield"]; - } - - if (p_dict.has("description")) { - doc.description = p_dict["description"]; - } - - Array values; - if (p_dict.has("values")) { - values = p_dict["values"]; - } - for (int i = 0; i < values.size(); i++) { - doc.values.push_back(ConstantDoc::from_dict(values[i])); - } - - return doc; - } - static Dictionary to_dict(const EnumDoc &p_doc) { - Dictionary dict; - - if (!p_doc.name.is_empty()) { - dict["name"] = p_doc.name; - } - - dict["is_bitfield"] = p_doc.is_bitfield; - - if (!p_doc.description.is_empty()) { - dict["description"] = p_doc.description; - } - - if (!p_doc.values.is_empty()) { - Array values; - for (int i = 0; i < p_doc.values.size(); i++) { - values.push_back(ConstantDoc::to_dict(p_doc.values[i])); - } - dict["values"] = values; - } - - return dict; - } - }; - struct PropertyDoc { String name; String type; diff --git a/core/io/compression.cpp b/core/io/compression.cpp index a6114e4f63..ac4a637597 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -35,11 +35,18 @@ #include "thirdparty/misc/fastlz.h" +#ifdef BROTLI_ENABLED +#include "thirdparty/brotli/include/brotli/decode.h" +#endif + #include <zlib.h> #include <zstd.h> int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, Mode p_mode) { switch (p_mode) { + case MODE_BROTLI: { + ERR_FAIL_V_MSG(-1, "Only brotli decompression is supported."); + } break; case MODE_FASTLZ: { if (p_src_size < 16) { uint8_t src[16]; @@ -95,6 +102,9 @@ int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, int Compression::get_max_compressed_buffer_size(int p_src_size, Mode p_mode) { switch (p_mode) { + case MODE_BROTLI: { + ERR_FAIL_V_MSG(-1, "Only brotli decompression is supported."); + } break; case MODE_FASTLZ: { int ss = p_src_size + p_src_size * 6 / 100; if (ss < 66) { @@ -129,6 +139,16 @@ int Compression::get_max_compressed_buffer_size(int p_src_size, Mode p_mode) { int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size, Mode p_mode) { switch (p_mode) { + case MODE_BROTLI: { +#ifdef BROTLI_ENABLED + size_t ret_size = p_dst_max_size; + BrotliDecoderResult res = BrotliDecoderDecompress(p_src_size, p_src, &ret_size, p_dst); + ERR_FAIL_COND_V(res != BROTLI_DECODER_RESULT_SUCCESS, -1); + return ret_size; +#else + ERR_FAIL_V_MSG(-1, "Godot was compiled without brotli support."); +#endif + } break; case MODE_FASTLZ: { int ret_size = 0; @@ -186,87 +206,147 @@ int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p This is much slower however than using Compression::decompress because it may result in multiple full copies of the output buffer. */ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_size, const uint8_t *p_src, int p_src_size, Mode p_mode) { - int ret; uint8_t *dst = nullptr; int out_mark = 0; - z_stream strm; ERR_FAIL_COND_V(p_src_size <= 0, Z_DATA_ERROR); - // This function only supports GZip and Deflate - int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; - ERR_FAIL_COND_V(p_mode != MODE_DEFLATE && p_mode != MODE_GZIP, Z_ERRNO); - - // Initialize the stream - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = 0; - strm.next_in = Z_NULL; - - int err = inflateInit2(&strm, window_bits); - ERR_FAIL_COND_V(err != Z_OK, -1); - - // Setup the stream inputs - strm.next_in = (Bytef *)p_src; - strm.avail_in = p_src_size; - - // Ensure the destination buffer is empty - p_dst_vect->clear(); - - // decompress until deflate stream ends or end of file - do { - // Add another chunk size to the output buffer - // This forces a copy of the whole buffer - p_dst_vect->resize(p_dst_vect->size() + gzip_chunk); - // Get pointer to the actual output buffer - dst = p_dst_vect->ptrw(); - - // Set the stream to the new output stream - // Since it was copied, we need to reset the stream to the new buffer - strm.next_out = &(dst[out_mark]); - strm.avail_out = gzip_chunk; - - // run inflate() on input until output buffer is full and needs to be resized - // or input runs out + if (p_mode == MODE_BROTLI) { +#ifdef BROTLI_ENABLED + BrotliDecoderResult ret; + BrotliDecoderState *state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); + ERR_FAIL_COND_V(state == nullptr, Z_DATA_ERROR); + + // Setup the stream inputs. + const uint8_t *next_in = p_src; + size_t avail_in = p_src_size; + uint8_t *next_out = nullptr; + size_t avail_out = 0; + size_t total_out = 0; + + // Ensure the destination buffer is empty. + p_dst_vect->clear(); + + // Decompress until stream ends or end of file. do { - ret = inflate(&strm, Z_SYNC_FLUSH); - - switch (ret) { - case Z_NEED_DICT: - ret = Z_DATA_ERROR; - [[fallthrough]]; - case Z_DATA_ERROR: - case Z_MEM_ERROR: - case Z_STREAM_ERROR: - case Z_BUF_ERROR: - if (strm.msg) { - WARN_PRINT(strm.msg); - } - (void)inflateEnd(&strm); - p_dst_vect->clear(); - return ret; + // Add another chunk size to the output buffer. + // This forces a copy of the whole buffer. + p_dst_vect->resize(p_dst_vect->size() + gzip_chunk); + // Get pointer to the actual output buffer. + dst = p_dst_vect->ptrw(); + + // Set the stream to the new output stream. + // Since it was copied, we need to reset the stream to the new buffer. + next_out = &(dst[out_mark]); + avail_out += gzip_chunk; + + ret = BrotliDecoderDecompressStream(state, &avail_in, &next_in, &avail_out, &next_out, &total_out); + if (ret == BROTLI_DECODER_RESULT_ERROR) { + WARN_PRINT(BrotliDecoderErrorString(BrotliDecoderGetErrorCode(state))); + BrotliDecoderDestroyInstance(state); + p_dst_vect->clear(); + return Z_DATA_ERROR; } - } while (strm.avail_out > 0 && strm.avail_in > 0); - out_mark += gzip_chunk; + out_mark += gzip_chunk - avail_out; - // Enforce max output size - if (p_max_dst_size > -1 && strm.total_out > (uint64_t)p_max_dst_size) { - (void)inflateEnd(&strm); - p_dst_vect->clear(); - return Z_BUF_ERROR; + // Enforce max output size. + if (p_max_dst_size > -1 && total_out > (uint64_t)p_max_dst_size) { + BrotliDecoderDestroyInstance(state); + p_dst_vect->clear(); + return Z_BUF_ERROR; + } + } while (ret != BROTLI_DECODER_RESULT_SUCCESS); + + // If all done successfully, resize the output if it's larger than the actual output. + if ((unsigned long)p_dst_vect->size() > total_out) { + p_dst_vect->resize(total_out); } - } while (ret != Z_STREAM_END); - // If all done successfully, resize the output if it's larger than the actual output - if ((unsigned long)p_dst_vect->size() > strm.total_out) { - p_dst_vect->resize(strm.total_out); - } + // Clean up and return. + BrotliDecoderDestroyInstance(state); + return Z_OK; +#else + ERR_FAIL_V_MSG(Z_ERRNO, "Godot was compiled without brotli support."); +#endif + } else { + // This function only supports GZip and Deflate. + ERR_FAIL_COND_V(p_mode != MODE_DEFLATE && p_mode != MODE_GZIP, Z_ERRNO); + + int ret; + z_stream strm; + int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; + + // Initialize the stream. + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + + int err = inflateInit2(&strm, window_bits); + ERR_FAIL_COND_V(err != Z_OK, -1); + + // Setup the stream inputs. + strm.next_in = (Bytef *)p_src; + strm.avail_in = p_src_size; + + // Ensure the destination buffer is empty. + p_dst_vect->clear(); + + // Decompress until deflate stream ends or end of file. + do { + // Add another chunk size to the output buffer. + // This forces a copy of the whole buffer. + p_dst_vect->resize(p_dst_vect->size() + gzip_chunk); + // Get pointer to the actual output buffer. + dst = p_dst_vect->ptrw(); + + // Set the stream to the new output stream. + // Since it was copied, we need to reset the stream to the new buffer. + strm.next_out = &(dst[out_mark]); + strm.avail_out = gzip_chunk; + + // Run inflate() on input until output buffer is full and needs to be resized or input runs out. + do { + ret = inflate(&strm, Z_SYNC_FLUSH); + + switch (ret) { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; + [[fallthrough]]; + case Z_DATA_ERROR: + case Z_MEM_ERROR: + case Z_STREAM_ERROR: + case Z_BUF_ERROR: + if (strm.msg) { + WARN_PRINT(strm.msg); + } + (void)inflateEnd(&strm); + p_dst_vect->clear(); + return ret; + } + } while (strm.avail_out > 0 && strm.avail_in > 0); + + out_mark += gzip_chunk; + + // Enforce max output size. + if (p_max_dst_size > -1 && strm.total_out > (uint64_t)p_max_dst_size) { + (void)inflateEnd(&strm); + p_dst_vect->clear(); + return Z_BUF_ERROR; + } + } while (ret != Z_STREAM_END); + + // If all done successfully, resize the output if it's larger than the actual output. + if ((unsigned long)p_dst_vect->size() > strm.total_out) { + p_dst_vect->resize(strm.total_out); + } - // clean up and return - (void)inflateEnd(&strm); - return Z_OK; + // Clean up and return. + (void)inflateEnd(&strm); + return Z_OK; + } } int Compression::zlib_level = Z_DEFAULT_COMPRESSION; diff --git a/core/io/compression.h b/core/io/compression.h index 063da6dc7d..a5a2d657da 100644 --- a/core/io/compression.h +++ b/core/io/compression.h @@ -47,7 +47,8 @@ public: MODE_FASTLZ, MODE_DEFLATE, MODE_ZSTD, - MODE_GZIP + MODE_GZIP, + MODE_BROTLI }; static int compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, Mode p_mode = MODE_ZSTD); diff --git a/core/io/dir_access.h b/core/io/dir_access.h index 51eb68eaea..52ed688deb 100644 --- a/core/io/dir_access.h +++ b/core/io/dir_access.h @@ -68,7 +68,7 @@ protected: virtual String _get_root_string() const; AccessType get_access_type() const; - String fix_path(String p_path) const; + virtual String fix_path(String p_path) const; template <class T> static Ref<DirAccess> _create_builtin() { diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index 3d10151327..a6a1a224b3 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -871,4 +871,5 @@ void FileAccess::_bind_methods() { BIND_ENUM_CONSTANT(COMPRESSION_DEFLATE); BIND_ENUM_CONSTANT(COMPRESSION_ZSTD); BIND_ENUM_CONSTANT(COMPRESSION_GZIP); + BIND_ENUM_CONSTANT(COMPRESSION_BROTLI); } diff --git a/core/io/file_access.h b/core/io/file_access.h index 47770cad87..ad1ac665f3 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -64,7 +64,8 @@ public: COMPRESSION_FASTLZ = Compression::MODE_FASTLZ, COMPRESSION_DEFLATE = Compression::MODE_DEFLATE, COMPRESSION_ZSTD = Compression::MODE_ZSTD, - COMPRESSION_GZIP = Compression::MODE_GZIP + COMPRESSION_GZIP = Compression::MODE_GZIP, + COMPRESSION_BROTLI = Compression::MODE_BROTLI, }; typedef void (*FileCloseFailNotify)(const String &); @@ -80,7 +81,7 @@ protected: static void _bind_methods(); AccessType get_access_type() const; - String fix_path(const String &p_path) const; + virtual String fix_path(const String &p_path) const; virtual Error open_internal(const String &p_path, int p_mode_flags) = 0; ///< open a file virtual uint64_t _get_modified_time(const String &p_file) = 0; virtual void _set_access_type(AccessType p_access); diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index da59ae8c59..3e5a1217dd 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -34,13 +34,7 @@ void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_mode, uint32_t p_block_size) { magic = p_magic.ascii().get_data(); - if (magic.length() > 4) { - magic = magic.substr(0, 4); - } else { - while (magic.length() < 4) { - magic += " "; - } - } + magic = (magic + " ").substr(0, 4); cmode = p_mode; block_size = p_block_size; diff --git a/core/io/json.cpp b/core/io/json.cpp index 8d0fe53ed4..a6e054a9fe 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -47,13 +47,7 @@ const char *JSON::tk_name[TK_MAX] = { }; String JSON::_make_indent(const String &p_indent, int p_size) { - String indent_text = ""; - if (!p_indent.is_empty()) { - for (int i = 0; i < p_size; i++) { - indent_text += p_indent; - } - } - return indent_text; + return p_indent.repeat(p_size); } String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, HashSet<const void *> &p_markers, bool p_full_precision) { diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp index 139dc3afb1..63f7c80bdd 100644 --- a/core/math/a_star_grid_2d.cpp +++ b/core/math/a_star_grid_2d.cpp @@ -32,6 +32,8 @@ #include "core/variant/typed_array.h" +#define GET_POINT_UNCHECKED(m_id) points[m_id.y - region.position.y][m_id.x - region.position.x] + static real_t heuristic_euclidian(const Vector2i &p_from, const Vector2i &p_to) { real_t dx = (real_t)ABS(p_to.x - p_from.x); real_t dy = (real_t)ABS(p_to.y - p_from.y); @@ -59,16 +61,29 @@ static real_t heuristic_chebyshev(const Vector2i &p_from, const Vector2i &p_to) static real_t (*heuristics[AStarGrid2D::HEURISTIC_MAX])(const Vector2i &, const Vector2i &) = { heuristic_euclidian, heuristic_manhattan, heuristic_octile, heuristic_chebyshev }; +void AStarGrid2D::set_region(const Rect2i &p_region) { + ERR_FAIL_COND(p_region.size.x < 0 || p_region.size.y < 0); + if (p_region != region) { + region = p_region; + dirty = true; + } +} + +Rect2i AStarGrid2D::get_region() const { + return region; +} + void AStarGrid2D::set_size(const Size2i &p_size) { + WARN_DEPRECATED_MSG(R"(The "size" property is deprecated, use "region" instead.)"); ERR_FAIL_COND(p_size.x < 0 || p_size.y < 0); - if (p_size != size) { - size = p_size; + if (p_size != region.size) { + region.size = p_size; dirty = true; } } Size2i AStarGrid2D::get_size() const { - return size; + return region.size; } void AStarGrid2D::set_offset(const Vector2 &p_offset) { @@ -95,9 +110,11 @@ Size2 AStarGrid2D::get_cell_size() const { void AStarGrid2D::update() { points.clear(); - for (int64_t y = 0; y < size.y; y++) { + const int64_t end_x = region.position.x + region.size.width; + const int64_t end_y = region.position.y + region.size.height; + for (int64_t y = region.position.y; y < end_y; y++) { LocalVector<Point> line; - for (int64_t x = 0; x < size.x; x++) { + for (int64_t x = region.position.x; x < end_x; x++) { line.push_back(Point(Vector2i(x, y), offset + Vector2(x, y) * cell_size)); } points.push_back(line); @@ -106,11 +123,11 @@ void AStarGrid2D::update() { } bool AStarGrid2D::is_in_bounds(int p_x, int p_y) const { - return p_x >= 0 && p_x < size.width && p_y >= 0 && p_y < size.height; + return region.has_point(Vector2i(p_x, p_y)); } bool AStarGrid2D::is_in_boundsv(const Vector2i &p_id) const { - return p_id.x >= 0 && p_id.x < size.width && p_id.y >= 0 && p_id.y < size.height; + return region.has_point(p_id); } bool AStarGrid2D::is_dirty() const { @@ -154,27 +171,27 @@ AStarGrid2D::Heuristic AStarGrid2D::get_default_estimate_heuristic() const { void AStarGrid2D::set_point_solid(const Vector2i &p_id, bool p_solid) { ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method."); - ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set if point is disabled. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height)); - points[p_id.y][p_id.x].solid = p_solid; + ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set if point is disabled. Point %s out of bounds %s.", p_id, region)); + GET_POINT_UNCHECKED(p_id).solid = p_solid; } bool AStarGrid2D::is_point_solid(const Vector2i &p_id) const { ERR_FAIL_COND_V_MSG(dirty, false, "Grid is not initialized. Call the update method."); - ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), false, vformat("Can't get if point is disabled. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height)); - return points[p_id.y][p_id.x].solid; + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), false, vformat("Can't get if point is disabled. Point %s out of bounds %s.", p_id, region)); + return GET_POINT_UNCHECKED(p_id).solid; } void AStarGrid2D::set_point_weight_scale(const Vector2i &p_id, real_t p_weight_scale) { ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method."); - ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set point's weight scale. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height)); + ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set point's weight scale. Point %s out of bounds %s.", p_id, region)); ERR_FAIL_COND_MSG(p_weight_scale < 0.0, vformat("Can't set point's weight scale less than 0.0: %f.", p_weight_scale)); - points[p_id.y][p_id.x].weight_scale = p_weight_scale; + GET_POINT_UNCHECKED(p_id).weight_scale = p_weight_scale; } real_t AStarGrid2D::get_point_weight_scale(const Vector2i &p_id) const { ERR_FAIL_COND_V_MSG(dirty, 0, "Grid is not initialized. Call the update method."); - ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), 0, vformat("Can't get point's weight scale. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height)); - return points[p_id.y][p_id.x].weight_scale; + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), 0, vformat("Can't get point's weight scale. Point %s out of bounds %s.", p_id, region)); + return GET_POINT_UNCHECKED(p_id).weight_scale; } AStarGrid2D::Point *AStarGrid2D::_jump(Point *p_from, Point *p_to) { @@ -285,15 +302,15 @@ void AStarGrid2D::_get_nbors(Point *p_point, LocalVector<Point *> &r_nbors) { bool has_left = false; bool has_right = false; - if (p_point->id.x - 1 >= 0) { + if (p_point->id.x - 1 >= region.position.x) { left = _get_point_unchecked(p_point->id.x - 1, p_point->id.y); has_left = true; } - if (p_point->id.x + 1 < size.width) { + if (p_point->id.x + 1 < region.position.x + region.size.width) { right = _get_point_unchecked(p_point->id.x + 1, p_point->id.y); has_right = true; } - if (p_point->id.y - 1 >= 0) { + if (p_point->id.y - 1 >= region.position.y) { top = _get_point_unchecked(p_point->id.x, p_point->id.y - 1); if (has_left) { top_left = _get_point_unchecked(p_point->id.x - 1, p_point->id.y - 1); @@ -302,7 +319,7 @@ void AStarGrid2D::_get_nbors(Point *p_point, LocalVector<Point *> &r_nbors) { top_right = _get_point_unchecked(p_point->id.x + 1, p_point->id.y - 1); } } - if (p_point->id.y + 1 < size.height) { + if (p_point->id.y + 1 < region.position.y + region.size.height) { bottom = _get_point_unchecked(p_point->id.x, p_point->id.y + 1); if (has_left) { bottom_left = _get_point_unchecked(p_point->id.x - 1, p_point->id.y + 1); @@ -461,19 +478,19 @@ real_t AStarGrid2D::_compute_cost(const Vector2i &p_from_id, const Vector2i &p_t void AStarGrid2D::clear() { points.clear(); - size = Vector2i(); + region = Rect2i(); } Vector2 AStarGrid2D::get_point_position(const Vector2i &p_id) const { ERR_FAIL_COND_V_MSG(dirty, Vector2(), "Grid is not initialized. Call the update method."); - ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), Vector2(), vformat("Can't get point's position. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height)); - return points[p_id.y][p_id.x].pos; + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), Vector2(), vformat("Can't get point's position. Point %s out of bounds %s.", p_id, region)); + return GET_POINT_UNCHECKED(p_id).pos; } Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vector2i &p_to_id) { ERR_FAIL_COND_V_MSG(dirty, Vector<Vector2>(), "Grid is not initialized. Call the update method."); - ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), Vector<Vector2>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_from_id.x, size.width, p_from_id.y, size.height)); - ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), Vector<Vector2>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_to_id.x, size.width, p_to_id.y, size.height)); + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), Vector<Vector2>(), vformat("Can't get id path. Point %s out of bounds %s.", p_from_id, region)); + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), Vector<Vector2>(), vformat("Can't get id path. Point %s out of bounds %s.", p_to_id, region)); Point *a = _get_point(p_from_id.x, p_from_id.y); Point *b = _get_point(p_to_id.x, p_to_id.y); @@ -520,8 +537,8 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id) { ERR_FAIL_COND_V_MSG(dirty, TypedArray<Vector2i>(), "Grid is not initialized. Call the update method."); - ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_from_id.x, size.width, p_from_id.y, size.height)); - ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_to_id.x, size.width, p_to_id.y, size.height)); + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point %s out of bounds %s.", p_from_id, region)); + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point %s out of bounds %s.", p_to_id, region)); Point *a = _get_point(p_from_id.x, p_from_id.y); Point *b = _get_point(p_to_id.x, p_to_id.y); @@ -565,6 +582,8 @@ TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const V } void AStarGrid2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_region", "region"), &AStarGrid2D::set_region); + ClassDB::bind_method(D_METHOD("get_region"), &AStarGrid2D::get_region); ClassDB::bind_method(D_METHOD("set_size", "size"), &AStarGrid2D::set_size); ClassDB::bind_method(D_METHOD("get_size"), &AStarGrid2D::get_size); ClassDB::bind_method(D_METHOD("set_offset", "offset"), &AStarGrid2D::set_offset); @@ -596,6 +615,7 @@ void AStarGrid2D::_bind_methods() { GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id") GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id") + ADD_PROPERTY(PropertyInfo(Variant::RECT2I, "region"), "set_region", "get_region"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "cell_size"), "set_cell_size", "get_cell_size"); @@ -617,3 +637,5 @@ void AStarGrid2D::_bind_methods() { BIND_ENUM_CONSTANT(DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES); BIND_ENUM_CONSTANT(DIAGONAL_MODE_MAX); } + +#undef GET_POINT_UNCHECKED diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h index e4e62ec360..50df58e0e9 100644 --- a/core/math/a_star_grid_2d.h +++ b/core/math/a_star_grid_2d.h @@ -58,7 +58,7 @@ public: }; private: - Size2i size; + Rect2i region; Vector2 offset; Size2 cell_size = Size2(1, 1); bool dirty = false; @@ -107,21 +107,21 @@ private: private: // Internal routines. _FORCE_INLINE_ bool _is_walkable(int64_t p_x, int64_t p_y) const { - if (p_x >= 0 && p_y >= 0 && p_x < size.width && p_y < size.height) { - return !points[p_y][p_x].solid; + if (region.has_point(Vector2i(p_x, p_y))) { + return !points[p_y - region.position.y][p_x - region.position.x].solid; } return false; } _FORCE_INLINE_ Point *_get_point(int64_t p_x, int64_t p_y) { - if (p_x >= 0 && p_y >= 0 && p_x < size.width && p_y < size.height) { - return &points[p_y][p_x]; + if (region.has_point(Vector2i(p_x, p_y))) { + return &points[p_y - region.position.y][p_x - region.position.x]; } return nullptr; } _FORCE_INLINE_ Point *_get_point_unchecked(int64_t p_x, int64_t p_y) { - return &points[p_y][p_x]; + return &points[p_y - region.position.y][p_x - region.position.x]; } void _get_nbors(Point *p_point, LocalVector<Point *> &r_nbors); @@ -138,6 +138,9 @@ protected: GDVIRTUAL2RC(real_t, _compute_cost, Vector2i, Vector2i) public: + void set_region(const Rect2i &p_region); + Rect2i get_region() const; + void set_size(const Size2i &p_size); Size2i get_size() const; diff --git a/core/math/bvh_debug.inc b/core/math/bvh_debug.inc index 2e519ceb3d..1964f2fa83 100644 --- a/core/math/bvh_debug.inc +++ b/core/math/bvh_debug.inc @@ -30,11 +30,7 @@ String _debug_aabb_to_string(const BVHABB_CLASS &aabb) const { void _debug_recursive_print_tree_node(uint32_t p_node_id, int depth = 0) const { const TNode &tnode = _nodes[p_node_id]; - String sz = ""; - for (int n = 0; n < depth; n++) { - sz += "\t"; - } - sz += itos(p_node_id); + String sz = String("\t").repeat(depth) + itos(p_node_id); if (tnode.is_leaf()) { sz += " L"; diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index 868665cb5c..a0187e00b1 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -46,7 +46,7 @@ Transform2D Transform2D::inverse() const { } void Transform2D::affine_invert() { - real_t det = basis_determinant(); + real_t det = determinant(); #ifdef MATH_CHECKS ERR_FAIL_COND(det == 0); #endif @@ -70,12 +70,12 @@ void Transform2D::rotate(const real_t p_angle) { } real_t Transform2D::get_skew() const { - real_t det = basis_determinant(); + real_t det = determinant(); return Math::acos(columns[0].normalized().dot(SIGN(det) * columns[1].normalized())) - (real_t)Math_PI * 0.5f; } void Transform2D::set_skew(const real_t p_angle) { - real_t det = basis_determinant(); + real_t det = determinant(); columns[1] = SIGN(det) * columns[0].rotated(((real_t)Math_PI * 0.5f + p_angle)).normalized() * columns[1].length(); } @@ -113,7 +113,7 @@ Transform2D::Transform2D(const real_t p_rot, const Size2 &p_scale, const real_t } Size2 Transform2D::get_scale() const { - real_t det_sign = SIGN(basis_determinant()); + real_t det_sign = SIGN(determinant()); return Size2(columns[0].length(), det_sign * columns[1].length()); } @@ -259,7 +259,7 @@ Transform2D Transform2D::rotated_local(const real_t p_angle) const { return (*this) * Transform2D(p_angle, Vector2()); // Could be optimized, because origin transform can be skipped. } -real_t Transform2D::basis_determinant() const { +real_t Transform2D::determinant() const { return columns[0].x * columns[1].y - columns[0].y * columns[1].x; } diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index 4a17a9db37..c511034669 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -77,7 +77,7 @@ struct _NO_DISCARD_ Transform2D { void translate_local(const real_t p_tx, const real_t p_ty); void translate_local(const Vector2 &p_translation); - real_t basis_determinant() const; + real_t determinant() const; Size2 get_scale() const; void set_scale(const Size2 &p_scale); diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index a602dc9fb8..760f3bfd0c 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -53,8 +53,10 @@ MethodDefinition D_METHODP(const char *p_name, const char *const **p_args, uint3 #endif ClassDB::APIType ClassDB::current_api = API_CORE; +HashMap<ClassDB::APIType, uint64_t> ClassDB::api_hashes_cache; void ClassDB::set_current_api(APIType p_api) { + DEV_ASSERT(!api_hashes_cache.has(p_api)); // This API type may not be suitable for caching of hash if it can change later. current_api = p_api; } @@ -165,6 +167,10 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { OBJTYPE_RLOCK; #ifdef DEBUG_METHODS_ENABLED + if (api_hashes_cache.has(p_api)) { + return api_hashes_cache[p_api]; + } + uint64_t hash = hash_murmur3_one_64(HashMapHasherDefault::hash(VERSION_FULL_CONFIG)); List<StringName> class_list; @@ -290,7 +296,14 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { } } - return hash_fmix32(hash); + hash = hash_fmix32(hash); + + // Extension API changes at runtime; let's just not cache them by now. + if (p_api != API_EXTENSION && p_api != API_EDITOR_EXTENSION) { + api_hashes_cache[p_api] = hash; + } + + return hash; #else return 0; #endif diff --git a/core/object/class_db.h b/core/object/class_db.h index 0b62cf40f7..1a5e9235cf 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -154,6 +154,7 @@ public: #endif static APIType current_api; + static HashMap<APIType, uint64_t> api_hashes_cache; static void _add_class2(const StringName &p_class, const StringName &p_inherits); diff --git a/core/object/object.cpp b/core/object/object.cpp index 39cae7c5bd..d96f4eb9fc 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -38,6 +38,7 @@ #include "core/os/os.h" #include "core/string/print_string.h" #include "core/string/translation.h" +#include "core/templates/local_vector.h" #include "core/variant/typed_array.h" #ifdef DEBUG_ENABLED @@ -1013,22 +1014,29 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int return ERR_UNAVAILABLE; } - List<_ObjectSignalDisconnectData> disconnect_data; + // If this is a ref-counted object, prevent it from being destroyed during signal emission, + // which is needed in certain edge cases; e.g., https://github.com/godotengine/godot/issues/73889. + Ref<RefCounted> rc = Ref<RefCounted>(Object::cast_to<RefCounted>(this)); - //copy on write will ensure that disconnecting the signal or even deleting the object will not affect the signal calling. - //this happens automatically and will not change the performance of calling. - //awesome, isn't it? - VMap<Callable, SignalData::Slot> slot_map = s->slot_map; + List<_ObjectSignalDisconnectData> disconnect_data; - int ssize = slot_map.size(); + // Ensure that disconnecting the signal or even deleting the object + // will not affect the signal calling. + LocalVector<Connection> slot_conns; + slot_conns.resize(s->slot_map.size()); + { + uint32_t idx = 0; + for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) { + slot_conns[idx++] = slot_kv.value.conn; + } + DEV_ASSERT(idx == s->slot_map.size()); + } OBJ_DEBUG_LOCK Error err = OK; - for (int i = 0; i < ssize; i++) { - const Connection &c = slot_map.getv(i).conn; - + for (const Connection &c : slot_conns) { Object *target = c.callable.get_object(); if (!target) { // Target might have been deleted during signal callback, this is expected and OK. @@ -1191,8 +1199,8 @@ void Object::get_all_signal_connections(List<Connection> *p_connections) const { for (const KeyValue<StringName, SignalData> &E : signal_map) { const SignalData *s = &E.value; - for (int i = 0; i < s->slot_map.size(); i++) { - p_connections->push_back(s->slot_map.getv(i).conn); + for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) { + p_connections->push_back(slot_kv.value.conn); } } } @@ -1203,8 +1211,8 @@ void Object::get_signal_connection_list(const StringName &p_signal, List<Connect return; //nothing } - for (int i = 0; i < s->slot_map.size(); i++) { - p_connections->push_back(s->slot_map.getv(i).conn); + for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) { + p_connections->push_back(slot_kv.value.conn); } } @@ -1214,8 +1222,8 @@ int Object::get_persistent_signal_connection_count() const { for (const KeyValue<StringName, SignalData> &E : signal_map) { const SignalData *s = &E.value; - for (int i = 0; i < s->slot_map.size(); i++) { - if (s->slot_map.getv(i).conn.flags & CONNECT_PERSIST) { + for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) { + if (slot_kv.value.conn.flags & CONNECT_PERSIST) { count += 1; } } @@ -1800,11 +1808,8 @@ Object::~Object() { SignalData *s = &E.value; //brute force disconnect for performance - int slot_count = s->slot_map.size(); - const VMap<Callable, SignalData::Slot>::Pair *slot_list = s->slot_map.get_array(); - - for (int i = 0; i < slot_count; i++) { - slot_list[i].value.conn.callable.get_object()->connections.erase(slot_list[i].value.cE); + for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) { + slot_kv.value.conn.callable.get_object()->connections.erase(slot_kv.value.cE); } signal_map.erase(E.key); diff --git a/core/object/object.h b/core/object/object.h index 4226b5e67b..f0acfffe14 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -41,7 +41,6 @@ #include "core/templates/list.h" #include "core/templates/rb_map.h" #include "core/templates/safe_refcount.h" -#include "core/templates/vmap.h" #include "core/variant/callable_bind.h" #include "core/variant/variant.h" @@ -587,7 +586,7 @@ private: }; MethodInfo user; - VMap<Callable, Slot> slot_map; + HashMap<Callable, Slot, HashableHasher<Callable>> slot_map; }; HashMap<StringName, SignalData> signal_map; diff --git a/core/os/os.cpp b/core/os/os.cpp index ef7d860d19..c82f87c731 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -281,6 +281,15 @@ Error OS::shell_open(String p_uri) { return ERR_UNAVAILABLE; } +Error OS::shell_show_in_file_manager(String p_path, bool p_open_folder) { + if (!p_path.begins_with("file://")) { + p_path = String("file://") + p_path; + } + if (!p_path.ends_with("/")) { + p_path = p_path.get_base_dir(); + } + return shell_open(p_path); +} // implement these with the canvas? uint64_t OS::get_static_memory_usage() const { @@ -295,8 +304,15 @@ Error OS::set_cwd(const String &p_cwd) { return ERR_CANT_OPEN; } -uint64_t OS::get_free_static_memory() const { - return Memory::get_mem_available(); +Dictionary OS::get_memory_info() const { + Dictionary meminfo; + + meminfo["physical"] = -1; + meminfo["free"] = -1; + meminfo["available"] = -1; + meminfo["stack"] = -1; + + return meminfo; } void OS::yield() { diff --git a/core/os/os.h b/core/os/os.h index d77890d89d..3248330c74 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -163,6 +163,7 @@ public: virtual void vibrate_handheld(int p_duration_ms = 500) {} virtual Error shell_open(String p_uri); + virtual Error shell_show_in_file_manager(String p_path, bool p_open_folder = true); virtual Error set_cwd(const String &p_cwd); virtual bool has_environment(const String &p_var) const = 0; @@ -229,7 +230,7 @@ public: virtual uint64_t get_static_memory_usage() const; virtual uint64_t get_static_memory_peak_usage() const; - virtual uint64_t get_free_static_memory() const; + virtual Dictionary get_memory_info() const; RenderThreadMode get_render_thread_mode() const { return _render_thread_mode; } diff --git a/core/os/thread.cpp b/core/os/thread.cpp index 92865576f3..502f82aaef 100644 --- a/core/os/thread.cpp +++ b/core/os/thread.cpp @@ -38,13 +38,9 @@ Thread::PlatformFunctions Thread::platform_functions; -uint64_t Thread::_thread_id_hash(const std::thread::id &p_t) { - static std::hash<std::thread::id> hasher; - return hasher(p_t); -} +SafeNumeric<uint64_t> Thread::id_counter(1); // The first value after .increment() is 2, hence by default the main thread ID should be 1. -Thread::ID Thread::main_thread_id = _thread_id_hash(std::this_thread::get_id()); -thread_local Thread::ID Thread::caller_id = 0; +thread_local Thread::ID Thread::caller_id = Thread::UNASSIGNED_ID; void Thread::_set_platform_functions(const PlatformFunctions &p_functions) { platform_functions = p_functions; @@ -71,31 +67,23 @@ void Thread::callback(ID p_caller_id, const Settings &p_settings, Callback p_cal } void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) { - if (id != _thread_id_hash(std::thread::id())) { -#ifdef DEBUG_ENABLED - WARN_PRINT("A Thread object has been re-started without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread."); -#endif - thread.detach(); - std::thread empty_thread; - thread.swap(empty_thread); - } - std::thread new_thread(&Thread::callback, _thread_id_hash(thread.get_id()), p_settings, p_callback, p_user); + ERR_FAIL_COND_MSG(id != UNASSIGNED_ID, "A Thread object has been re-started without wait_to_finish() having been called on it."); + id = id_counter.increment(); + std::thread new_thread(&Thread::callback, id, p_settings, p_callback, p_user); thread.swap(new_thread); - id = _thread_id_hash(thread.get_id()); } bool Thread::is_started() const { - return id != _thread_id_hash(std::thread::id()); + return id != UNASSIGNED_ID; } void Thread::wait_to_finish() { - if (id != _thread_id_hash(std::thread::id())) { - ERR_FAIL_COND_MSG(id == get_caller_id(), "A Thread can't wait for itself to finish."); - thread.join(); - std::thread empty_thread; - thread.swap(empty_thread); - id = _thread_id_hash(std::thread::id()); - } + ERR_FAIL_COND_MSG(id == UNASSIGNED_ID, "Attempt of waiting to finish on a thread that was never started."); + ERR_FAIL_COND_MSG(id == get_caller_id(), "Threads can't wait to finish on themselves, another thread must wait."); + thread.join(); + std::thread empty_thread; + thread.swap(empty_thread); + id = UNASSIGNED_ID; } Error Thread::set_name(const String &p_name) { @@ -107,11 +95,10 @@ Error Thread::set_name(const String &p_name) { } Thread::Thread() { - caller_id = _thread_id_hash(std::this_thread::get_id()); } Thread::~Thread() { - if (id != _thread_id_hash(std::thread::id())) { + if (id != UNASSIGNED_ID) { #ifdef DEBUG_ENABLED WARN_PRINT("A Thread object has been destroyed without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread."); #endif diff --git a/core/os/thread.h b/core/os/thread.h index 6eb21fba65..19e1376ca8 100644 --- a/core/os/thread.h +++ b/core/os/thread.h @@ -52,6 +52,11 @@ public: typedef uint64_t ID; + enum : ID { + UNASSIGNED_ID = 0, + MAIN_ID = 1 + }; + enum Priority { PRIORITY_LOW, PRIORITY_NORMAL, @@ -74,11 +79,8 @@ public: private: friend class Main; - static ID main_thread_id; - - static uint64_t _thread_id_hash(const std::thread::id &p_t); - - ID id = _thread_id_hash(std::thread::id()); + ID id = UNASSIGNED_ID; + static SafeNumeric<uint64_t> id_counter; static thread_local ID caller_id; std::thread thread; @@ -86,14 +88,22 @@ private: static PlatformFunctions platform_functions; + static void make_main_thread() { caller_id = MAIN_ID; } + static void release_main_thread() { caller_id = UNASSIGNED_ID; } + public: static void _set_platform_functions(const PlatformFunctions &p_functions); _FORCE_INLINE_ ID get_id() const { return id; } // get the ID of the caller thread - _FORCE_INLINE_ static ID get_caller_id() { return caller_id; } + _FORCE_INLINE_ static ID get_caller_id() { + if (unlikely(caller_id == UNASSIGNED_ID)) { + caller_id = id_counter.increment(); + } + return caller_id; + } // get the ID of the main thread - _FORCE_INLINE_ static ID get_main_id() { return main_thread_id; } + _FORCE_INLINE_ static ID get_main_id() { return MAIN_ID; } static Error set_name(const String &p_name); diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 160bad14ab..3ca2e5ccdf 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -941,18 +941,11 @@ String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const } String TranslationServer::add_padding(const String &p_message, int p_length) const { - String res; - String prefix = pseudolocalization_prefix; - String suffix; - for (int i = 0; i < p_length * expansion_ratio / 2; i++) { - prefix += "_"; - suffix += "_"; - } - suffix += pseudolocalization_suffix; - res += prefix; - res += p_message; - res += suffix; - return res; + String underscores = String("_").repeat(p_length * expansion_ratio / 2); + String prefix = pseudolocalization_prefix + underscores; + String suffix = underscores + pseudolocalization_suffix; + + return prefix + p_message + suffix; } const char32_t *TranslationServer::get_accented_version(char32_t p_character) const { diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 49c72a9dcf..92c9e4b70c 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -2857,6 +2857,12 @@ String String::insert(int p_at_pos, const String &p_string) const { return pre + p_string + post; } +String String::erase(int p_pos, int p_chars) const { + ERR_FAIL_COND_V_MSG(p_pos < 0, "", vformat("Invalid starting position for `String.erase()`: %d. Starting position must be positive or zero.", p_pos)); + ERR_FAIL_COND_V_MSG(p_chars < 0, "", vformat("Invalid character count for `String.erase()`: %d. Character count must be positive or zero.", p_chars)); + return left(p_pos) + substr(p_pos + p_chars); +} + String String::substr(int p_from, int p_chars) const { if (p_chars == -1) { p_chars = length() - p_from; @@ -3524,6 +3530,14 @@ String String::replacen(const String &p_key, const String &p_with) const { String String::repeat(int p_count) const { ERR_FAIL_COND_V_MSG(p_count < 0, "", "Parameter count should be a positive number."); + if (p_count == 0) { + return ""; + } + + if (p_count == 1) { + return *this; + } + int len = length(); String new_string = *this; new_string.resize(p_count * len + 1); @@ -4161,13 +4175,11 @@ String String::pad_decimals(int p_digits) const { } if (s.length() - (c + 1) > p_digits) { - s = s.substr(0, c + p_digits + 1); + return s.substr(0, c + p_digits + 1); } else { - while (s.length() - (c + 1) < p_digits) { - s += "0"; - } + int zeros_to_add = p_digits - s.length() + (c + 1); + return s + String("0").repeat(zeros_to_add); } - return s; } String String::pad_zeros(int p_digits) const { @@ -4192,12 +4204,8 @@ String String::pad_zeros(int p_digits) const { return s; } - while (end - begin < p_digits) { - s = s.insert(begin, "0"); - end++; - } - - return s; + int zeros_to_add = p_digits - (end - begin); + return s.insert(begin, String("0").repeat(zeros_to_add)); } String String::trim_prefix(const String &p_prefix) const { @@ -4376,11 +4384,8 @@ String String::path_to(const String &p_path) const { common_parent--; - String dir; - - for (int i = src_dirs.size() - 1; i > common_parent; i--) { - dir += "../"; - } + int dirs_to_backtrack = (src_dirs.size() - 1) - common_parent; + String dir = String("../").repeat(dirs_to_backtrack); for (int i = common_parent + 1; i < dst_dirs.size(); i++) { dir += dst_dirs[i] + "/"; @@ -4669,11 +4674,8 @@ String String::rpad(int min_length, const String &character) const { String s = *this; int padding = min_length - s.length(); if (padding > 0) { - for (int i = 0; i < padding; i++) { - s = s + character; - } + s += character.repeat(padding); } - return s; } @@ -4682,11 +4684,8 @@ String String::lpad(int min_length, const String &character) const { String s = *this; int padding = min_length - s.length(); if (padding > 0) { - for (int i = 0; i < padding; i++) { - s = character + s; - } + s = character.repeat(padding) + s; } - return s; } diff --git a/core/string/ustring.h b/core/string/ustring.h index e1512cfb26..c771dff515 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -304,6 +304,7 @@ public: String replacen(const String &p_key, const String &p_with) const; String repeat(int p_count) const; String insert(int p_at_pos, const String &p_string) const; + String erase(int p_pos, int p_chars = 1) const; String pad_decimals(int p_digits) const; String pad_zeros(int p_digits) const; String trim_prefix(const String &p_prefix) const; diff --git a/core/templates/hash_map.h b/core/templates/hash_map.h index f3944fcd0d..0b846dfaba 100644 --- a/core/templates/hash_map.h +++ b/core/templates/hash_map.h @@ -67,9 +67,9 @@ template <class TKey, class TValue, class Allocator = DefaultTypedAllocator<HashMapElement<TKey, TValue>>> class HashMap { public: - const uint32_t MIN_CAPACITY_INDEX = 2; // Use a prime. - const float MAX_OCCUPANCY = 0.75; - const uint32_t EMPTY_HASH = 0; + static constexpr uint32_t MIN_CAPACITY_INDEX = 2; // Use a prime. + static constexpr float MAX_OCCUPANCY = 0.75; + static constexpr uint32_t EMPTY_HASH = 0; private: Allocator element_alloc; diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h index 95e6bad2f2..2a212f3dcb 100644 --- a/core/templates/hashfuncs.h +++ b/core/templates/hashfuncs.h @@ -386,6 +386,12 @@ struct HashMapHasherDefault { } }; +// TODO: Fold this into HashMapHasherDefault once C++20 concepts are allowed +template <class T> +struct HashableHasher { + static _FORCE_INLINE_ uint32_t hash(const T &hashable) { return hashable.hash(); } +}; + template <typename T> struct HashMapComparatorDefault { static bool compare(const T &p_lhs, const T &p_rhs) { diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 5a4a017abe..d77222b166 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1659,6 +1659,7 @@ static void _register_variant_builtin_methods() { bind_string_method(replacen, sarray("what", "forwhat"), varray()); bind_string_method(repeat, sarray("count"), varray()); bind_string_method(insert, sarray("position", "what"), varray()); + bind_string_method(erase, sarray("position", "chars"), varray(1)); bind_string_method(capitalize, sarray(), varray()); bind_string_method(to_camel_case, sarray(), varray()); bind_string_method(to_pascal_case, sarray(), varray()); @@ -2073,6 +2074,7 @@ static void _register_variant_builtin_methods() { bind_method(Transform2D, scaled_local, sarray("scale"), varray()); bind_method(Transform2D, translated, sarray("offset"), varray()); bind_method(Transform2D, translated_local, sarray("offset"), varray()); + bind_method(Transform2D, determinant, sarray(), varray()); bind_method(Transform2D, basis_xform, sarray("v"), varray()); bind_method(Transform2D, basis_xform_inv, sarray("v"), varray()); bind_method(Transform2D, interpolate_with, sarray("xform", "weight"), varray()); diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index a6363039ba..fd079dbeea 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -374,6 +374,7 @@ struct VariantUtilityFunctions { r_error.error = Callable::CallError::CALL_OK; if (from.get_type() != to.get_type()) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = from.get_type(); r_error.argument = 1; return Variant(); } |
