diff options
Diffstat (limited to 'core')
36 files changed, 525 insertions, 178 deletions
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 267391c4d6..6df8dd380a 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -454,7 +454,7 @@ Dictionary _OS::get_datetime_from_unix_time(int64_t unix_time_val) const { } else { dayno = (unix_time_val - SECS_DAY + 1) / SECS_DAY; dayclock = unix_time_val - dayno * SECS_DAY; - date.weekday = static_cast<OS::Weekday>((dayno - 3) % 7 + 7); + date.weekday = static_cast<OS::Weekday>(((dayno % 7) + 11) % 7); do { year--; dayno += YEARSIZE(year); @@ -1619,12 +1619,17 @@ Error _Directory::open(const String &p_path) { memdelete(d); } d = alt; + dir_open = true; return OK; } +bool _Directory::is_open() const { + return d && dir_open; +} + Error _Directory::list_dir_begin(bool p_skip_navigational, bool p_skip_hidden) { - ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); _list_skip_navigational = p_skip_navigational; _list_skip_hidden = p_skip_hidden; @@ -1633,7 +1638,7 @@ Error _Directory::list_dir_begin(bool p_skip_navigational, bool p_skip_hidden) { } String _Directory::get_next() { - ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use."); String next = d->get_next(); while (next != "" && ((_list_skip_navigational && (next == "." || next == "..")) || (_list_skip_hidden && d->current_is_hidden()))) { @@ -1643,42 +1648,42 @@ String _Directory::get_next() { } bool _Directory::current_is_dir() const { - ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), false, "Directory must be opened before use."); return d->current_is_dir(); } void _Directory::list_dir_end() { - ERR_FAIL_COND_MSG(!d, "Directory must be opened before use."); + ERR_FAIL_COND_MSG(!is_open(), "Directory must be opened before use."); d->list_dir_end(); } int _Directory::get_drive_count() { - ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use."); return d->get_drive_count(); } String _Directory::get_drive(int p_drive) { - ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use."); return d->get_drive(p_drive); } int _Directory::get_current_drive() { - ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use."); return d->get_current_drive(); } Error _Directory::change_dir(String p_dir) { - ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); return d->change_dir(p_dir); } String _Directory::get_current_dir() { - ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use."); return d->get_current_dir(); } Error _Directory::make_dir(String p_dir) { - ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); if (!p_dir.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); Error err = d->make_dir(p_dir); @@ -1689,7 +1694,7 @@ Error _Directory::make_dir(String p_dir) { } Error _Directory::make_dir_recursive(String p_dir) { - ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); if (!p_dir.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); Error err = d->make_dir_recursive(p_dir); @@ -1700,7 +1705,7 @@ Error _Directory::make_dir_recursive(String p_dir) { } bool _Directory::file_exists(String p_file) { - ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), false, "Directory must be opened before use."); if (!p_file.is_rel_path()) { return FileAccess::exists(p_file); @@ -1710,7 +1715,7 @@ bool _Directory::file_exists(String p_file) { } bool _Directory::dir_exists(String p_dir) { - ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), false, "Directory must be opened before use."); if (!p_dir.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); bool exists = d->dir_exists(p_dir); @@ -1723,17 +1728,17 @@ bool _Directory::dir_exists(String p_dir) { } int _Directory::get_space_left() { - ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use."); return d->get_space_left() / 1024 * 1024; //return value in megabytes, given binding is int } Error _Directory::copy(String p_from, String p_to) { - ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); return d->copy(p_from, p_to); } Error _Directory::rename(String p_from, String p_to) { - ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); if (!p_from.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_from); Error err = d->rename(p_from, p_to); @@ -1745,7 +1750,7 @@ Error _Directory::rename(String p_from, String p_to) { } Error _Directory::remove(String p_name) { - ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); if (!p_name.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_name); Error err = d->remove(p_name); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index f9f5a4e7d7..a1fedf1bb8 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -457,6 +457,7 @@ VARIANT_ENUM_CAST(_File::CompressionMode); class _Directory : public Reference { GDCLASS(_Directory, Reference); DirAccess *d; + bool dir_open = false; protected: static void _bind_methods(); @@ -464,6 +465,8 @@ protected: public: Error open(const String &p_path); + bool is_open() const; + Error list_dir_begin(bool p_skip_navigational = false, bool p_skip_hidden = false); // This starts dir listing. String get_next(); bool current_is_dir() const; diff --git a/core/crypto/aes_context.cpp b/core/crypto/aes_context.cpp new file mode 100644 index 0000000000..8ef1f4f1d4 --- /dev/null +++ b/core/crypto/aes_context.cpp @@ -0,0 +1,116 @@ +/*************************************************************************/ +/* aes_context.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/crypto/aes_context.h" + +Error AESContext::start(Mode p_mode, PackedByteArray p_key, PackedByteArray p_iv) { + ERR_FAIL_COND_V_MSG(mode != MODE_MAX, ERR_ALREADY_IN_USE, "AESContext already started. Call 'finish' before starting a new one."); + ERR_FAIL_COND_V_MSG(p_mode < 0 || p_mode >= MODE_MAX, ERR_INVALID_PARAMETER, "Invalid mode requested."); + // Key check. + int key_bits = p_key.size() << 3; + ERR_FAIL_COND_V_MSG(key_bits != 128 && key_bits != 256, ERR_INVALID_PARAMETER, "AES key must be either 16 or 32 bytes"); + // Initialization vector. + if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_CBC_DECRYPT) { + ERR_FAIL_COND_V_MSG(p_iv.size() != 16, ERR_INVALID_PARAMETER, "The initialization vector (IV) must be exactly 16 bytes."); + iv.resize(0); + iv.append_array(p_iv); + } + // Encryption/decryption key. + if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_ECB_ENCRYPT) { + ctx.set_encode_key(p_key.ptr(), key_bits); + } else { + ctx.set_decode_key(p_key.ptr(), key_bits); + } + mode = p_mode; + return OK; +} + +PackedByteArray AESContext::update(PackedByteArray p_src) { + ERR_FAIL_COND_V_MSG(mode < 0 || mode >= MODE_MAX, PackedByteArray(), "AESContext not started. Call 'start' before calling 'update'."); + int len = p_src.size(); + ERR_FAIL_COND_V_MSG(len % 16, PackedByteArray(), "The number of bytes to be encrypted must be multiple of 16. Add padding if needed"); + PackedByteArray out; + out.resize(len); + const uint8_t *src_ptr = p_src.ptr(); + uint8_t *out_ptr = out.ptrw(); + switch (mode) { + case MODE_ECB_ENCRYPT: { + for (int i = 0; i < len; i += 16) { + Error err = ctx.encrypt_ecb(src_ptr + i, out_ptr + i); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } + } break; + case MODE_ECB_DECRYPT: { + for (int i = 0; i < len; i += 16) { + Error err = ctx.decrypt_ecb(src_ptr + i, out_ptr + i); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } + } break; + case MODE_CBC_ENCRYPT: { + Error err = ctx.encrypt_cbc(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + case MODE_CBC_DECRYPT: { + Error err = ctx.decrypt_cbc(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + default: + ERR_FAIL_V_MSG(PackedByteArray(), "Bug!"); + } + return out; +} + +PackedByteArray AESContext::get_iv_state() { + ERR_FAIL_COND_V_MSG(mode != MODE_CBC_ENCRYPT && mode != MODE_CBC_DECRYPT, PackedByteArray(), "Calling 'get_iv_state' only makes sense when the context is started in CBC mode."); + PackedByteArray out; + out.append_array(iv); + return out; +} + +void AESContext::finish() { + mode = MODE_MAX; + iv.resize(0); +} + +void AESContext::_bind_methods() { + ClassDB::bind_method(D_METHOD("start", "mode", "key", "iv"), &AESContext::start, DEFVAL(PackedByteArray())); + ClassDB::bind_method(D_METHOD("update", "src"), &AESContext::update); + ClassDB::bind_method(D_METHOD("get_iv_state"), &AESContext::get_iv_state); + ClassDB::bind_method(D_METHOD("finish"), &AESContext::finish); + BIND_ENUM_CONSTANT(MODE_ECB_ENCRYPT); + BIND_ENUM_CONSTANT(MODE_ECB_DECRYPT); + BIND_ENUM_CONSTANT(MODE_CBC_ENCRYPT); + BIND_ENUM_CONSTANT(MODE_CBC_DECRYPT); + BIND_ENUM_CONSTANT(MODE_MAX); +} + +AESContext::AESContext() { + mode = MODE_MAX; +} diff --git a/core/crypto/aes_context.h b/core/crypto/aes_context.h new file mode 100644 index 0000000000..006ecee2ad --- /dev/null +++ b/core/crypto/aes_context.h @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* aes_context.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef AES_CONTEXT_H +#define AES_CONTEXT_H + +#include "core/crypto/crypto_core.h" +#include "core/reference.h" + +class AESContext : public Reference { + GDCLASS(AESContext, Reference); + +public: + enum Mode { + MODE_ECB_ENCRYPT, + MODE_ECB_DECRYPT, + MODE_CBC_ENCRYPT, + MODE_CBC_DECRYPT, + MODE_MAX + }; + +private: + Mode mode; + CryptoCore::AESContext ctx; + PackedByteArray iv; + +protected: + static void _bind_methods(); + +public: + Error start(Mode p_mode, PackedByteArray p_key, PackedByteArray p_iv = PackedByteArray()); + PackedByteArray update(PackedByteArray p_src); + PackedByteArray get_iv_state(); + void finish(); + + AESContext(); +}; + +VARIANT_ENUM_CAST(AESContext::Mode); + +#endif // AES_CONTEXT_H diff --git a/core/crypto/crypto.cpp b/core/crypto/crypto.cpp index a9a7cabee9..29d02e11df 100644 --- a/core/crypto/crypto.cpp +++ b/core/crypto/crypto.cpp @@ -45,8 +45,11 @@ CryptoKey *CryptoKey::create() { } void CryptoKey::_bind_methods() { - ClassDB::bind_method(D_METHOD("save", "path"), &CryptoKey::save); - ClassDB::bind_method(D_METHOD("load", "path"), &CryptoKey::load); + ClassDB::bind_method(D_METHOD("save", "path", "public_only"), &CryptoKey::save, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("load", "path", "public_only"), &CryptoKey::load, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_public_only"), &CryptoKey::is_public_only); + ClassDB::bind_method(D_METHOD("save_to_string", "public_only"), &CryptoKey::save_to_string, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("load_from_string", "string_key", "public_only"), &CryptoKey::load_from_string, DEFVAL(false)); } X509Certificate *(*X509Certificate::_create)() = nullptr; @@ -83,6 +86,10 @@ void Crypto::_bind_methods() { ClassDB::bind_method(D_METHOD("generate_random_bytes", "size"), &Crypto::generate_random_bytes); ClassDB::bind_method(D_METHOD("generate_rsa", "size"), &Crypto::generate_rsa); ClassDB::bind_method(D_METHOD("generate_self_signed_certificate", "key", "issuer_name", "not_before", "not_after"), &Crypto::generate_self_signed_certificate, DEFVAL("CN=myserver,O=myorganisation,C=IT"), DEFVAL("20140101000000"), DEFVAL("20340101000000")); + ClassDB::bind_method(D_METHOD("sign", "hash_type", "hash", "key"), &Crypto::sign); + ClassDB::bind_method(D_METHOD("verify", "hash_type", "hash", "signature", "key"), &Crypto::verify); + ClassDB::bind_method(D_METHOD("encrypt", "key", "plaintext"), &Crypto::encrypt); + ClassDB::bind_method(D_METHOD("decrypt", "key", "ciphertext"), &Crypto::decrypt); } /// Resource loader/saver @@ -98,9 +105,14 @@ RES ResourceFormatLoaderCrypto::load(const String &p_path, const String &p_origi } else if (el == "key") { CryptoKey *key = CryptoKey::create(); if (key) { - key->load(p_path); + key->load(p_path, false); } return key; + } else if (el == "pub") { + CryptoKey *key = CryptoKey::create(); + if (key) + key->load(p_path, true); + return key; } return nullptr; } @@ -108,6 +120,7 @@ RES ResourceFormatLoaderCrypto::load(const String &p_path, const String &p_origi void ResourceFormatLoaderCrypto::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("crt"); p_extensions->push_back("key"); + p_extensions->push_back("pub"); } bool ResourceFormatLoaderCrypto::handles_type(const String &p_type) const { @@ -118,7 +131,7 @@ String ResourceFormatLoaderCrypto::get_resource_type(const String &p_path) const String el = p_path.get_extension().to_lower(); if (el == "crt") { return "X509Certificate"; - } else if (el == "key") { + } else if (el == "key" || el == "pub") { return "CryptoKey"; } return ""; @@ -131,7 +144,8 @@ Error ResourceFormatSaverCrypto::save(const String &p_path, const RES &p_resourc if (cert.is_valid()) { err = cert->save(p_path); } else if (key.is_valid()) { - err = key->save(p_path); + String el = p_path.get_extension().to_lower(); + err = key->save(p_path, el == "pub"); } else { ERR_FAIL_V(ERR_INVALID_PARAMETER); } @@ -146,7 +160,10 @@ void ResourceFormatSaverCrypto::get_recognized_extensions(const RES &p_resource, p_extensions->push_back("crt"); } if (key) { - p_extensions->push_back("key"); + if (!key->is_public_only()) { + p_extensions->push_back("key"); + } + p_extensions->push_back("pub"); } } diff --git a/core/crypto/crypto.h b/core/crypto/crypto.h index 6cc5f46164..916f7798eb 100644 --- a/core/crypto/crypto.h +++ b/core/crypto/crypto.h @@ -31,6 +31,7 @@ #ifndef CRYPTO_H #define CRYPTO_H +#include "core/crypto/hashing_context.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/reference.h" @@ -45,8 +46,11 @@ protected: public: static CryptoKey *create(); - virtual Error load(String p_path) = 0; - virtual Error save(String p_path) = 0; + virtual Error load(String p_path, bool p_public_only = false) = 0; + virtual Error save(String p_path, bool p_public_only = false) = 0; + virtual String save_to_string(bool p_public_only = false) = 0; + virtual Error load_from_string(String p_string_key, bool p_public_only = false) = 0; + virtual bool is_public_only() const = 0; }; class X509Certificate : public Resource { @@ -79,6 +83,11 @@ public: virtual Ref<CryptoKey> generate_rsa(int p_bytes) = 0; virtual Ref<X509Certificate> generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after) = 0; + virtual Vector<uint8_t> sign(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Ref<CryptoKey> p_key) = 0; + virtual bool verify(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Vector<uint8_t> p_signature, Ref<CryptoKey> p_key) = 0; + virtual Vector<uint8_t> encrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_plaintext) = 0; + virtual Vector<uint8_t> decrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_ciphertext) = 0; + Crypto() {} }; diff --git a/core/crypto/crypto_core.cpp b/core/crypto/crypto_core.cpp index ec25ee0d38..b0dc47e655 100644 --- a/core/crypto/crypto_core.cpp +++ b/core/crypto/crypto_core.cpp @@ -145,6 +145,16 @@ Error CryptoCore::AESContext::decrypt_ecb(const uint8_t p_src[16], uint8_t r_dst return ret ? FAILED : OK; } +Error CryptoCore::AESContext::encrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst) { + int ret = mbedtls_aes_crypt_cbc((mbedtls_aes_context *)ctx, MBEDTLS_AES_ENCRYPT, p_length, r_iv, p_src, r_dst); + return ret ? FAILED : OK; +} + +Error CryptoCore::AESContext::decrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst) { + int ret = mbedtls_aes_crypt_cbc((mbedtls_aes_context *)ctx, MBEDTLS_AES_DECRYPT, p_length, r_iv, p_src, r_dst); + return ret ? FAILED : OK; +} + // CryptoCore String CryptoCore::b64_encode_str(const uint8_t *p_src, int p_src_len) { int b64len = p_src_len / 3 * 4 + 4 + 1; diff --git a/core/crypto/crypto_core.h b/core/crypto/crypto_core.h index 36d8ace723..82df9c23a8 100644 --- a/core/crypto/crypto_core.h +++ b/core/crypto/crypto_core.h @@ -86,6 +86,8 @@ public: Error set_decode_key(const uint8_t *p_key, size_t p_bits); Error encrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]); Error decrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]); + Error encrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst); + Error decrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst); }; static String b64_encode_str(const uint8_t *p_src, int p_src_len); diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp index 5c9fb67de4..4bf31aa55f 100644 --- a/core/debugger/engine_debugger.cpp +++ b/core/debugger/engine_debugger.cpp @@ -169,7 +169,7 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, Ve for (int i = 0; i < p_breakpoints.size(); i++) { String bp = p_breakpoints[i]; - int sp = bp.find_last(":"); + int sp = bp.rfind(":"); ERR_CONTINUE_MSG(sp == -1, "Invalid breakpoint: '" + bp + "', expected file:line format."); singleton_script_debugger->insert_breakpoint(bp.substr(sp + 1, bp.length()).to_int(), bp.substr(0, sp)); diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index 62f600c5e5..9d55e1312e 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -373,6 +373,7 @@ struct RemoteDebugger::VisualProfiler { struct RemoteDebugger::PerformanceProfiler { Object *performance = nullptr; int last_perf_time = 0; + uint64_t last_monitor_modification_time = 0; void toggle(bool p_enable, const Array &p_opts) {} void add(const Array &p_data) {} @@ -386,12 +387,31 @@ struct RemoteDebugger::PerformanceProfiler { return; } last_perf_time = pt; + + Array custom_monitor_names = performance->call("get_custom_monitor_names"); + + uint64_t monitor_modification_time = performance->call("get_monitor_modification_time"); + if (monitor_modification_time > last_monitor_modification_time) { + last_monitor_modification_time = monitor_modification_time; + EngineDebugger::get_singleton()->send_message("performance:profile_names", custom_monitor_names); + } + int max = performance->get("MONITOR_MAX"); Array arr; - arr.resize(max); + arr.resize(max + custom_monitor_names.size()); for (int i = 0; i < max; i++) { arr[i] = performance->call("get_monitor", i); } + + for (int i = 0; i < custom_monitor_names.size(); i++) { + Variant monitor_value = performance->call("get_custom_monitor", custom_monitor_names[i]); + if (!monitor_value.is_num()) { + ERR_PRINT("Value of custom monitor '" + String(custom_monitor_names[i]) + "' is not a number"); + arr[i + max] = Variant(); + } + arr[i + max] = monitor_value; + } + EngineDebugger::get_singleton()->send_message("performance:profile_frame", arr); } diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp index faa3a75fda..0ce0042f50 100644 --- a/core/debugger/remote_debugger_peer.cpp +++ b/core/debugger/remote_debugger_peer.cpp @@ -225,7 +225,7 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) { uint16_t debug_port = 6007; if (debug_host.find(":") != -1) { - int sep_pos = debug_host.find_last(":"); + int sep_pos = debug_host.rfind(":"); debug_port = debug_host.substr(sep_pos + 1).to_int(); debug_host = debug_host.substr(0, sep_pos); } diff --git a/core/error_macros.h b/core/error_macros.h index d7366be453..6353961b04 100644 --- a/core/error_macros.h +++ b/core/error_macros.h @@ -476,7 +476,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * The current function returns. */ #define ERR_FAIL() \ - if (1) { \ + if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed."); \ return; \ } else \ @@ -489,7 +489,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Prints `m_msg`, and the current function returns. */ #define ERR_FAIL_MSG(m_msg) \ - if (1) { \ + if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", DEBUG_STR(m_msg)); \ return; \ } else \ @@ -503,7 +503,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * The current function returns `m_retval`. */ #define ERR_FAIL_V(m_retval) \ - if (1) { \ + if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval)); \ return m_retval; \ } else \ @@ -516,7 +516,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Prints `m_msg`, and the current function returns `m_retval`. */ #define ERR_FAIL_V_MSG(m_retval, m_msg) \ - if (1) { \ + if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), DEBUG_STR(m_msg)); \ return m_retval; \ } else \ @@ -536,7 +536,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Prints `m_msg` once during the application lifetime. */ #define ERR_PRINT_ONCE(m_msg) \ - if (1) { \ + if (true) { \ static bool first_print = true; \ if (first_print) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg); \ @@ -561,7 +561,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. */ #define WARN_PRINT_ONCE(m_msg) \ - if (1) { \ + if (true) { \ static bool first_print = true; \ if (first_print) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, ERR_HANDLER_WARNING); \ @@ -576,7 +576,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Warns that the current function is deprecated. */ #define WARN_DEPRECATED \ - if (1) { \ + if (true) { \ static volatile bool warning_shown = false; \ if (!warning_shown) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", ERR_HANDLER_WARNING); \ @@ -589,7 +589,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Warns that the current function is deprecated and prints `m_msg`. */ #define WARN_DEPRECATED_MSG(m_msg) \ - if (1) { \ + if (true) { \ static volatile bool warning_shown = false; \ if (!warning_shown) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", DEBUG_STR(m_msg), ERR_HANDLER_WARNING); \ @@ -605,7 +605,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * The application crashes. */ #define CRASH_NOW() \ - if (1) { \ + if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed."); \ GENERATE_TRAP(); \ } else \ @@ -617,7 +617,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Prints `m_msg`, and then the application crashes. */ #define CRASH_NOW_MSG(m_msg) \ - if (1) { \ + if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", DEBUG_STR(m_msg)); \ GENERATE_TRAP(); \ } else \ diff --git a/core/hash_map.h b/core/hash_map.h index 843430d082..10fc931e7a 100644 --- a/core/hash_map.h +++ b/core/hash_map.h @@ -280,13 +280,13 @@ public: const TData &get(const TKey &p_key) const { const TData *res = getptr(p_key); - ERR_FAIL_COND_V(!res, *res); + CRASH_COND_MSG(!res, "Map key not found."); return *res; } TData &get(const TKey &p_key) { TData *res = getptr(p_key); - ERR_FAIL_COND_V(!res, *res); + CRASH_COND_MSG(!res, "Map key not found."); return *res; } diff --git a/core/image.cpp b/core/image.cpp index 0f15574053..e2f353698f 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -30,6 +30,7 @@ #include "image.h" +#include "core/error_macros.h" #include "core/hash_map.h" #include "core/io/image_loader.h" #include "core/io/resource_loader.h" @@ -678,34 +679,35 @@ static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict enum { FRAC_BITS = 8, FRAC_LEN = (1 << FRAC_BITS), + FRAC_HALF = (FRAC_LEN >> 1), FRAC_MASK = FRAC_LEN - 1 - }; for (uint32_t i = 0; i < p_dst_height; i++) { - uint32_t src_yofs_up_fp = (i * p_src_height * FRAC_LEN / p_dst_height); - uint32_t src_yofs_frac = src_yofs_up_fp & FRAC_MASK; - uint32_t src_yofs_up = src_yofs_up_fp >> FRAC_BITS; - - uint32_t src_yofs_down = (i + 1) * p_src_height / p_dst_height; + // Add 0.5 in order to interpolate based on pixel center + uint32_t src_yofs_up_fp = (i + 0.5) * p_src_height * FRAC_LEN / p_dst_height; + // Calculate nearest src pixel center above current, and truncate to get y index + uint32_t src_yofs_up = src_yofs_up_fp >= FRAC_HALF ? (src_yofs_up_fp - FRAC_HALF) >> FRAC_BITS : 0; + uint32_t src_yofs_down = (src_yofs_up_fp + FRAC_HALF) >> FRAC_BITS; if (src_yofs_down >= p_src_height) { src_yofs_down = p_src_height - 1; } - - //src_yofs_up*=CC; - //src_yofs_down*=CC; + // Calculate distance to pixel center of src_yofs_up + uint32_t src_yofs_frac = src_yofs_up_fp & FRAC_MASK; + src_yofs_frac = src_yofs_frac >= FRAC_HALF ? src_yofs_frac - FRAC_HALF : src_yofs_frac + FRAC_HALF; uint32_t y_ofs_up = src_yofs_up * p_src_width * CC; uint32_t y_ofs_down = src_yofs_down * p_src_width * CC; for (uint32_t j = 0; j < p_dst_width; j++) { - uint32_t src_xofs_left_fp = (j * p_src_width * FRAC_LEN / p_dst_width); - uint32_t src_xofs_frac = src_xofs_left_fp & FRAC_MASK; - uint32_t src_xofs_left = src_xofs_left_fp >> FRAC_BITS; - uint32_t src_xofs_right = (j + 1) * p_src_width / p_dst_width; + uint32_t src_xofs_left_fp = (j + 0.5) * p_src_width * FRAC_LEN / p_dst_width; + uint32_t src_xofs_left = src_xofs_left_fp >= FRAC_HALF ? (src_xofs_left_fp - FRAC_HALF) >> FRAC_BITS : 0; + uint32_t src_xofs_right = (src_xofs_left_fp + FRAC_HALF) >> FRAC_BITS; if (src_xofs_right >= p_src_width) { src_xofs_right = p_src_width - 1; } + uint32_t src_xofs_frac = src_xofs_left_fp & FRAC_MASK; + src_xofs_frac = src_xofs_frac >= FRAC_HALF ? src_xofs_frac - FRAC_HALF : src_xofs_frac + FRAC_HALF; src_xofs_left *= CC; src_xofs_right *= CC; @@ -1893,8 +1895,10 @@ Vector<uint8_t> Image::get_data() const { } void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { - ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH); - ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT); + ERR_FAIL_COND_MSG(p_width <= 0, "Image width must be greater than 0."); + ERR_FAIL_COND_MSG(p_height <= 0, "Image height must be greater than 0."); + ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + "."); + ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + "."); ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS)); int mm = 0; @@ -1913,8 +1917,10 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma } void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { - ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH); - ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT); + ERR_FAIL_COND_MSG(p_width <= 0, "Image width must be greater than 0."); + ERR_FAIL_COND_MSG(p_height <= 0, "Image height must be greater than 0."); + ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + "."); + ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + "."); ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS)); int mm; @@ -2628,6 +2634,7 @@ void Image::fill(const Color &c) { ImageMemLoadFunc Image::_png_mem_loader_func = nullptr; ImageMemLoadFunc Image::_jpg_mem_loader_func = nullptr; ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr; +ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr; void (*Image::_image_compress_bc_func)(Image *, float, Image::UsedChannels) = nullptr; void (*Image::_image_compress_bptc_func)(Image *, float, Image::UsedChannels) = nullptr; @@ -3057,6 +3064,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("load_png_from_buffer", "buffer"), &Image::load_png_from_buffer); ClassDB::bind_method(D_METHOD("load_jpg_from_buffer", "buffer"), &Image::load_jpg_from_buffer); ClassDB::bind_method(D_METHOD("load_webp_from_buffer", "buffer"), &Image::load_webp_from_buffer); + ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "_set_data", "_get_data"); @@ -3388,6 +3396,11 @@ Error Image::load_webp_from_buffer(const Vector<uint8_t> &p_array) { return _load_from_buffer(p_array, _webp_mem_loader_func); } +Error Image::load_tga_from_buffer(const Vector<uint8_t> &p_array) { + ERR_FAIL_NULL_V_MSG(_tga_mem_loader_func, ERR_UNAVAILABLE, "TGA module was not installed."); + return _load_from_buffer(p_array, _tga_mem_loader_func); +} + void Image::convert_rg_to_ra_rgba8() { ERR_FAIL_COND(format != FORMAT_RGBA8); ERR_FAIL_COND(!data.size()); diff --git a/core/image.h b/core/image.h index 53c203998e..711bf5721c 100644 --- a/core/image.h +++ b/core/image.h @@ -135,6 +135,7 @@ public: static ImageMemLoadFunc _png_mem_loader_func; static ImageMemLoadFunc _jpg_mem_loader_func; static ImageMemLoadFunc _webp_mem_loader_func; + static ImageMemLoadFunc _tga_mem_loader_func; static void (*_image_compress_bc_func)(Image *, float, UsedChannels p_channels); static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, UsedChannels p_channels); @@ -360,6 +361,7 @@ public: Error load_png_from_buffer(const Vector<uint8_t> &p_array); Error load_jpg_from_buffer(const Vector<uint8_t> &p_array); Error load_webp_from_buffer(const Vector<uint8_t> &p_array); + Error load_tga_from_buffer(const Vector<uint8_t> &p_array); void convert_rg_to_ra_rgba8(); void convert_ra_rgba8_to_rg(); diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index f9d2c9067c..534f3e44de 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -865,7 +865,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem bool near_match = false; for (int i = 0; i < res_remaps.size(); i++) { - int split = res_remaps[i].find_last(":"); + int split = res_remaps[i].rfind(":"); if (split == -1) { continue; } diff --git a/core/list.h b/core/list.h index 6052a619fb..f850db5241 100644 --- a/core/list.h +++ b/core/list.h @@ -348,7 +348,7 @@ public: * erase an element in the list, by iterator pointing to it. Return true if it was found/erased. */ bool erase(const Element *p_I) { - if (_data) { + if (_data && p_I) { bool ret = _data->erase(p_I); if (_data->size_cache == 0) { diff --git a/core/local_vector.h b/core/local_vector.h index 7f96b25f8b..d97f3330dc 100644 --- a/core/local_vector.h +++ b/core/local_vector.h @@ -45,6 +45,14 @@ private: T *data = nullptr; public: + T *ptr() { + return data; + } + + const T *ptr() const { + return data; + } + _FORCE_INLINE_ void push_back(T p_elem) { if (unlikely(count == capacity)) { if (capacity == 0) { diff --git a/core/math/basis.cpp b/core/math/basis.cpp index df5199b0f9..dd38e25bb1 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -773,7 +773,7 @@ Basis::operator String() const { mtx += ", "; } - mtx += rtos(elements[i][j]); + mtx += rtos(elements[j][i]); //matrix is stored transposed for performance, so print it transposed } } diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 6421606ca2..13a49feb6b 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -1064,7 +1064,7 @@ Error Expression::_get_token(Token &r_token) { if (is_float) { r_token.value = num.to_double(); } else { - r_token.value = num.to_int64(); + r_token.value = num.to_int(); } return OK; diff --git a/core/math/geometry_2d.cpp b/core/math/geometry_2d.cpp index 7d8fde8bcc..4636e1c774 100644 --- a/core/math/geometry_2d.cpp +++ b/core/math/geometry_2d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* geometry.cpp */ +/* geometry_2d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp index 3b30f4b1fe..7807ab19a7 100644 --- a/core/math/geometry_3d.cpp +++ b/core/math/geometry_3d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* geometry.cpp */ +/* geometry_3d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -993,6 +993,8 @@ Vector<uint32_t> Geometry3D::generate_edf(const Vector<bool> &p_voxels, const Ve } } + memdelete_arr(work_memory); + return ret; } diff --git a/core/math/geometry_3d.h b/core/math/geometry_3d.h index 64cd34892e..6bbf518141 100644 --- a/core/math/geometry_3d.h +++ b/core/math/geometry_3d.h @@ -945,6 +945,16 @@ public: return Color(va6 * v6, vb6 * v6, vc6 * v6, vd6 * v6); #undef STP } + + _FORCE_INLINE_ static Vector3 octahedron_map_decode(const Vector2 &p_uv) { + // https://twitter.com/Stubbesaurus/status/937994790553227264 + Vector2 f = p_uv * 2.0 - Vector2(1.0, 1.0); + Vector3 n = Vector3(f.x, f.y, 1.0f - Math::abs(f.x) - Math::abs(f.y)); + float t = CLAMP(-n.z, 0.0, 1.0); + n.x += n.x >= 0 ? -t : t; + n.y += n.y >= 0 ? -t : t; + return n.normalized(); + } }; #endif // GEOMETRY_3D_H diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp index dc68c2a9f9..434f6fa300 100644 --- a/core/os/main_loop.cpp +++ b/core/os/main_loop.cpp @@ -48,8 +48,10 @@ void MainLoop::_bind_methods() { BIND_CONSTANT(NOTIFICATION_WM_ABOUT); BIND_CONSTANT(NOTIFICATION_CRASH); BIND_CONSTANT(NOTIFICATION_OS_IME_UPDATE); - BIND_CONSTANT(NOTIFICATION_APP_RESUMED); - BIND_CONSTANT(NOTIFICATION_APP_PAUSED); + BIND_CONSTANT(NOTIFICATION_APPLICATION_RESUMED); + BIND_CONSTANT(NOTIFICATION_APPLICATION_PAUSED); + BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_IN); + BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_OUT); ADD_SIGNAL(MethodInfo("on_request_permissions_result", PropertyInfo(Variant::STRING, "permission"), PropertyInfo(Variant::BOOL, "granted"))); }; diff --git a/core/os/main_loop.h b/core/os/main_loop.h index 90790a45a1..2c34cf193c 100644 --- a/core/os/main_loop.h +++ b/core/os/main_loop.h @@ -52,8 +52,10 @@ public: NOTIFICATION_WM_ABOUT = 2011, NOTIFICATION_CRASH = 2012, NOTIFICATION_OS_IME_UPDATE = 2013, - NOTIFICATION_APP_RESUMED = 2014, - NOTIFICATION_APP_PAUSED = 2015, + NOTIFICATION_APPLICATION_RESUMED = 2014, + NOTIFICATION_APPLICATION_PAUSED = 2015, + NOTIFICATION_APPLICATION_FOCUS_IN = 2016, + NOTIFICATION_APPLICATION_FOCUS_OUT = 2017, }; virtual void init(); diff --git a/core/os/os.cpp b/core/os/os.cpp index c842be333c..231069fcfb 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -41,6 +41,7 @@ #include <stdarg.h> OS *OS::singleton = nullptr; +uint64_t OS::target_ticks = 0; OS *OS::get_singleton() { return singleton; @@ -468,6 +469,41 @@ void OS::close_midi_inputs() { } } +void OS::add_frame_delay(bool p_can_draw) { + const uint32_t frame_delay = Engine::get_singleton()->get_frame_delay(); + if (frame_delay) { + // Add fixed frame delay to decrease CPU/GPU usage. This doesn't take + // the actual frame time into account. + // Due to the high fluctuation of the actual sleep duration, it's not recommended + // to use this as a FPS limiter. + delay_usec(frame_delay * 1000); + } + + // Add a dynamic frame delay to decrease CPU/GPU usage. This takes the + // previous frame time into account for a smoother result. + uint64_t dynamic_delay = 0; + if (is_in_low_processor_usage_mode() || !p_can_draw) { + dynamic_delay = get_low_processor_usage_mode_sleep_usec(); + } + const int target_fps = Engine::get_singleton()->get_target_fps(); + if (target_fps > 0 && !Engine::get_singleton()->is_editor_hint()) { + // Override the low processor usage mode sleep delay if the target FPS is lower. + dynamic_delay = MAX(dynamic_delay, (uint64_t)(1000000 / target_fps)); + } + + if (dynamic_delay > 0) { + target_ticks += dynamic_delay; + uint64_t current_ticks = get_ticks_usec(); + + if (current_ticks < target_ticks) { + delay_usec(target_ticks - current_ticks); + } + + current_ticks = get_ticks_usec(); + target_ticks = MIN(MAX(target_ticks, current_ticks - dynamic_delay), current_ticks + dynamic_delay); + } +} + OS::OS() { void *volatile stack_bottom; diff --git a/core/os/os.h b/core/os/os.h index 04e10518dc..f21c0d4df7 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -43,6 +43,7 @@ class OS { static OS *singleton; + static uint64_t target_ticks; String _execpath; List<String> _cmdline; bool _keep_screen_on = true; // set default value to true, because this had been true before godot 2.0. @@ -212,6 +213,8 @@ public: virtual double get_unix_time() const; virtual void delay_usec(uint32_t p_usec) const = 0; + virtual void add_frame_delay(bool p_can_draw); + virtual uint64_t get_ticks_usec() const = 0; uint32_t get_ticks_msec() const; uint64_t get_splash_tick_msec() const; diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 5247f6da40..3f9585c03c 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -93,7 +93,7 @@ String ProjectSettings::localize_path(const String &p_path) const { } else { memdelete(dir); - int sep = path.find_last("/"); + int sep = path.rfind("/"); if (sep == -1) { return "res://" + path; } @@ -144,6 +144,12 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { if (p_value.get_type() == Variant::NIL) { props.erase(p_name); + if (p_name.operator String().begins_with("autoload/")) { + String node_name = p_name.operator String().split("/")[1]; + if (autoloads.has(node_name)) { + remove_autoload(node_name); + } + } } else { if (p_name == CoreStringNames::get_singleton()->_custom_features) { Vector<String> custom_feature_array = String(p_value).split(","); @@ -181,6 +187,19 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { } else { props[p_name] = VariantContainer(p_value, last_order++); } + if (p_name.operator String().begins_with("autoload/")) { + String node_name = p_name.operator String().split("/")[1]; + AutoloadInfo autoload; + autoload.name = node_name; + String path = p_value; + if (path.begins_with("*")) { + autoload.is_singleton = true; + autoload.path = path.substr(1); + } else { + autoload.path = path; + } + add_autoload(autoload); + } } return true; @@ -945,6 +964,29 @@ bool ProjectSettings::has_custom_feature(const String &p_feature) const { return custom_features.has(p_feature); } +Map<StringName, ProjectSettings::AutoloadInfo> ProjectSettings::get_autoload_list() const { + return autoloads; +} + +void ProjectSettings::add_autoload(const AutoloadInfo &p_autoload) { + ERR_FAIL_COND_MSG(p_autoload.name == StringName(), "Trying to add autoload with no name."); + autoloads[p_autoload.name] = p_autoload; +} + +void ProjectSettings::remove_autoload(const StringName &p_autoload) { + ERR_FAIL_COND_MSG(!autoloads.has(p_autoload), "Trying to remove non-existent autoload."); + autoloads.erase(p_autoload); +} + +bool ProjectSettings::has_autoload(const StringName &p_autoload) const { + return autoloads.has(p_autoload); +} + +ProjectSettings::AutoloadInfo ProjectSettings::get_autoload(const StringName &p_name) const { + ERR_FAIL_COND_V_MSG(!autoloads.has(p_name), AutoloadInfo(), "Trying to get non-existent autoload."); + return autoloads[p_name]; +} + void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting); ClassDB::bind_method(D_METHOD("set_setting", "name", "value"), &ProjectSettings::set_setting); diff --git a/core/project_settings.h b/core/project_settings.h index 3ed80738a1..4aceafe3c0 100644 --- a/core/project_settings.h +++ b/core/project_settings.h @@ -47,6 +47,12 @@ public: NO_BUILTIN_ORDER_BASE = 1 << 16 }; + struct AutoloadInfo { + StringName name; + String path; + bool is_singleton = false; + }; + protected: struct VariantContainer { int order = 0; @@ -79,6 +85,8 @@ protected: Set<String> custom_features; Map<StringName, StringName> feature_overrides; + Map<StringName, AutoloadInfo> autoloads; + bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; @@ -148,6 +156,12 @@ public: bool has_custom_feature(const String &p_feature) const; + Map<StringName, AutoloadInfo> get_autoload_list() const; + void add_autoload(const AutoloadInfo &p_autoload); + void remove_autoload(const StringName &p_autoload); + bool has_autoload(const StringName &p_autoload) const; + AutoloadInfo get_autoload(const StringName &p_name) const; + ProjectSettings(); ~ProjectSettings(); }; diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index c0f9eea76d..5dac42cacb 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -34,6 +34,7 @@ #include "core/class_db.h" #include "core/compressed_translation.h" #include "core/core_string_names.h" +#include "core/crypto/aes_context.h" #include "core/crypto/crypto.h" #include "core/crypto/hashing_context.h" #include "core/engine.h" @@ -165,6 +166,7 @@ void register_core_types() { // Crypto ClassDB::register_class<HashingContext>(); + ClassDB::register_class<AESContext>(); ClassDB::register_custom_instance_class<X509Certificate>(); ClassDB::register_custom_instance_class<CryptoKey>(); ClassDB::register_custom_instance_class<Crypto>(); diff --git a/core/ustring.cpp b/core/ustring.cpp index 444338d5ae..5d3cf5f1a4 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -1618,49 +1618,7 @@ String::String(const StrRange &p_range) { copy_from(p_range.c_str, p_range.len); } -int String::hex_to_int(bool p_with_prefix) const { - if (p_with_prefix && length() < 3) { - return 0; - } - - const CharType *s = ptr(); - - int sign = s[0] == '-' ? -1 : 1; - - if (sign < 0) { - s++; - } - - if (p_with_prefix) { - if (s[0] != '0' || s[1] != 'x') { - return 0; - } - s += 2; - } - - int hex = 0; - - while (*s) { - CharType c = LOWERCASE(*s); - int n; - if (c >= '0' && c <= '9') { - n = c - '0'; - } else if (c >= 'a' && c <= 'f') { - n = (c - 'a') + 10; - } else { - return 0; - } - - ERR_FAIL_COND_V_MSG(hex > INT32_MAX / 16, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + *this + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); - hex *= 16; - hex += n; - s++; - } - - return hex * sign; -} - -int64_t String::hex_to_int64(bool p_with_prefix) const { +int64_t String::hex_to_int(bool p_with_prefix) const { if (p_with_prefix && length() < 3) { return 0; } @@ -1692,8 +1650,9 @@ int64_t String::hex_to_int64(bool p_with_prefix) const { } else { return 0; } - - ERR_FAIL_COND_V_MSG(hex > INT64_MAX / 16, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + // Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error + bool overflow = ((hex > INT64_MAX / 16) && (sign == 1 || (sign == -1 && hex != (INT64_MAX >> 4) + 1))) || (sign == -1 && hex == (INT64_MAX >> 4) + 1 && c > '0'); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); hex *= 16; hex += n; s++; @@ -1702,7 +1661,7 @@ int64_t String::hex_to_int64(bool p_with_prefix) const { return hex * sign; } -int64_t String::bin_to_int64(bool p_with_prefix) const { +int64_t String::bin_to_int(bool p_with_prefix) const { if (p_with_prefix && length() < 3) { return 0; } @@ -1732,8 +1691,9 @@ int64_t String::bin_to_int64(bool p_with_prefix) const { } else { return 0; } - - ERR_FAIL_COND_V_MSG(binary > INT64_MAX / 2, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + // Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error + bool overflow = ((binary > INT64_MAX / 2) && (sign == 1 || (sign == -1 && binary != (INT64_MAX >> 1) + 1))) || (sign == -1 && binary == (INT64_MAX >> 1) + 1 && c > '0'); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); binary *= 2; binary += n; s++; @@ -1742,32 +1702,7 @@ int64_t String::bin_to_int64(bool p_with_prefix) const { return binary * sign; } -int String::to_int() const { - if (length() == 0) { - return 0; - } - - int to = (find(".") >= 0) ? find(".") : length(); - - int integer = 0; - int sign = 1; - - for (int i = 0; i < to; i++) { - CharType c = operator[](i); - if (c >= '0' && c <= '9') { - ERR_FAIL_COND_V_MSG(integer > INT32_MAX / 10, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + *this + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); - integer *= 10; - integer += c - '0'; - - } else if (integer == 0 && c == '-') { - sign = -sign; - } - } - - return integer * sign; -} - -int64_t String::to_int64() const { +int64_t String::to_int() const { if (length() == 0) { return 0; } @@ -1780,7 +1715,8 @@ int64_t String::to_int64() const { for (int i = 0; i < to; i++) { CharType c = operator[](i); if (c >= '0' && c <= '9') { - ERR_FAIL_COND_V_MSG(integer > INT64_MAX / 10, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); integer *= 10; integer += c - '0'; @@ -1792,7 +1728,7 @@ int64_t String::to_int64() const { return integer * sign; } -int String::to_int(const char *p_str, int p_len) { +int64_t String::to_int(const char *p_str, int p_len) { int to = 0; if (p_len >= 0) { to = p_len; @@ -1802,13 +1738,14 @@ int String::to_int(const char *p_str, int p_len) { } } - int integer = 0; - int sign = 1; + int64_t integer = 0; + int64_t sign = 1; for (int i = 0; i < to; i++) { char c = p_str[i]; if (c >= '0' && c <= '9') { - ERR_FAIL_COND_V_MSG(integer > INT32_MAX / 10, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); integer *= 10; integer += c - '0'; @@ -2343,18 +2280,6 @@ String String::substr(int p_from, int p_chars) const { return s; } -int String::find_last(const String &p_str) const { - int pos = -1; - int findfrom = 0; - int findres = -1; - while ((findres = find(p_str, findfrom)) != -1) { - pos = findres; - findfrom = pos + 1; - } - - return pos; -} - int String::find(const String &p_str, int p_from) const { if (p_from < 0) { return -1; @@ -2645,7 +2570,7 @@ int String::rfindn(const String &p_str, int p_from) const { } bool String::ends_with(const String &p_string) const { - int pos = find_last(p_string); + int pos = rfind(p_string); if (pos == -1) { return false; } @@ -3838,7 +3763,7 @@ bool String::is_valid_ip_address() const { continue; } if (n.is_valid_hex_number(false)) { - int nint = n.hex_to_int(false); + int64_t nint = n.hex_to_int(false); if (nint < 0 || nint > 0xffff) { return false; } @@ -3894,7 +3819,7 @@ String String::get_base_dir() const { } } - int sep = MAX(rs.find_last("/"), rs.find_last("\\")); + int sep = MAX(rs.rfind("/"), rs.rfind("\\")); if (sep == -1) { return base; } @@ -3903,7 +3828,7 @@ String String::get_base_dir() const { } String String::get_file() const { - int sep = MAX(find_last("/"), find_last("\\")); + int sep = MAX(rfind("/"), rfind("\\")); if (sep == -1) { return *this; } @@ -3912,8 +3837,8 @@ String String::get_file() const { } String String::get_extension() const { - int pos = find_last("."); - if (pos < 0 || pos < MAX(find_last("/"), find_last("\\"))) { + int pos = rfind("."); + if (pos < 0 || pos < MAX(rfind("/"), rfind("\\"))) { return ""; } @@ -4001,8 +3926,8 @@ String String::property_name_encode() const { } String String::get_basename() const { - int pos = find_last("."); - if (pos < 0 || pos < MAX(find_last("/"), find_last("\\"))) { + int pos = rfind("."); + if (pos < 0 || pos < MAX(rfind("/"), rfind("\\"))) { return *this; } diff --git a/core/ustring.h b/core/ustring.h index 5b13a1c704..e745475f11 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -202,7 +202,6 @@ public: int find(const String &p_str, int p_from = 0) const; ///< return <0 if failed int find(const char *p_str, int p_from = 0) const; ///< return <0 if failed int find_char(const CharType &p_char, int p_from = 0) const; ///< return <0 if failed - int find_last(const String &p_str) const; ///< return <0 if failed int findn(const String &p_str, int p_from = 0) const; ///< return <0 if failed, case insensitive int rfind(const String &p_str, int p_from = -1) const; ///< return <0 if failed int rfindn(const String &p_str, int p_from = -1) const; ///< return <0 if failed, case insensitive @@ -245,13 +244,11 @@ public: bool is_numeric() const; double to_double() const; float to_float() const; - int hex_to_int(bool p_with_prefix = true) const; - int to_int() const; - int64_t hex_to_int64(bool p_with_prefix = true) const; - int64_t bin_to_int64(bool p_with_prefix = true) const; - int64_t to_int64() const; - static int to_int(const char *p_str, int p_len = -1); + int64_t hex_to_int(bool p_with_prefix = true) const; + int64_t bin_to_int(bool p_with_prefix = true) const; + int64_t to_int() const; + static int64_t to_int(const char *p_str, int p_len = -1); static double to_double(const char *p_str); static double to_double(const CharType *p_str, const CharType **r_end = nullptr); static int64_t to_int(const CharType *p_str, int p_len = -1, bool p_clamp = false); diff --git a/core/variant.cpp b/core/variant.cpp index 21aaa0fe9e..f6b7e2821a 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -1409,7 +1409,7 @@ Variant::operator int64_t() const { case FLOAT: return _data._float; case STRING: - return operator String().to_int64(); + return operator String().to_int(); default: { return 0; } diff --git a/core/variant_call.cpp b/core/variant_call.cpp index a8beac1e44..0aa1339401 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -244,7 +244,6 @@ struct _VariantCall { VCALL_LOCALMEM3R(String, countn); VCALL_LOCALMEM2R(String, substr); VCALL_LOCALMEM2R(String, find); - VCALL_LOCALMEM1R(String, find_last); VCALL_LOCALMEM2R(String, findn); VCALL_LOCALMEM2R(String, rfind); VCALL_LOCALMEM2R(String, rfindn); @@ -702,6 +701,8 @@ struct _VariantCall { VCALL_PARRMEM1(PackedByteArray, uint8_t, remove); VCALL_PARRMEM1(PackedByteArray, uint8_t, append); VCALL_PARRMEM1(PackedByteArray, uint8_t, append_array); + VCALL_PARRMEM1R(PackedByteArray, uint8_t, has); + VCALL_PARRMEM0(PackedByteArray, uint8_t, sort); VCALL_PARRMEM0(PackedByteArray, uint8_t, invert); VCALL_PARRMEM2R(PackedByteArray, uint8_t, subarray); @@ -715,6 +716,8 @@ struct _VariantCall { VCALL_PARRMEM1(PackedInt32Array, int32_t, remove); VCALL_PARRMEM1(PackedInt32Array, int32_t, append); VCALL_PARRMEM1(PackedInt32Array, int32_t, append_array); + VCALL_PARRMEM1R(PackedInt32Array, int32_t, has); + VCALL_PARRMEM0(PackedInt32Array, int32_t, sort); VCALL_PARRMEM0(PackedInt32Array, int32_t, invert); VCALL_PARRMEM0R(PackedInt64Array, int64_t, size); @@ -727,6 +730,8 @@ struct _VariantCall { VCALL_PARRMEM1(PackedInt64Array, int64_t, remove); VCALL_PARRMEM1(PackedInt64Array, int64_t, append); VCALL_PARRMEM1(PackedInt64Array, int64_t, append_array); + VCALL_PARRMEM1R(PackedInt64Array, int64_t, has); + VCALL_PARRMEM0(PackedInt64Array, int64_t, sort); VCALL_PARRMEM0(PackedInt64Array, int64_t, invert); VCALL_PARRMEM0R(PackedFloat32Array, float, size); @@ -739,6 +744,8 @@ struct _VariantCall { VCALL_PARRMEM1(PackedFloat32Array, float, remove); VCALL_PARRMEM1(PackedFloat32Array, float, append); VCALL_PARRMEM1(PackedFloat32Array, float, append_array); + VCALL_PARRMEM1R(PackedFloat32Array, float, has); + VCALL_PARRMEM0(PackedFloat32Array, float, sort); VCALL_PARRMEM0(PackedFloat32Array, float, invert); VCALL_PARRMEM0R(PackedFloat64Array, double, size); @@ -751,6 +758,8 @@ struct _VariantCall { VCALL_PARRMEM1(PackedFloat64Array, double, remove); VCALL_PARRMEM1(PackedFloat64Array, double, append); VCALL_PARRMEM1(PackedFloat64Array, double, append_array); + VCALL_PARRMEM1R(PackedFloat64Array, double, has); + VCALL_PARRMEM0(PackedFloat64Array, double, sort); VCALL_PARRMEM0(PackedFloat64Array, double, invert); VCALL_PARRMEM0R(PackedStringArray, String, size); @@ -763,6 +772,8 @@ struct _VariantCall { VCALL_PARRMEM1(PackedStringArray, String, remove); VCALL_PARRMEM1(PackedStringArray, String, append); VCALL_PARRMEM1(PackedStringArray, String, append_array); + VCALL_PARRMEM1R(PackedStringArray, String, has); + VCALL_PARRMEM0(PackedStringArray, String, sort); VCALL_PARRMEM0(PackedStringArray, String, invert); VCALL_PARRMEM0R(PackedVector2Array, Vector2, size); @@ -775,6 +786,8 @@ struct _VariantCall { VCALL_PARRMEM1(PackedVector2Array, Vector2, remove); VCALL_PARRMEM1(PackedVector2Array, Vector2, append); VCALL_PARRMEM1(PackedVector2Array, Vector2, append_array); + VCALL_PARRMEM1R(PackedVector2Array, Vector2, has); + VCALL_PARRMEM0(PackedVector2Array, Vector2, sort); VCALL_PARRMEM0(PackedVector2Array, Vector2, invert); VCALL_PARRMEM0R(PackedVector3Array, Vector3, size); @@ -787,6 +800,8 @@ struct _VariantCall { VCALL_PARRMEM1(PackedVector3Array, Vector3, remove); VCALL_PARRMEM1(PackedVector3Array, Vector3, append); VCALL_PARRMEM1(PackedVector3Array, Vector3, append_array); + VCALL_PARRMEM1R(PackedVector3Array, Vector3, has); + VCALL_PARRMEM0(PackedVector3Array, Vector3, sort); VCALL_PARRMEM0(PackedVector3Array, Vector3, invert); VCALL_PARRMEM0R(PackedColorArray, Color, size); @@ -799,6 +814,8 @@ struct _VariantCall { VCALL_PARRMEM1(PackedColorArray, Color, remove); VCALL_PARRMEM1(PackedColorArray, Color, append); VCALL_PARRMEM1(PackedColorArray, Color, append_array); + VCALL_PARRMEM1R(PackedColorArray, Color, has); + VCALL_PARRMEM0(PackedColorArray, Color, sort); VCALL_PARRMEM0(PackedColorArray, Color, invert); #define VCALL_PTR0(m_type, m_method) \ @@ -1780,7 +1797,6 @@ void register_variant_methods() { ADDFUNC3R(STRING, INT, String, count, STRING, "what", INT, "from", INT, "to", varray(0, 0)); ADDFUNC3R(STRING, INT, String, countn, STRING, "what", INT, "from", INT, "to", varray(0, 0)); - ADDFUNC1R(STRING, INT, String, find_last, STRING, "what", varray()); ADDFUNC2R(STRING, INT, String, findn, STRING, "what", INT, "from", varray(0)); ADDFUNC2R(STRING, INT, String, rfind, STRING, "what", INT, "from", varray(-1)); ADDFUNC2R(STRING, INT, String, rfindn, STRING, "what", INT, "from", varray(-1)); @@ -2087,6 +2103,8 @@ void register_variant_methods() { ADDFUNC1(PACKED_BYTE_ARRAY, NIL, PackedByteArray, remove, INT, "idx", varray()); ADDFUNC2R(PACKED_BYTE_ARRAY, INT, PackedByteArray, insert, INT, "idx", INT, "byte", varray()); ADDFUNC1(PACKED_BYTE_ARRAY, NIL, PackedByteArray, resize, INT, "idx", varray()); + ADDFUNC1R(PACKED_BYTE_ARRAY, BOOL, PackedByteArray, has, INT, "value", varray()); + ADDFUNC0(PACKED_BYTE_ARRAY, NIL, PackedByteArray, sort, varray()); ADDFUNC0(PACKED_BYTE_ARRAY, NIL, PackedByteArray, invert, varray()); ADDFUNC2R(PACKED_BYTE_ARRAY, PACKED_BYTE_ARRAY, PackedByteArray, subarray, INT, "from", INT, "to", varray()); @@ -2105,6 +2123,8 @@ void register_variant_methods() { ADDFUNC1(PACKED_INT32_ARRAY, NIL, PackedInt32Array, remove, INT, "idx", varray()); ADDFUNC2R(PACKED_INT32_ARRAY, INT, PackedInt32Array, insert, INT, "idx", INT, "integer", varray()); ADDFUNC1(PACKED_INT32_ARRAY, NIL, PackedInt32Array, resize, INT, "idx", varray()); + ADDFUNC1R(PACKED_INT32_ARRAY, BOOL, PackedInt32Array, has, INT, "value", varray()); + ADDFUNC0(PACKED_INT32_ARRAY, NIL, PackedInt32Array, sort, varray()); ADDFUNC0(PACKED_INT32_ARRAY, NIL, PackedInt32Array, invert, varray()); ADDFUNC0R(PACKED_INT64_ARRAY, INT, PackedInt64Array, size, varray()); @@ -2116,6 +2136,8 @@ void register_variant_methods() { ADDFUNC1(PACKED_INT64_ARRAY, NIL, PackedInt64Array, remove, INT, "idx", varray()); ADDFUNC2R(PACKED_INT64_ARRAY, INT, PackedInt64Array, insert, INT, "idx", INT, "integer", varray()); ADDFUNC1(PACKED_INT64_ARRAY, NIL, PackedInt64Array, resize, INT, "idx", varray()); + ADDFUNC1R(PACKED_INT64_ARRAY, BOOL, PackedInt64Array, has, INT, "value", varray()); + ADDFUNC0(PACKED_INT64_ARRAY, NIL, PackedInt64Array, sort, varray()); ADDFUNC0(PACKED_INT64_ARRAY, NIL, PackedInt64Array, invert, varray()); ADDFUNC0R(PACKED_FLOAT32_ARRAY, INT, PackedFloat32Array, size, varray()); @@ -2127,6 +2149,8 @@ void register_variant_methods() { ADDFUNC1(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, remove, INT, "idx", varray()); ADDFUNC2R(PACKED_FLOAT32_ARRAY, INT, PackedFloat32Array, insert, INT, "idx", FLOAT, "value", varray()); ADDFUNC1(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, resize, INT, "idx", varray()); + ADDFUNC1R(PACKED_FLOAT32_ARRAY, BOOL, PackedFloat32Array, has, FLOAT, "value", varray()); + ADDFUNC0(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, sort, varray()); ADDFUNC0(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, invert, varray()); ADDFUNC0R(PACKED_FLOAT64_ARRAY, INT, PackedFloat64Array, size, varray()); @@ -2138,6 +2162,8 @@ void register_variant_methods() { ADDFUNC1(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, remove, INT, "idx", varray()); ADDFUNC2R(PACKED_FLOAT64_ARRAY, INT, PackedFloat64Array, insert, INT, "idx", FLOAT, "value", varray()); ADDFUNC1(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, resize, INT, "idx", varray()); + ADDFUNC1R(PACKED_FLOAT64_ARRAY, BOOL, PackedFloat64Array, has, FLOAT, "value", varray()); + ADDFUNC0(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, sort, varray()); ADDFUNC0(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, invert, varray()); ADDFUNC0R(PACKED_STRING_ARRAY, INT, PackedStringArray, size, varray()); @@ -2149,6 +2175,8 @@ void register_variant_methods() { ADDFUNC1(PACKED_STRING_ARRAY, NIL, PackedStringArray, remove, INT, "idx", varray()); ADDFUNC2R(PACKED_STRING_ARRAY, INT, PackedStringArray, insert, INT, "idx", STRING, "string", varray()); ADDFUNC1(PACKED_STRING_ARRAY, NIL, PackedStringArray, resize, INT, "idx", varray()); + ADDFUNC1R(PACKED_STRING_ARRAY, BOOL, PackedStringArray, has, STRING, "value", varray()); + ADDFUNC0(PACKED_STRING_ARRAY, NIL, PackedStringArray, sort, varray()); ADDFUNC0(PACKED_STRING_ARRAY, NIL, PackedStringArray, invert, varray()); ADDFUNC0R(PACKED_VECTOR2_ARRAY, INT, PackedVector2Array, size, varray()); @@ -2160,6 +2188,8 @@ void register_variant_methods() { ADDFUNC1(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, remove, INT, "idx", varray()); ADDFUNC2R(PACKED_VECTOR2_ARRAY, INT, PackedVector2Array, insert, INT, "idx", VECTOR2, "vector2", varray()); ADDFUNC1(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, resize, INT, "idx", varray()); + ADDFUNC1R(PACKED_VECTOR2_ARRAY, BOOL, PackedVector2Array, has, VECTOR2, "value", varray()); + ADDFUNC0(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, sort, varray()); ADDFUNC0(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, invert, varray()); ADDFUNC0R(PACKED_VECTOR3_ARRAY, INT, PackedVector3Array, size, varray()); @@ -2171,6 +2201,8 @@ void register_variant_methods() { ADDFUNC1(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, remove, INT, "idx", varray()); ADDFUNC2R(PACKED_VECTOR3_ARRAY, INT, PackedVector3Array, insert, INT, "idx", VECTOR3, "vector3", varray()); ADDFUNC1(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, resize, INT, "idx", varray()); + ADDFUNC1R(PACKED_VECTOR3_ARRAY, BOOL, PackedVector3Array, has, VECTOR3, "value", varray()); + ADDFUNC0(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, sort, varray()); ADDFUNC0(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, invert, varray()); ADDFUNC0R(PACKED_COLOR_ARRAY, INT, PackedColorArray, size, varray()); @@ -2182,6 +2214,8 @@ void register_variant_methods() { ADDFUNC1(PACKED_COLOR_ARRAY, NIL, PackedColorArray, remove, INT, "idx", varray()); ADDFUNC2R(PACKED_COLOR_ARRAY, INT, PackedColorArray, insert, INT, "idx", COLOR, "color", varray()); ADDFUNC1(PACKED_COLOR_ARRAY, NIL, PackedColorArray, resize, INT, "idx", varray()); + ADDFUNC1R(PACKED_COLOR_ARRAY, BOOL, PackedColorArray, has, COLOR, "value", varray()); + ADDFUNC0(PACKED_COLOR_ARRAY, NIL, PackedColorArray, sort, varray()); ADDFUNC0(PACKED_COLOR_ARRAY, NIL, PackedColorArray, invert, varray()); //pointerbased diff --git a/core/variant_op.cpp b/core/variant_op.cpp index 2c79e2029e..0c9a4a992a 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -1786,6 +1786,7 @@ Variant Variant::get_named(const StringName &p_index, bool *r_valid) const { if (r_valid) { *r_valid = true; } + switch (type) { case VECTOR2: { const Vector2 *v = reinterpret_cast<const Vector2 *>(_data._mem); diff --git a/core/vector.h b/core/vector.h index 4c152fb084..5fb630c21c 100644 --- a/core/vector.h +++ b/core/vector.h @@ -92,6 +92,10 @@ public: void append_array(Vector<T> p_other); + bool has(const T &p_val) { + return find(p_val, 0) != -1; + } + template <class C> void sort_custom() { int len = _cowdata.size(); |