summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/SCsub25
-rw-r--r--core/core_bind.cpp15
-rw-r--r--core/core_bind.h2
-rw-r--r--core/debugger/remote_debugger_peer.cpp4
-rw-r--r--core/doc_data.h55
-rw-r--r--core/io/compression.cpp218
-rw-r--r--core/io/compression.h3
-rw-r--r--core/io/dir_access.h2
-rw-r--r--core/io/file_access.cpp1
-rw-r--r--core/io/file_access.h5
-rw-r--r--core/io/file_access_compressed.cpp8
-rw-r--r--core/io/json.cpp8
-rw-r--r--core/math/a_star_grid_2d.cpp74
-rw-r--r--core/math/a_star_grid_2d.h15
-rw-r--r--core/math/bvh_debug.inc6
-rw-r--r--core/math/transform_2d.cpp10
-rw-r--r--core/math/transform_2d.h2
-rw-r--r--core/object/class_db.cpp15
-rw-r--r--core/object/class_db.h1
-rw-r--r--core/object/object.cpp45
-rw-r--r--core/object/object.h3
-rw-r--r--core/os/os.cpp20
-rw-r--r--core/os/os.h3
-rw-r--r--core/os/thread.cpp39
-rw-r--r--core/os/thread.h24
-rw-r--r--core/string/translation.cpp17
-rw-r--r--core/string/ustring.cpp47
-rw-r--r--core/string/ustring.h1
-rw-r--r--core/templates/hash_map.h6
-rw-r--r--core/templates/hashfuncs.h6
-rw-r--r--core/variant/variant_call.cpp2
-rw-r--r--core/variant/variant_utility.cpp1
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();
}