diff options
Diffstat (limited to 'core')
64 files changed, 607 insertions, 316 deletions
diff --git a/core/array.cpp b/core/array.cpp index 108d9f7386..fd507f46c3 100644 --- a/core/array.cpp +++ b/core/array.cpp @@ -222,6 +222,66 @@ Array Array::duplicate(bool p_deep) const { return new_arr; } + +int Array::_fix_slice_index(int p_index, int p_arr_len, int p_top_mod) { + p_index = CLAMP(p_index, -p_arr_len, p_arr_len + p_top_mod); + if (p_index < 0) { + p_index = (p_index % p_arr_len + p_arr_len) % p_arr_len; // positive modulo + } + return p_index; +} + +int Array::_clamp_index(int p_index) const { + return CLAMP(p_index, -size() + 1, size() - 1); +} + +#define ARRAY_GET_DEEP(idx, is_deep) is_deep ? get(idx).duplicate(is_deep) : get(idx) + +Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const { // like python, but inclusive on upper bound + Array new_arr; + + if (empty()) // Don't try to slice empty arrays. + return new_arr; + + p_begin = Array::_fix_slice_index(p_begin, size(), -1); // can't start out of range + p_end = Array::_fix_slice_index(p_end, size(), 0); + + int x = p_begin; + int new_arr_i = 0; + + ERR_FAIL_COND_V(p_step == 0, new_arr); + if (Array::_clamp_index(p_begin) == Array::_clamp_index(p_end)) { // don't include element twice + new_arr.resize(1); + // new_arr[0] = 1; + new_arr[0] = ARRAY_GET_DEEP(Array::_clamp_index(p_begin), p_deep); + return new_arr; + } else { + int element_count = ceil((int)MAX(0, (p_end - p_begin) / p_step)) + 1; + if (element_count == 1) { // delta going in wrong direction to reach end + new_arr.resize(0); + return new_arr; + } + new_arr.resize(element_count); + } + + // if going backwards, have to have a different terminating condition + if (p_step < 0) { + while (x >= p_end) { + new_arr[new_arr_i] = ARRAY_GET_DEEP(Array::_clamp_index(x), p_deep); + x += p_step; + new_arr_i += 1; + } + } else if (p_step > 0) { + while (x <= p_end) { + new_arr[new_arr_i] = ARRAY_GET_DEEP(Array::_clamp_index(x), p_deep); + x += p_step; + new_arr_i += 1; + } + } + + return new_arr; +} + struct _ArrayVariantSort { _FORCE_INLINE_ bool operator()(const Variant &p_l, const Variant &p_r) const { diff --git a/core/array.h b/core/array.h index d4e937a486..7a754d53ea 100644 --- a/core/array.h +++ b/core/array.h @@ -44,6 +44,9 @@ class Array { void _ref(const Array &p_from) const; void _unref() const; + int _clamp_index(int p_index) const; + static int _fix_slice_index(int p_index, int p_arr_len, int p_top_mod); + public: Variant &operator[](int p_idx); const Variant &operator[](int p_idx) const; @@ -91,6 +94,8 @@ public: Array duplicate(bool p_deep = false) const; + Array slice(int p_begin, int p_end, int p_step = 1, bool p_deep = false) const; + Variant min() const; Variant max() const; diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 5161f8bab2..d07ba44788 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -145,13 +145,13 @@ _ResourceLoader::_ResourceLoader() { } Error _ResourceSaver::save(const String &p_path, const RES &p_resource, SaverFlags p_flags) { - ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, "Can't save empty resource to path: " + String(p_path) + "."); + ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, "Can't save empty resource to path '" + String(p_path) + "'."); return ResourceSaver::save(p_path, p_resource, p_flags); } PoolVector<String> _ResourceSaver::get_recognized_extensions(const RES &p_resource) { - ERR_FAIL_COND_V(p_resource.is_null(), PoolVector<String>()); + ERR_FAIL_COND_V_MSG(p_resource.is_null(), PoolVector<String>(), "It's not a reference to a valid Resource object."); List<String> exts; ResourceSaver::get_recognized_extensions(p_resource, &exts); PoolVector<String> ret; @@ -481,15 +481,18 @@ Error _OS::shell_open(String p_uri) { int _OS::execute(const String &p_path, const Vector<String> &p_arguments, bool p_blocking, Array p_output, bool p_read_stderr) { OS::ProcessID pid = -2; + int exitcode = 0; List<String> args; for (int i = 0; i < p_arguments.size(); i++) args.push_back(p_arguments[i]); String pipe; - Error err = OS::get_singleton()->execute(p_path, args, p_blocking, &pid, &pipe, NULL, p_read_stderr); + Error err = OS::get_singleton()->execute(p_path, args, p_blocking, &pid, &pipe, &exitcode, p_read_stderr); p_output.clear(); p_output.push_back(pipe); if (err != OK) return -1; + else if (p_blocking) + return exitcode; else return pid; } @@ -755,7 +758,7 @@ int64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const { ERR_FAIL_COND_V_MSG(month > 12 || month == 0, 0, "Invalid month value of: " + itos(month) + "."); // Do this check after month is tested as valid - ERR_FAIL_COND_V_MSG(day > MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1] || day == 0, 0, "Invalid day value of: " + itos(day) + " which is larger than " + itos(MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1]) + " or 0."); + ERR_FAIL_COND_V_MSG(day > MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1] || day == 0, 0, "Invalid day value of '" + itos(day) + "' which is larger than '" + itos(MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1]) + "' or 0."); // Calculate all the seconds from months past in this year uint64_t SECONDS_FROM_MONTHS_PAST_THIS_YEAR = DAYS_PAST_THIS_YEAR_TABLE[LEAPYEAR(year)][month - 1] * SECONDS_PER_DAY; @@ -1866,92 +1869,92 @@ bool _File::is_open() const { } String _File::get_path() const { - ERR_FAIL_COND_V(!f, ""); + ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); return f->get_path(); } String _File::get_path_absolute() const { - ERR_FAIL_COND_V(!f, ""); + ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); return f->get_path_absolute(); } void _File::seek(int64_t p_position) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->seek(p_position); } void _File::seek_end(int64_t p_position) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->seek_end(p_position); } int64_t _File::get_position() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_position(); } int64_t _File::get_len() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_len(); } bool _File::eof_reached() const { - ERR_FAIL_COND_V(!f, false); + ERR_FAIL_COND_V_MSG(!f, false, "File must be opened before use."); return f->eof_reached(); } uint8_t _File::get_8() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_8(); } uint16_t _File::get_16() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_16(); } uint32_t _File::get_32() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_32(); } uint64_t _File::get_64() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_64(); } float _File::get_float() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_float(); } double _File::get_double() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_double(); } real_t _File::get_real() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); return f->get_real(); } PoolVector<uint8_t> _File::get_buffer(int p_length) const { PoolVector<uint8_t> data; - ERR_FAIL_COND_V(!f, data); + ERR_FAIL_COND_V_MSG(!f, data, "File must be opened before use."); - ERR_FAIL_COND_V(p_length < 0, data); + ERR_FAIL_COND_V_MSG(p_length < 0, data, "Length of buffer cannot be smaller than 0."); if (p_length == 0) return data; Error err = data.resize(p_length); - ERR_FAIL_COND_V(err != OK, data); + ERR_FAIL_COND_V_MSG(err != OK, data, "Can't resize data to " + itos(p_length) + " elements."); PoolVector<uint8_t>::Write w = data.write(); int len = f->get_buffer(&w[0], p_length); @@ -1967,7 +1970,7 @@ PoolVector<uint8_t> _File::get_buffer(int p_length) const { String _File::get_as_text() const { - ERR_FAIL_COND_V(!f, String()); + ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use."); String text; size_t original_pos = f->get_position(); @@ -1997,12 +2000,12 @@ String _File::get_sha256(const String &p_path) const { String _File::get_line() const { - ERR_FAIL_COND_V(!f, String()); + ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use."); return f->get_line(); } Vector<String> _File::get_csv_line(const String &p_delim) const { - ERR_FAIL_COND_V(!f, Vector<String>()); + ERR_FAIL_COND_V_MSG(!f, Vector<String>(), "File must be opened before use."); return f->get_csv_line(p_delim); } @@ -2031,83 +2034,83 @@ Error _File::get_error() const { void _File::store_8(uint8_t p_dest) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_8(p_dest); } void _File::store_16(uint16_t p_dest) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_16(p_dest); } void _File::store_32(uint32_t p_dest) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_32(p_dest); } void _File::store_64(uint64_t p_dest) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_64(p_dest); } void _File::store_float(float p_dest) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_float(p_dest); } void _File::store_double(double p_dest) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_double(p_dest); } void _File::store_real(real_t p_real) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_real(p_real); } void _File::store_string(const String &p_string) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_string(p_string); } void _File::store_pascal_string(const String &p_string) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_pascal_string(p_string); }; String _File::get_pascal_string() { - ERR_FAIL_COND_V(!f, ""); + ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); return f->get_pascal_string(); }; void _File::store_line(const String &p_string) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_line(p_string); } void _File::store_csv_line(const Vector<String> &p_values, const String &p_delim) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); f->store_csv_line(p_values, p_delim); } void _File::store_buffer(const PoolVector<uint8_t> &p_buffer) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); int len = p_buffer.size(); if (len == 0) @@ -2125,17 +2128,17 @@ bool _File::file_exists(const String &p_name) const { void _File::store_var(const Variant &p_var, bool p_full_objects) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); int len; Error err = encode_variant(p_var, NULL, len, p_full_objects); - ERR_FAIL_COND(err != OK); + ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant."); PoolVector<uint8_t> buff; buff.resize(len); PoolVector<uint8_t>::Write w = buff.write(); err = encode_variant(p_var, &w[0], len, p_full_objects); - ERR_FAIL_COND(err != OK); + ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant."); w.release(); store_32(len); @@ -2144,7 +2147,7 @@ void _File::store_var(const Variant &p_var, bool p_full_objects) { Variant _File::get_var(bool p_allow_objects) const { - ERR_FAIL_COND_V(!f, Variant()); + ERR_FAIL_COND_V_MSG(!f, Variant(), "File must be opened before use."); uint32_t len = get_32(); PoolVector<uint8_t> buff = get_buffer(len); ERR_FAIL_COND_V((uint32_t)buff.size() != len, Variant()); @@ -2153,7 +2156,7 @@ Variant _File::get_var(bool p_allow_objects) const { Variant v; Error err = decode_variant(v, &r[0], len, NULL, p_allow_objects); - ERR_FAIL_COND_V(err != OK, Variant()); + ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to encode Variant."); return v; } @@ -2258,7 +2261,7 @@ Error _Directory::open(const String &p_path) { Error _Directory::list_dir_begin(bool p_skip_navigational, bool p_skip_hidden) { - ERR_FAIL_COND_V(!d, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); _list_skip_navigational = p_skip_navigational; _list_skip_hidden = p_skip_hidden; @@ -2268,7 +2271,7 @@ Error _Directory::list_dir_begin(bool p_skip_navigational, bool p_skip_hidden) { String _Directory::get_next() { - ERR_FAIL_COND_V(!d, ""); + ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use."); String next = d->get_next(); while (next != "" && ((_list_skip_navigational && (next == "." || next == "..")) || (_list_skip_hidden && d->current_is_hidden()))) { @@ -2279,44 +2282,44 @@ String _Directory::get_next() { } bool _Directory::current_is_dir() const { - ERR_FAIL_COND_V(!d, false); + ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use."); return d->current_is_dir(); } void _Directory::list_dir_end() { - ERR_FAIL_COND(!d); + ERR_FAIL_COND_MSG(!d, "Directory must be opened before use."); d->list_dir_end(); } int _Directory::get_drive_count() { - ERR_FAIL_COND_V(!d, 0); + ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use."); return d->get_drive_count(); } String _Directory::get_drive(int p_drive) { - ERR_FAIL_COND_V(!d, ""); + ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use."); return d->get_drive(p_drive); } int _Directory::get_current_drive() { - ERR_FAIL_COND_V(!d, 0); + ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use."); return d->get_current_drive(); } Error _Directory::change_dir(String p_dir) { - ERR_FAIL_COND_V(!d, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); return d->change_dir(p_dir); } String _Directory::get_current_dir() { - ERR_FAIL_COND_V(!d, ""); + ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use."); return d->get_current_dir(); } Error _Directory::make_dir(String p_dir) { - ERR_FAIL_COND_V(!d, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); if (!p_dir.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); Error err = d->make_dir(p_dir); @@ -2327,7 +2330,7 @@ Error _Directory::make_dir(String p_dir) { } Error _Directory::make_dir_recursive(String p_dir) { - ERR_FAIL_COND_V(!d, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); if (!p_dir.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); Error err = d->make_dir_recursive(p_dir); @@ -2339,7 +2342,7 @@ Error _Directory::make_dir_recursive(String p_dir) { bool _Directory::file_exists(String p_file) { - ERR_FAIL_COND_V(!d, false); + ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use."); if (!p_file.is_rel_path()) { return FileAccess::exists(p_file); @@ -2349,7 +2352,7 @@ bool _Directory::file_exists(String p_file) { } bool _Directory::dir_exists(String p_dir) { - ERR_FAIL_COND_V(!d, false); + ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use."); if (!p_dir.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); @@ -2364,18 +2367,18 @@ bool _Directory::dir_exists(String p_dir) { int _Directory::get_space_left() { - ERR_FAIL_COND_V(!d, 0); + ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use."); return d->get_space_left() / 1024 * 1024; //return value in megabytes, given binding is int } Error _Directory::copy(String p_from, String p_to) { - ERR_FAIL_COND_V(!d, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); return d->copy(p_from, p_to); } Error _Directory::rename(String p_from, String p_to) { - ERR_FAIL_COND_V(!d, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); if (!p_from.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_from); Error err = d->rename(p_from, p_to); @@ -2387,7 +2390,7 @@ Error _Directory::rename(String p_from, String p_to) { } Error _Directory::remove(String p_name) { - ERR_FAIL_COND_V(!d, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use."); if (!p_name.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_name); Error err = d->remove(p_name); @@ -2442,14 +2445,14 @@ String _Marshalls::variant_to_base64(const Variant &p_var, bool p_full_objects) int len; Error err = encode_variant(p_var, NULL, len, p_full_objects); - ERR_FAIL_COND_V(err != OK, ""); + ERR_FAIL_COND_V_MSG(err != OK, "", "Error when trying to encode Variant."); PoolVector<uint8_t> buff; buff.resize(len); PoolVector<uint8_t>::Write w = buff.write(); err = encode_variant(p_var, &w[0], len, p_full_objects); - ERR_FAIL_COND_V(err != OK, ""); + ERR_FAIL_COND_V_MSG(err != OK, "", "Error when trying to encode Variant."); String ret = CryptoCore::b64_encode_str(&w[0], len); ERR_FAIL_COND_V(ret == "", ret); @@ -2471,7 +2474,7 @@ Variant _Marshalls::base64_to_variant(const String &p_str, bool p_allow_objects) Variant v; Error err = decode_variant(v, &w[0], len, NULL, p_allow_objects); - ERR_FAIL_COND_V(err != OK, Variant()); + ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to decode Variant."); return v; }; @@ -2638,13 +2641,13 @@ void _Thread::_start_func(void *ud) { } } - ERR_FAIL_MSG("Could not call function '" + t->target_method.operator String() + "'' starting thread ID: " + t->get_id() + " Reason: " + reason + "."); + ERR_FAIL_MSG("Could not call function '" + t->target_method.operator String() + "' to start thread " + t->get_id() + ": " + reason + "."); } } Error _Thread::start(Object *p_instance, const StringName &p_method, const Variant &p_userdata, Priority p_priority) { - ERR_FAIL_COND_V(active, ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V_MSG(active, ERR_ALREADY_IN_USE, "Thread already started."); ERR_FAIL_COND_V(!p_instance, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_method == StringName(), ERR_INVALID_PARAMETER); ERR_FAIL_INDEX_V(p_priority, PRIORITY_MAX, ERR_INVALID_PARAMETER); @@ -2685,8 +2688,8 @@ bool _Thread::is_active() const { } Variant _Thread::wait_to_finish() { - ERR_FAIL_COND_V(!thread, Variant()); - ERR_FAIL_COND_V(!active, Variant()); + ERR_FAIL_COND_V_MSG(!thread, Variant(), "Thread must exist to wait for its completion."); + ERR_FAIL_COND_V_MSG(!active, Variant(), "Thread must be active to wait for its completion."); Thread::wait_to_finish(thread); Variant r = ret; active = false; diff --git a/core/class_db.cpp b/core/class_db.cpp index 3ad59bc309..f52937bdca 100644 --- a/core/class_db.cpp +++ b/core/class_db.cpp @@ -340,7 +340,7 @@ StringName ClassDB::get_parent_class(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_COND_V(!ti, StringName()); + ERR_FAIL_COND_V_MSG(!ti, StringName(), "Cannot get class '" + String(p_class) + "'."); return ti->inherits; } @@ -350,7 +350,7 @@ ClassDB::APIType ClassDB::get_api_type(const StringName &p_class) { ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_COND_V(!ti, API_NONE); + ERR_FAIL_COND_V_MSG(!ti, API_NONE, "Cannot get class '" + String(p_class) + "'."); return ti->api; } @@ -375,7 +375,7 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { for (List<StringName>::Element *E = names.front(); E; E = E->next()) { ClassInfo *t = classes.getptr(E->get()); - ERR_FAIL_COND_V(!t, 0); + ERR_FAIL_COND_V_MSG(!t, 0, "Cannot get class '" + String(E->get()) + "'."); if (t->api != p_api || !t->exposed) continue; hash = hash_djb2_one_64(t->name.hash(), hash); @@ -528,8 +528,8 @@ Object *ClassDB::instance(const StringName &p_class) { ti = classes.getptr(compat_classes[p_class]); } } - ERR_FAIL_COND_V(!ti, NULL); - ERR_FAIL_COND_V(ti->disabled, NULL); + ERR_FAIL_COND_V_MSG(!ti, NULL, "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_COND_V_MSG(ti->disabled, NULL, "Class '" + String(p_class) + "' is disabled."); ERR_FAIL_COND_V(!ti->creation_func, NULL); } #ifdef TOOLS_ENABLED @@ -545,7 +545,7 @@ bool ClassDB::can_instance(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_COND_V(!ti, false); + ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'."); #ifdef TOOLS_ENABLED if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { return false; @@ -560,7 +560,7 @@ void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherit const StringName &name = p_class; - ERR_FAIL_COND(classes.has(name)); + ERR_FAIL_COND_MSG(classes.has(name), "Class '" + String(p_class) + "' already exists."); classes[name] = ClassInfo(); ClassInfo &ti = classes[name]; @@ -836,7 +836,7 @@ void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) { #ifdef DEBUG_METHODS_ENABLED ClassInfo *check = type; while (check) { - ERR_FAIL_COND_MSG(check->signal_map.has(sname), "Type " + String(p_class) + " already has signal: " + String(sname) + "."); + ERR_FAIL_COND_MSG(check->signal_map.has(sname), "Class '" + String(p_class) + "' already has signal '" + String(sname) + "'."); check = check->inherits_ptr; } #endif @@ -922,10 +922,10 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons mb_set = get_method(p_class, p_setter); #ifdef DEBUG_METHODS_ENABLED - ERR_FAIL_COND_MSG(!mb_set, "Invalid setter: " + p_class + "::" + p_setter + " for property: " + p_pinfo.name + "."); + ERR_FAIL_COND_MSG(!mb_set, "Invalid setter '" + p_class + "::" + p_setter + "' for property '" + p_pinfo.name + "'."); int exp_args = 1 + (p_index >= 0 ? 1 : 0); - ERR_FAIL_COND_MSG(mb_set->get_argument_count() != exp_args, "Invalid function for setter: " + p_class + "::" + p_setter + " for property: " + p_pinfo.name + "."); + ERR_FAIL_COND_MSG(mb_set->get_argument_count() != exp_args, "Invalid function for setter '" + p_class + "::" + p_setter + " for property '" + p_pinfo.name + "'."); #endif } @@ -935,15 +935,15 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons mb_get = get_method(p_class, p_getter); #ifdef DEBUG_METHODS_ENABLED - ERR_FAIL_COND_MSG(!mb_get, "Invalid getter: " + p_class + "::" + p_getter + " for property: " + p_pinfo.name + "."); + ERR_FAIL_COND_MSG(!mb_get, "Invalid getter '" + p_class + "::" + p_getter + "' for property '" + p_pinfo.name + "'."); int exp_args = 0 + (p_index >= 0 ? 1 : 0); - ERR_FAIL_COND_MSG(mb_get->get_argument_count() != exp_args, "Invalid function for getter: " + p_class + "::" + p_getter + " for property: " + p_pinfo.name + "."); + ERR_FAIL_COND_MSG(mb_get->get_argument_count() != exp_args, "Invalid function for getter '" + p_class + "::" + p_getter + "' for property: '" + p_pinfo.name + "'."); #endif } #ifdef DEBUG_METHODS_ENABLED - ERR_FAIL_COND_MSG(type->property_setget.has(p_pinfo.name), "Object " + p_class + " already has property: " + p_pinfo.name + "."); + ERR_FAIL_COND_MSG(type->property_setget.has(p_pinfo.name), "Object '" + p_class + "' already has property '" + p_pinfo.name + "'."); #endif OBJTYPE_WLOCK @@ -1228,20 +1228,20 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c ClassInfo *type = classes.getptr(instance_type); if (!type) { memdelete(p_bind); - ERR_FAIL_V_MSG(NULL, "Couldn't bind method '" + mdname + "' for instance: " + instance_type + "."); + ERR_FAIL_V_MSG(NULL, "Couldn't bind method '" + mdname + "' for instance '" + instance_type + "'."); } if (type->method_map.has(mdname)) { memdelete(p_bind); // overloading not supported - ERR_FAIL_V_MSG(NULL, "Method already bound: " + instance_type + "::" + mdname + "."); + ERR_FAIL_V_MSG(NULL, "Method already bound '" + instance_type + "::" + mdname + "'."); } #ifdef DEBUG_METHODS_ENABLED if (method_name.args.size() > p_bind->get_argument_count()) { memdelete(p_bind); - ERR_FAIL_V_MSG(NULL, "Method definition provides more arguments than the method actually has: " + instance_type + "::" + mdname + "."); + ERR_FAIL_V_MSG(NULL, "Method definition provides more arguments than the method actually has '" + instance_type + "::" + mdname + "'."); } p_bind->set_argument_names(method_name.args); @@ -1265,7 +1265,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c } void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual) { - ERR_FAIL_COND(!classes.has(p_class)); + ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); OBJTYPE_WLOCK; @@ -1280,7 +1280,7 @@ void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_ void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance) { - ERR_FAIL_COND(!classes.has(p_class)); + ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); #ifdef DEBUG_METHODS_ENABLED @@ -1304,7 +1304,7 @@ void ClassDB::set_class_enabled(StringName p_class, bool p_enable) { OBJTYPE_WLOCK; - ERR_FAIL_COND(!classes.has(p_class)); + ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); classes[p_class].disabled = !p_enable; } @@ -1319,7 +1319,7 @@ bool ClassDB::is_class_enabled(StringName p_class) { } } - ERR_FAIL_COND_V(!ti, false); + ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'."); return !ti->disabled; } @@ -1328,7 +1328,7 @@ bool ClassDB::is_class_exposed(StringName p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_COND_V(!ti, false); + ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'."); return ti->exposed; } diff --git a/core/crypto/crypto.cpp b/core/crypto/crypto.cpp index 925a01b36a..83a25da901 100644 --- a/core/crypto/crypto.cpp +++ b/core/crypto/crypto.cpp @@ -149,7 +149,7 @@ Error ResourceFormatSaverCrypto::save(const String &p_path, const RES &p_resourc } else { ERR_FAIL_V(ERR_INVALID_PARAMETER); } - ERR_FAIL_COND_V(err != OK, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save Crypto resource to file '" + p_path + "'."); return OK; } diff --git a/core/crypto/hashing_context.cpp b/core/crypto/hashing_context.cpp index bdccb258dd..bd863f546b 100644 --- a/core/crypto/hashing_context.cpp +++ b/core/crypto/hashing_context.cpp @@ -103,7 +103,7 @@ void HashingContext::_create_ctx(HashType p_type) { } void HashingContext::_delete_ctx() { - return; + switch (type) { case HASH_MD5: memdelete((CryptoCore::MD5Context *)ctx); diff --git a/core/engine.cpp b/core/engine.cpp index 937439faaf..b9dc057257 100644 --- a/core/engine.cpp +++ b/core/engine.cpp @@ -38,7 +38,7 @@ void Engine::set_iterations_per_second(int p_ips) { - ERR_FAIL_COND(p_ips <= 0); + ERR_FAIL_COND_MSG(p_ips <= 0, "Engine iterations per second must be greater than 0."); ips = p_ips; } int Engine::get_iterations_per_second() const { diff --git a/core/hash_map.h b/core/hash_map.h index 81ddc376d0..38da1d59ab 100644 --- a/core/hash_map.h +++ b/core/hash_map.h @@ -112,7 +112,7 @@ private: void erase_hash_table() { - ERR_FAIL_COND(elements); + ERR_FAIL_COND_MSG(elements, "Cannot erase hash table if there are still elements inside."); memdelete_arr(hash_table); hash_table = 0; diff --git a/core/image.cpp b/core/image.cpp index 900efb0eb0..e0b0a1f8be 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -885,10 +885,10 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */; - ERR_FAIL_COND(p_width <= 0); - ERR_FAIL_COND(p_height <= 0); - ERR_FAIL_COND(p_width > MAX_WIDTH); - ERR_FAIL_COND(p_height > MAX_HEIGHT); + ERR_FAIL_COND_MSG(p_width <= 0, "Image width cannot be greater than 0."); + ERR_FAIL_COND_MSG(p_height <= 0, "Image height cannot be greater than 0."); + ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + "."); + ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + "."); if (p_width == width && p_height == height) return; @@ -1096,12 +1096,12 @@ void Image::crop_from_point(int p_x, int p_y, int p_width, int p_height) { ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot crop in compressed or custom image formats."); - ERR_FAIL_COND(p_x < 0); - ERR_FAIL_COND(p_y < 0); - ERR_FAIL_COND(p_width <= 0); - ERR_FAIL_COND(p_height <= 0); - ERR_FAIL_COND(p_x + p_width > MAX_WIDTH); - ERR_FAIL_COND(p_y + p_height > MAX_HEIGHT); + ERR_FAIL_COND_MSG(p_x < 0, "Start x position cannot be smaller than 0."); + ERR_FAIL_COND_MSG(p_y < 0, "Start y position cannot be smaller than 0."); + ERR_FAIL_COND_MSG(p_width <= 0, "Width of image must be greater than 0."); + ERR_FAIL_COND_MSG(p_height <= 0, "Height of image must be greater than 0."); + ERR_FAIL_COND_MSG(p_x + p_width > MAX_WIDTH, "End x position cannot be greater than " + itos(MAX_WIDTH) + "."); + ERR_FAIL_COND_MSG(p_y + p_height > MAX_HEIGHT, "End y position cannot be greater than " + itos(MAX_HEIGHT) + "."); /* to save memory, cropping should be done in-place, however, since this function will most likely either not be used much, or in critical areas, for now it won't, because @@ -2055,7 +2055,7 @@ Ref<Image> Image::get_rect(const Rect2 &p_area) const { void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest) { - ERR_FAIL_COND(p_src.is_null()); + ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); int dsize = data.size(); int srcdsize = p_src->data.size(); ERR_FAIL_COND(dsize == 0); @@ -2105,16 +2105,16 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest) { - ERR_FAIL_COND(p_src.is_null()); - ERR_FAIL_COND(p_mask.is_null()); + ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); + ERR_FAIL_COND_MSG(p_mask.is_null(), "It's not a reference to a valid Image object."); int dsize = data.size(); int srcdsize = p_src->data.size(); int maskdsize = p_mask->data.size(); ERR_FAIL_COND(dsize == 0); ERR_FAIL_COND(srcdsize == 0); ERR_FAIL_COND(maskdsize == 0); - ERR_FAIL_COND(p_src->width != p_mask->width); - ERR_FAIL_COND(p_src->height != p_mask->height); + ERR_FAIL_COND_MSG(p_src->width != p_mask->width, "Source image width is different from mask width."); + ERR_FAIL_COND_MSG(p_src->height != p_mask->height, "Source image height is different from mask height."); ERR_FAIL_COND(format != p_src->format); Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); @@ -2168,7 +2168,7 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest) { - ERR_FAIL_COND(p_src.is_null()); + ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); int dsize = data.size(); int srcdsize = p_src->data.size(); ERR_FAIL_COND(dsize == 0); @@ -2218,16 +2218,16 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const P void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest) { - ERR_FAIL_COND(p_src.is_null()); - ERR_FAIL_COND(p_mask.is_null()); + ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); + ERR_FAIL_COND_MSG(p_mask.is_null(), "It's not a reference to a valid Image object."); int dsize = data.size(); int srcdsize = p_src->data.size(); int maskdsize = p_mask->data.size(); ERR_FAIL_COND(dsize == 0); ERR_FAIL_COND(srcdsize == 0); ERR_FAIL_COND(maskdsize == 0); - ERR_FAIL_COND(p_src->width != p_mask->width); - ERR_FAIL_COND(p_src->height != p_mask->height); + ERR_FAIL_COND_MSG(p_src->width != p_mask->width, "Source image width is different from mask width."); + ERR_FAIL_COND_MSG(p_src->height != p_mask->height, "Source image height is different from mask height."); ERR_FAIL_COND(format != p_src->format); Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); diff --git a/core/image.h b/core/image.h index f29a30cda0..94ee8a2c33 100644 --- a/core/image.h +++ b/core/image.h @@ -356,7 +356,7 @@ public: void set_pixel(int p_x, int p_y, const Color &p_color); void copy_internals_from(const Ref<Image> &p_image) { - ERR_FAIL_COND(p_image.is_null()); + ERR_FAIL_COND_MSG(p_image.is_null(), "It's not a reference to a valid Image object."); format = p_image->format; width = p_image->width; height = p_image->height; diff --git a/core/input_map.cpp b/core/input_map.cpp index 2a8ac435fe..05c75febf2 100644 --- a/core/input_map.cpp +++ b/core/input_map.cpp @@ -56,7 +56,7 @@ void InputMap::_bind_methods() { void InputMap::add_action(const StringName &p_action, float p_deadzone) { - ERR_FAIL_COND(input_map.has(p_action)); + ERR_FAIL_COND_MSG(input_map.has(p_action), "InputMap already has action '" + String(p_action) + "'."); input_map[p_action] = Action(); static int last_id = 1; input_map[p_action].id = last_id; @@ -66,7 +66,7 @@ void InputMap::add_action(const StringName &p_action, float p_deadzone) { void InputMap::erase_action(const StringName &p_action) { - ERR_FAIL_COND(!input_map.has(p_action)); + ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); input_map.erase(p_action); } @@ -126,15 +126,15 @@ bool InputMap::has_action(const StringName &p_action) const { void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) { - ERR_FAIL_COND(!input_map.has(p_action)); + ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); input_map[p_action].deadzone = p_deadzone; } void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event) { - ERR_FAIL_COND(p_event.is_null()); - ERR_FAIL_COND(!input_map.has(p_action)); + ERR_FAIL_COND_MSG(p_event.is_null(), "It's not a reference to a valid InputEvent object."); + ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); if (_find_event(input_map[p_action], p_event)) return; //already gots @@ -143,13 +143,13 @@ void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent bool InputMap::action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event) { - ERR_FAIL_COND_V(!input_map.has(p_action), false); + ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); return (_find_event(input_map[p_action], p_event) != NULL); } void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) { - ERR_FAIL_COND(!input_map.has(p_action)); + ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); List<Ref<InputEvent> >::Element *E = _find_event(input_map[p_action], p_event); if (E) @@ -158,7 +158,7 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEve void InputMap::action_erase_events(const StringName &p_action) { - ERR_FAIL_COND(!input_map.has(p_action)); + ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); input_map[p_action].inputs.clear(); } @@ -192,7 +192,7 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed, float *p_strength) const { Map<StringName, Action>::Element *E = input_map.find(p_action); - ERR_FAIL_COND_V_MSG(!E, false, "Request for nonexistent InputMap action: " + String(p_action) + "."); + ERR_FAIL_COND_V_MSG(!E, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); Ref<InputEventAction> input_event_action = p_event; if (input_event_action.is_valid()) { @@ -333,6 +333,6 @@ void InputMap::load_default() { InputMap::InputMap() { - ERR_FAIL_COND(singleton); + ERR_FAIL_COND_MSG(singleton, "Singleton in InputMap already exist."); singleton = this; } diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index 9063e028be..271e5c5a0b 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -111,7 +111,7 @@ void ConfigFile::get_sections(List<String> *r_sections) const { } void ConfigFile::get_section_keys(const String &p_section, List<String> *r_keys) const { - ERR_FAIL_COND(!values.has(p_section)); + ERR_FAIL_COND_MSG(!values.has(p_section), "Cannont get keys from nonexistent section '" + p_section + "'."); for (OrderedHashMap<String, Variant>::ConstElement E = values[p_section].front(); E; E = E.next()) { r_keys->push_back(E.key()); diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index 102cd9cf6c..a52c6f79c9 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -195,7 +195,7 @@ bool FileAccessCompressed::is_open() const { void FileAccessCompressed::seek(size_t p_position) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); if (writing) { ERR_FAIL_COND(p_position > write_max); @@ -227,7 +227,7 @@ void FileAccessCompressed::seek(size_t p_position) { void FileAccessCompressed::seek_end(int64_t p_position) { - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); if (writing) { seek(write_max + p_position); @@ -238,7 +238,7 @@ void FileAccessCompressed::seek_end(int64_t p_position) { } size_t FileAccessCompressed::get_position() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); if (writing) { return write_pos; @@ -249,7 +249,7 @@ size_t FileAccessCompressed::get_position() const { } size_t FileAccessCompressed::get_len() const { - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); if (writing) { return write_max; @@ -260,7 +260,7 @@ size_t FileAccessCompressed::get_len() const { bool FileAccessCompressed::eof_reached() const { - ERR_FAIL_COND_V(!f, false); + ERR_FAIL_COND_V_MSG(!f, false, "File must be opened before use."); if (writing) { return false; } else { @@ -270,8 +270,8 @@ bool FileAccessCompressed::eof_reached() const { uint8_t FileAccessCompressed::get_8() const { - ERR_FAIL_COND_V(writing, 0); - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); if (at_end) { read_eof = true; @@ -301,8 +301,8 @@ uint8_t FileAccessCompressed::get_8() const { } int FileAccessCompressed::get_buffer(uint8_t *p_dst, int p_length) const { - ERR_FAIL_COND_V(writing, 0); - ERR_FAIL_COND_V(!f, 0); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); if (at_end) { read_eof = true; @@ -342,16 +342,16 @@ Error FileAccessCompressed::get_error() const { } void FileAccessCompressed::flush() { - ERR_FAIL_COND(!f); - ERR_FAIL_COND(!writing); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); // compressed files keep data in memory till close() } void FileAccessCompressed::store_8(uint8_t p_dest) { - ERR_FAIL_COND(!f); - ERR_FAIL_COND(!writing); + ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); WRITE_FIT(1); write_ptr[write_pos++] = p_dest; diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index 77decc107d..c2e4e0f575 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -41,7 +41,7 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8_t> &p_key, Mode p_mode) { - ERR_FAIL_COND_V(file != NULL, ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V_MSG(file != NULL, ERR_ALREADY_IN_USE, "Can't open file while another file from path '" + file->get_path_absolute() + "' is open."); ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER); pos = 0; @@ -176,6 +176,22 @@ bool FileAccessEncrypted::is_open() const { return file != NULL; } +String FileAccessEncrypted::get_path() const { + + if (file) + return file->get_path(); + else + return ""; +} + +String FileAccessEncrypted::get_path_absolute() const { + + if (file) + return file->get_path_absolute(); + else + return ""; +} + void FileAccessEncrypted::seek(size_t p_position) { if (p_position > (size_t)data.size()) @@ -205,7 +221,7 @@ bool FileAccessEncrypted::eof_reached() const { uint8_t FileAccessEncrypted::get_8() const { - ERR_FAIL_COND_V(writing, 0); + ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); if (pos >= data.size()) { eofed = true; return 0; @@ -217,7 +233,7 @@ uint8_t FileAccessEncrypted::get_8() const { } int FileAccessEncrypted::get_buffer(uint8_t *p_dst, int p_length) const { - ERR_FAIL_COND_V(writing, 0); + ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); int to_copy = MIN(p_length, data.size() - pos); for (int i = 0; i < to_copy; i++) { @@ -239,7 +255,7 @@ Error FileAccessEncrypted::get_error() const { void FileAccessEncrypted::store_buffer(const uint8_t *p_src, int p_length) { - ERR_FAIL_COND(!writing); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); if (pos < data.size()) { @@ -259,14 +275,14 @@ void FileAccessEncrypted::store_buffer(const uint8_t *p_src, int p_length) { } void FileAccessEncrypted::flush() { - ERR_FAIL_COND(!writing); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); // encrypted files keep data in memory till close() } void FileAccessEncrypted::store_8(uint8_t p_dest) { - ERR_FAIL_COND(!writing); + ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); if (pos < data.size()) { data.write[pos] = p_dest; diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index d779a150ac..c3be0f7de8 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -60,6 +60,9 @@ public: virtual void close(); ///< close a file virtual bool is_open() const; ///< true when file is open + virtual String get_path() const; /// returns the path for the current open file + virtual String get_path_absolute() const; /// returns the absolute path for the current open file + virtual void seek(size_t p_position); ///< seek to a given position virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file virtual size_t get_position() const; ///< get position in the file diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index fbcf5b8021..c0acd36751 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -90,7 +90,7 @@ Error FileAccessMemory::_open(const String &p_path, int p_mode_flags) { //name = DirAccess::normalize_path(name); Map<String, Vector<uint8_t> >::Element *E = files->find(name); - ERR_FAIL_COND_V(!E, ERR_FILE_NOT_FOUND); + ERR_FAIL_COND_V_MSG(!E, ERR_FILE_NOT_FOUND, "Can't find file '" + p_path + "'."); data = E->get().ptrw(); length = E->get().size(); diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index d1c7f5c334..e653a924ba 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -195,7 +195,7 @@ Error FileAccessNetworkClient::connect(const String &p_host, int p_port, const S DEBUG_PRINT("IP: " + String(ip) + " port " + itos(p_port)); Error err = client->connect_to_host(ip, p_port); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot connect to host with IP: " + String(ip) + " and port: " + itos(p_port)); while (client->get_status() == StreamPeerTCP::STATUS_CONNECTING) { //DEBUG_PRINT("trying to connect...."); OS::get_singleton()->delay_usec(1000); @@ -339,7 +339,7 @@ bool FileAccessNetwork::is_open() const { void FileAccessNetwork::seek(size_t p_position) { - ERR_FAIL_COND(!opened); + ERR_FAIL_COND_MSG(!opened, "File must be opened before use."); eof_flag = p_position > total_size; if (p_position >= total_size) { @@ -355,18 +355,18 @@ void FileAccessNetwork::seek_end(int64_t p_position) { } size_t FileAccessNetwork::get_position() const { - ERR_FAIL_COND_V(!opened, 0); + ERR_FAIL_COND_V_MSG(!opened, 0, "File must be opened before use."); return pos; } size_t FileAccessNetwork::get_len() const { - ERR_FAIL_COND_V(!opened, 0); + ERR_FAIL_COND_V_MSG(!opened, 0, "File must be opened before use."); return total_size; } bool FileAccessNetwork::eof_reached() const { - ERR_FAIL_COND_V(!opened, false); + ERR_FAIL_COND_V_MSG(!opened, false, "File must be opened before use."); return eof_flag; } diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index d49d36c2b9..54ef753b7c 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -36,11 +36,11 @@ #define PACK_VERSION 1 -Error PackedData::add_pack(const String &p_path) { +Error PackedData::add_pack(const String &p_path, bool p_replace_files) { for (int i = 0; i < sources.size(); i++) { - if (sources[i]->try_open_pack(p_path)) { + if (sources[i]->try_open_pack(p_path, p_replace_files)) { return OK; }; @@ -49,7 +49,7 @@ Error PackedData::add_pack(const String &p_path) { return ERR_FILE_UNRECOGNIZED; }; -void PackedData::add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src) { +void PackedData::add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files) { PathMD5 pmd5(path.md5_buffer()); //printf("adding path %ls, %lli, %lli\n", path.c_str(), pmd5.a, pmd5.b); @@ -64,7 +64,8 @@ void PackedData::add_path(const String &pkg_path, const String &path, uint64_t o pf.md5[i] = p_md5[i]; pf.src = p_src; - files[pmd5] = pf; + if (!exists || p_replace_files) + files[pmd5] = pf; if (!exists) { //search for dir @@ -133,7 +134,7 @@ PackedData::~PackedData() { ////////////////////////////////////////////////////////////////// -bool PackedSourcePCK::try_open_pack(const String &p_path) { +bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files) { FileAccess *f = FileAccess::open(p_path, FileAccess::READ); if (!f) @@ -196,7 +197,7 @@ bool PackedSourcePCK::try_open_pack(const String &p_path) { uint64_t size = f->get_64(); uint8_t md5[16]; f->get_buffer(md5, 16); - PackedData::get_singleton()->add_path(p_path, path, ofs, size, md5, this); + PackedData::get_singleton()->add_path(p_path, path, ofs, size, md5, this, p_replace_files); }; return true; @@ -321,7 +322,7 @@ FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFil pf(p_file), f(FileAccess::open(pf.pack, FileAccess::READ)) { - ERR_FAIL_COND_MSG(!f, "Can't open pack-referenced file: " + String(pf.pack) + "."); + ERR_FAIL_COND_MSG(!f, "Can't open pack-referenced file '" + String(pf.pack) + "'."); f->seek(pf.offset); pos = 0; diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index a21dd7d22d..8c34069f3a 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -102,13 +102,13 @@ private: public: void add_pack_source(PackSource *p_source); - void add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src); // for PackSource + void add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files); // for PackSource void set_disabled(bool p_disabled) { disabled = p_disabled; } _FORCE_INLINE_ bool is_disabled() const { return disabled; } static PackedData *get_singleton() { return singleton; } - Error add_pack(const String &p_path); + Error add_pack(const String &p_path, bool p_replace_files); _FORCE_INLINE_ FileAccess *try_open_path(const String &p_path); _FORCE_INLINE_ bool has_path(const String &p_path); @@ -120,7 +120,7 @@ public: class PackSource { public: - virtual bool try_open_pack(const String &p_path) = 0; + virtual bool try_open_pack(const String &p_path, bool p_replace_files) = 0; virtual FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file) = 0; virtual ~PackSource() {} }; @@ -128,7 +128,7 @@ public: class PackedSourcePCK : public PackSource { public: - virtual bool try_open_pack(const String &p_path); + virtual bool try_open_pack(const String &p_path, bool p_replace_files); virtual FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file); }; diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index be28c9a877..3187f3bab6 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -117,7 +117,7 @@ static void godot_free(voidpf opaque, voidpf address) { void ZipArchive::close_handle(unzFile p_file) const { - ERR_FAIL_COND(!p_file); + ERR_FAIL_COND_MSG(!p_file, "Cannot close a file if none is open."); FileAccess *f = (FileAccess *)unzGetOpaque(p_file); unzCloseCurrentFile(p_file); unzClose(p_file); @@ -126,11 +126,11 @@ void ZipArchive::close_handle(unzFile p_file) const { unzFile ZipArchive::get_file_handle(String p_file) const { - ERR_FAIL_COND_V(!file_exists(p_file), NULL); + ERR_FAIL_COND_V_MSG(!file_exists(p_file), NULL, "File '" + p_file + " doesn't exist."); File file = files[p_file]; FileAccess *f = FileAccess::open(packages[file.package].filename, FileAccess::READ); - ERR_FAIL_COND_V(!f, NULL); + ERR_FAIL_COND_V_MSG(!f, NULL, "Cannot open file '" + packages[file.package].filename + "'."); zlib_filefunc_def io; zeromem(&io, sizeof(io)); @@ -160,7 +160,7 @@ unzFile ZipArchive::get_file_handle(String p_file) const { return pkg; } -bool ZipArchive::try_open_pack(const String &p_path) { +bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files) { //printf("opening zip pack %ls, %i, %i\n", p_name.c_str(), p_name.extension().nocasecmp_to("zip"), p_name.extension().nocasecmp_to("pcz")); if (p_path.get_extension().nocasecmp_to("zip") != 0 && p_path.get_extension().nocasecmp_to("pcz") != 0) @@ -194,7 +194,7 @@ bool ZipArchive::try_open_pack(const String &p_path) { packages.push_back(pkg); int pkg_num = packages.size() - 1; - for (unsigned int i = 0; i < gi.number_entry; i++) { + for (uint64_t i = 0; i < gi.number_entry; i++) { char filename_inzip[256]; @@ -210,7 +210,7 @@ bool ZipArchive::try_open_pack(const String &p_path) { files[fname] = f; uint8_t md5[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - PackedData::get_singleton()->add_path(p_path, fname, 1, 0, md5, this); + PackedData::get_singleton()->add_path(p_path, fname, 1, 0, md5, this, p_replace_files); //printf("packed data add path %ls, %ls\n", p_name.c_str(), fname.c_str()); if ((i + 1) < gi.number_entry) { diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index 217176c0af..cdd50f9eb3 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -74,7 +74,7 @@ public: bool file_exists(String p_name) const; - virtual bool try_open_pack(const String &p_path); + virtual bool try_open_pack(const String &p_path, bool p_replace_files); FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file); static ZipArchive *get_singleton(); diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index a759e615c7..095c2abb54 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -46,14 +46,14 @@ bool ImageFormatLoader::recognize(const String &p_extension) const { } Error ImageLoader::load_image(String p_file, Ref<Image> p_image, FileAccess *p_custom, bool p_force_linear, float p_scale) { - ERR_FAIL_COND_V(p_image.is_null(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(p_image.is_null(), ERR_INVALID_PARAMETER, "It's not a reference to a valid Image object."); FileAccess *f = p_custom; if (!f) { Error err; f = FileAccess::open(p_file, FileAccess::READ, &err); if (!f) { - ERR_PRINTS("Error opening file: " + p_file); + ERR_PRINTS("Error opening file '" + p_file + "'."); return err; } } diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 3d87131b51..f1b6570799 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -305,7 +305,7 @@ IP *(*IP::_create)() = NULL; IP *IP::create() { - ERR_FAIL_COND_V(singleton, NULL); + ERR_FAIL_COND_V_MSG(singleton, NULL, "IP singleton already exist."); ERR_FAIL_COND_V(!_create, NULL); return _create(); } diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp index df4be9b9fd..0980027f42 100644 --- a/core/io/ip_address.cpp +++ b/core/io/ip_address.cpp @@ -181,7 +181,7 @@ bool IP_Address::is_ipv4() const { } const uint8_t *IP_Address::get_ipv4() const { - ERR_FAIL_COND_V(!is_ipv4(), &(field8[12])); // Not the correct IPv4 (it's an IPv6), but we don't want to return a null pointer risking an engine crash. + ERR_FAIL_COND_V_MSG(!is_ipv4(), &(field8[12]), "IPv4 requested, but current IP is IPv6."); // Not the correct IPv4 (it's an IPv6), but we don't want to return a null pointer risking an engine crash. return &(field8[12]); } diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index b386feb14c..2ae542bca7 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -478,7 +478,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int int used; Error err = decode_variant(key, buf, len, &used, p_allow_objects); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to decode Variant."); buf += used; len -= used; @@ -487,7 +487,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } err = decode_variant(value, buf, len, &used, p_allow_objects); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to decode Variant."); buf += used; len -= used; @@ -522,7 +522,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int int used = 0; Variant v; Error err = decode_variant(v, buf, len, &used, p_allow_objects); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to decode Variant."); buf += used; len -= used; varr.push_back(v); diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index ed6905c9a6..0ba84d0c8f 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -917,8 +917,7 @@ int MultiplayerAPI::_get_bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, i = (i + p_buffer.size() - 1) % p_buffer.size(); } - ERR_EXPLAIN("Reached the end of the bandwidth profiler buffer, values might be inaccurate."); - ERR_FAIL_COND_V(i == p_pointer, total_bandwidth); + ERR_FAIL_COND_V_MSG(i == p_pointer, total_bandwidth, "Reached the end of the bandwidth profiler buffer, values might be inaccurate."); return total_bandwidth; } diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 1c792c43d1..821a04ebad 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -101,9 +101,9 @@ Error PacketPeer::put_var(const Variant &p_packet, bool p_full_objects) { return OK; uint8_t *buf = (uint8_t *)alloca(len); - ERR_FAIL_COND_V(!buf, ERR_OUT_OF_MEMORY); + ERR_FAIL_COND_V_MSG(!buf, ERR_OUT_OF_MEMORY, "Out of memory."); err = encode_variant(p_packet, buf, len, p_full_objects || allow_object_decoding); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to encode Variant."); return put_packet(buf, len); } @@ -150,7 +150,7 @@ void PacketPeer::_bind_methods() { void PacketPeerStream::_set_stream_peer(REF p_peer) { - ERR_FAIL_COND(p_peer.is_null()); + ERR_FAIL_COND_MSG(p_peer.is_null(), "It's not a reference to a valid Resource object."); set_stream_peer(p_peer); } diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 1c89bc6268..443f390bb7 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -107,7 +107,7 @@ Error PCKPacker::add_file(const String &p_file, const String &p_src) { Error PCKPacker::flush(bool p_verbose) { - ERR_FAIL_COND_V(!file, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(!file, ERR_INVALID_PARAMETER, "File must be opened before use."); // write the index diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 0ad2479b05..e91dd579b5 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -378,10 +378,10 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { for (uint32_t i = 0; i < len; i++) { Variant key; Error err = parse_variant(key); - ERR_FAIL_COND_V(err, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Error when trying to parse Variant."); Variant value; err = parse_variant(value); - ERR_FAIL_COND_V(err, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Error when trying to parse Variant."); d[key] = value; } r_v = d; @@ -395,7 +395,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { for (uint32_t i = 0; i < len; i++) { Variant val; Error err = parse_variant(val); - ERR_FAIL_COND_V(err, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Error when trying to parse Variant."); a[i] = val; } r_v = a; @@ -983,7 +983,7 @@ Ref<ResourceInteractiveLoader> ResourceFormatLoaderBinary::load_interactive(cons Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - ERR_FAIL_COND_V(err != OK, Ref<ResourceInteractiveLoader>()); + ERR_FAIL_COND_V_MSG(err != OK, Ref<ResourceInteractiveLoader>(), "Cannot open file '" + p_path + "'."); Ref<ResourceInteractiveLoaderBinary> ria = memnew(ResourceInteractiveLoaderBinary); String path = p_original_path != "" ? p_original_path : p_path; @@ -1032,7 +1032,7 @@ bool ResourceFormatLoaderBinary::handles_type(const String &p_type) const { void ResourceFormatLoaderBinary::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "Cannot open file '" + p_path + "'."); Ref<ResourceInteractiveLoaderBinary> ria = memnew(ResourceInteractiveLoaderBinary); ria->local_path = ProjectSettings::get_singleton()->localize_path(p_path); @@ -1046,7 +1046,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons //Error error=OK; FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); + ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open file '" + p_path + "'."); FileAccess *fw = NULL; //=FileAccess::open(p_path+".depren"); @@ -1066,7 +1066,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons if (err) { memdelete(fac); memdelete(facw); - ERR_FAIL_COND_V(err, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Cannot create file '" + p_path + ".depren'."); } fw = facw; @@ -1076,13 +1076,13 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons //error=ERR_FILE_UNRECOGNIZED; memdelete(f); - ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unrecognized binary resource file: " + local_path + "."); + ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unrecognized binary resource file '" + local_path + "'."); } else { fw = FileAccess::open(p_path + ".depren", FileAccess::WRITE); if (!fw) { memdelete(f); } - ERR_FAIL_COND_V(!fw, ERR_CANT_CREATE); + ERR_FAIL_COND_V_MSG(!fw, ERR_CANT_CREATE, "Cannot create file '" + p_path + ".depren'."); uint8_t magic[4] = { 'R', 'S', 'R', 'C' }; fw->store_buffer(magic, 4); @@ -1113,12 +1113,12 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons memdelete(da); //use the old approach - WARN_PRINTS("This file is old, so it can't refactor dependencies, opening and resaving: " + p_path + "."); + WARN_PRINTS("This file is old, so it can't refactor dependencies, opening and resaving '" + p_path + "'."); Error err; f = FileAccess::open(p_path, FileAccess::READ, &err); - ERR_FAIL_COND_V(err != OK, ERR_FILE_CANT_OPEN); + ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_OPEN, "Cannot open file '" + p_path + "'."); Ref<ResourceInteractiveLoaderBinary> ria = memnew(ResourceInteractiveLoaderBinary); ria->local_path = ProjectSettings::get_singleton()->localize_path(p_path); @@ -1751,7 +1751,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p f = FileAccess::open(p_path, FileAccess::WRITE, &err); } - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create file '" + p_path + "'."); relative_paths = p_flags & ResourceSaver::FLAG_RELATIVE_PATHS; skip_editor = p_flags & ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 9e954890bc..f3eba44973 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -205,7 +205,7 @@ RES ResourceFormatLoader::load(const String &p_path, const String &p_original_pa if (r_error) *r_error = err; - ERR_FAIL_COND_V(err != OK, RES()); + ERR_FAIL_COND_V_MSG(err != OK, RES(), "Failed to load resource '" + p_path + "'."); } } diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index a9ad62afe6..7aa8732366 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -158,7 +158,7 @@ void ResourceSaver::get_recognized_extensions(const RES &p_resource, List<String void ResourceSaver::add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front) { - ERR_FAIL_COND(p_format_saver.is_null()); + ERR_FAIL_COND_MSG(p_format_saver.is_null(), "It's not a reference to a valid ResourceFormatSaver object."); ERR_FAIL_COND(saver_count >= MAX_SAVERS); if (p_at_front) { @@ -174,7 +174,7 @@ void ResourceSaver::add_resource_format_saver(Ref<ResourceFormatSaver> p_format_ void ResourceSaver::remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver) { - ERR_FAIL_COND(p_format_saver.is_null()); + ERR_FAIL_COND_MSG(p_format_saver.is_null(), "It's not a reference to a valid ResourceFormatSaver object."); // Find saver int i = 0; diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 84b8554d54..f19e055b64 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -370,7 +370,7 @@ Variant StreamPeer::get_var(bool p_allow_objects) { Variant ret; err = decode_variant(ret, var.ptr(), len, NULL, p_allow_objects); - ERR_FAIL_COND_V(err != OK, Variant()); + ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to decode Variant."); return ret; } diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp index 310bb12bc0..b9c5896b24 100644 --- a/core/io/stream_peer_tcp.cpp +++ b/core/io/stream_peer_tcp.cpp @@ -248,16 +248,7 @@ void StreamPeerTCP::set_no_delay(bool p_enabled) { bool StreamPeerTCP::is_connected_to_host() const { - if (status == STATUS_NONE || status == STATUS_ERROR) { - - return false; - } - - if (status != STATUS_CONNECTED) { - return true; - } - - return _sock.is_valid() && _sock->is_open(); + return _sock.is_valid() && _sock->is_open() && (status == STATUS_CONNECTED || status == STATUS_CONNECTING); } StreamPeerTCP::Status StreamPeerTCP::get_status() { diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index e8e71c10ca..9b6888ac21 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -182,7 +182,7 @@ RES TranslationLoaderPO::load(const String &p_path, const String &p_original_pat *r_error = ERR_CANT_OPEN; FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V(!f, RES()); + ERR_FAIL_COND_V_MSG(!f, RES(), "Cannot open file '" + p_path + "'."); return load_translation(f, r_error); } diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index 9487947365..575c78734f 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -484,7 +484,7 @@ Error XMLParser::open(const String &p_path) { Error err; FileAccess *file = FileAccess::open(p_path, FileAccess::READ, &err); - ERR_FAIL_COND_V(err != OK, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'."); length = file->get_len(); ERR_FAIL_COND_V(length < 1, ERR_FILE_CORRUPT); diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index 60b7326c29..ae2b56e7b7 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -257,14 +257,14 @@ void AStar::reserve_space(int p_num_nodes) { points.reserve(p_num_nodes); } -int AStar::get_closest_point(const Vector3 &p_point) const { +int AStar::get_closest_point(const Vector3 &p_point, bool p_include_disabled) const { int closest_id = -1; real_t closest_dist = 1e20; for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) { - if (!(*it.value)->enabled) continue; // Disabled points should not be considered. + if (!p_include_disabled && !(*it.value)->enabled) continue; // Disabled points should not be considered. real_t d = p_point.distance_squared_to((*it.value)->pos); if (closest_id < 0 || d < closest_dist) { @@ -540,7 +540,7 @@ void AStar::_bind_methods() { ClassDB::bind_method(D_METHOD("reserve_space", "num_nodes"), &AStar::reserve_space); ClassDB::bind_method(D_METHOD("clear"), &AStar::clear); - ClassDB::bind_method(D_METHOD("get_closest_point", "to_position"), &AStar::get_closest_point); + ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar::get_closest_point, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar::get_closest_position_in_segment); ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar::get_point_path); @@ -638,8 +638,8 @@ void AStar2D::reserve_space(int p_num_nodes) { astar.reserve_space(p_num_nodes); } -int AStar2D::get_closest_point(const Vector2 &p_point) const { - return astar.get_closest_point(Vector3(p_point.x, p_point.y, 0)); +int AStar2D::get_closest_point(const Vector2 &p_point, bool p_include_disabled) const { + return astar.get_closest_point(Vector3(p_point.x, p_point.y, 0), p_include_disabled); } Vector2 AStar2D::get_closest_position_in_segment(const Vector2 &p_point) const { @@ -693,7 +693,7 @@ void AStar2D::_bind_methods() { ClassDB::bind_method(D_METHOD("reserve_space", "num_nodes"), &AStar2D::reserve_space); ClassDB::bind_method(D_METHOD("clear"), &AStar2D::clear); - ClassDB::bind_method(D_METHOD("get_closest_point", "to_position"), &AStar2D::get_closest_point); + ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar2D::get_closest_point, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar2D::get_closest_position_in_segment); ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar2D::get_point_path); diff --git a/core/math/a_star.h b/core/math/a_star.h index ec2a06f07f..0a5d3e992c 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -141,7 +141,7 @@ public: void reserve_space(int p_num_nodes); void clear(); - int get_closest_point(const Vector3 &p_point) const; + int get_closest_point(const Vector3 &p_point, bool p_include_disabled = false) const; Vector3 get_closest_position_in_segment(const Vector3 &p_point) const; PoolVector<Vector3> get_point_path(int p_from_id, int p_to_id); @@ -183,7 +183,7 @@ public: void reserve_space(int p_num_nodes); void clear(); - int get_closest_point(const Vector2 &p_point) const; + int get_closest_point(const Vector2 &p_point, bool p_include_disabled = false) const; Vector2 get_closest_position_in_segment(const Vector2 &p_point) const; PoolVector<Vector2> get_point_path(int p_from_id, int p_to_id); diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 2985959113..0a491010e2 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -807,7 +807,7 @@ void Basis::set_quat(const Quat &p_quat) { void Basis::set_axis_angle(const Vector3 &p_axis, real_t p_phi) { // Rotation matrix from axis and angle, see https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_angle #ifdef MATH_CHECKS - ERR_FAIL_COND(!p_axis.is_normalized()); + ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "Axis must be normalized."); #endif Vector3 axis_sq(p_axis.x * p_axis.x, p_axis.y * p_axis.y, p_axis.z * p_axis.z); real_t cosine = Math::cos(p_phi); diff --git a/core/math/disjoint_set.cpp b/core/math/disjoint_set.cpp new file mode 100644 index 0000000000..c9d47aa0ae --- /dev/null +++ b/core/math/disjoint_set.cpp @@ -0,0 +1,31 @@ +/*************************************************************************/ +/* disjoint_set.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "disjoint_set.h" diff --git a/core/math/disjoint_set.h b/core/math/disjoint_set.h new file mode 100644 index 0000000000..c9b3d0b65d --- /dev/null +++ b/core/math/disjoint_set.h @@ -0,0 +1,155 @@ +/*************************************************************************/ +/* disjoint_set.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef DISJOINT_SET_H +#define DISJOINT_SET_H + +#include "core/map.h" +#include "core/vector.h" + +/** + @author Marios Staikopoulos <marios@staik.net> +*/ + +/* This DisjointSet class uses Find with path compression and Union by rank */ +template <typename T, class C = Comparator<T>, class AL = DefaultAllocator> +class DisjointSet { + + struct Element { + T object; + Element *parent = nullptr; + int rank = 0; + }; + + typedef Map<T, Element *, C, AL> MapT; + + MapT elements; + + Element *get_parent(Element *element); + + _FORCE_INLINE_ Element *insert_or_get(T object); + +public: + ~DisjointSet(); + + _FORCE_INLINE_ void insert(T object) { (void)insert_or_get(object); } + + void create_union(T a, T b); + + void get_representatives(Vector<T> &out_roots); + + void get_members(Vector<T> &out_members, T representative); +}; + +/* FUNCTIONS */ + +template <typename T, class C, class AL> +DisjointSet<T, C, AL>::~DisjointSet() { + for (typename MapT::Element *itr = elements.front(); itr != nullptr; itr = itr->next()) { + memdelete_allocator<Element, AL>(itr->value()); + } +} + +template <typename T, class C, class AL> +typename DisjointSet<T, C, AL>::Element *DisjointSet<T, C, AL>::get_parent(Element *element) { + if (element->parent != element) { + element->parent = get_parent(element->parent); + } + + return element->parent; +} + +template <typename T, class C, class AL> +typename DisjointSet<T, C, AL>::Element *DisjointSet<T, C, AL>::insert_or_get(T object) { + typename MapT::Element *itr = elements.find(object); + if (itr != nullptr) { + return itr->value(); + } + + Element *new_element = memnew_allocator(Element, AL); + new_element->object = object; + new_element->parent = new_element; + elements.insert(object, new_element); + + return new_element; +} + +template <typename T, class C, class AL> +void DisjointSet<T, C, AL>::create_union(T a, T b) { + + Element *x = insert_or_get(a); + Element *y = insert_or_get(b); + + Element *x_root = get_parent(x); + Element *y_root = get_parent(y); + + // Already in the same set + if (x_root == y_root) + return; + + // Not in the same set, merge + if (x_root->rank < y_root->rank) { + SWAP(x_root, y_root); + } + + // Merge y_root into x_root + y_root->parent = x_root; + if (x_root->rank == y_root->rank) { + ++x_root->rank; + } +} + +template <typename T, class C, class AL> +void DisjointSet<T, C, AL>::get_representatives(Vector<T> &out_representatives) { + for (typename MapT::Element *itr = elements.front(); itr != nullptr; itr = itr->next()) { + Element *element = itr->value(); + if (element->parent == element) { + out_representatives.push_back(element->object); + } + } +} + +template <typename T, class C, class AL> +void DisjointSet<T, C, AL>::get_members(Vector<T> &out_members, T representative) { + typename MapT::Element *rep_itr = elements.find(representative); + ERR_FAIL_COND(rep_itr == nullptr); + + Element *rep_element = rep_itr->value(); + ERR_FAIL_COND(rep_element->parent != rep_element); + + for (typename MapT::Element *itr = elements.front(); itr != nullptr; itr = itr->next()) { + Element *parent = get_parent(itr->value()); + if (parent == rep_element) { + out_members.push_back(itr->key()); + } + } +} + +#endif diff --git a/core/math/geometry.cpp b/core/math/geometry.cpp index f37db90929..e0ead8446f 100644 --- a/core/math/geometry.cpp +++ b/core/math/geometry.cpp @@ -241,10 +241,7 @@ PoolVector<PoolVector<Face3> > Geometry::separate_objects(PoolVector<Face3> p_ar bool error = _connect_faces(_fcptr, len, -1); - if (error) { - - ERR_FAIL_COND_V(error, PoolVector<PoolVector<Face3> >()); // Invalid geometry. - } + ERR_FAIL_COND_V_MSG(error, PoolVector<PoolVector<Face3> >(), "Invalid geometry."); // Group connected faces in separate objects. @@ -715,7 +712,7 @@ Vector<Vector<Vector2> > Geometry::decompose_polygon_in_convex(Vector<Point2> po decomp.write[idx].resize(tp.GetNumPoints()); - for (int i = 0; i < tp.GetNumPoints(); i++) { + for (int64_t i = 0; i < tp.GetNumPoints(); i++) { decomp.write[idx].write[i] = tp.GetPoint(i); } diff --git a/core/message_queue.cpp b/core/message_queue.cpp index 390989ac91..a76b5167b6 100644 --- a/core/message_queue.cpp +++ b/core/message_queue.cpp @@ -340,7 +340,7 @@ bool MessageQueue::is_flushing() const { MessageQueue::MessageQueue() { - ERR_FAIL_COND(singleton != NULL); + ERR_FAIL_COND_MSG(singleton != NULL, "MessageQueue singleton already exist."); singleton = this; flushing = false; diff --git a/core/node_path.cpp b/core/node_path.cpp index 8244785d84..b43f76f680 100644 --- a/core/node_path.cpp +++ b/core/node_path.cpp @@ -375,7 +375,7 @@ NodePath::NodePath(const String &p_path) { if (str == "") { if (path[i] == 0) continue; // Allow end-of-path : - ERR_FAIL_MSG("Invalid NodePath: " + p_path + "."); + ERR_FAIL_MSG("Invalid NodePath '" + p_path + "'."); } subpath.push_back(str); diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h index 5ea6d8b0d4..1a466e57f4 100644 --- a/core/oa_hash_map.h +++ b/core/oa_hash_map.h @@ -37,10 +37,11 @@ #include "core/os/memory.h" /** - * A HashMap implementation that uses open addressing with robinhood hashing. - * Robinhood hashing swaps out entries that have a smaller probing distance + * A HashMap implementation that uses open addressing with Robin Hood hashing. + * Robin Hood hashing swaps out entries that have a smaller probing distance * than the to-be-inserted entry, that evens out the average probing distance - * and enables faster lookups. + * and enables faster lookups. Backward shift deletion is employed to further + * improve the performance and to avoid infinite loops in rare cases. * * The entries are stored inplace, so huge keys or values might fill cache lines * a lot faster. @@ -60,25 +61,20 @@ private: uint32_t num_elements; static const uint32_t EMPTY_HASH = 0; - static const uint32_t DELETED_HASH_BIT = 1 << 31; _FORCE_INLINE_ uint32_t _hash(const TKey &p_key) const { uint32_t hash = Hasher::hash(p_key); if (hash == EMPTY_HASH) { hash = EMPTY_HASH + 1; - } else if (hash & DELETED_HASH_BIT) { - hash &= ~DELETED_HASH_BIT; } return hash; } _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash) const { - p_hash = p_hash & ~DELETED_HASH_BIT; // we don't care if it was deleted or not - uint32_t original_pos = p_hash % capacity; - return (p_pos - original_pos) % capacity; + return (p_pos - original_pos + capacity) % capacity; } _FORCE_INLINE_ void _construct(uint32_t p_pos, uint32_t p_hash, const TKey &p_key, const TValue &p_value) { @@ -132,14 +128,6 @@ private: // not an empty slot, let's check the probing length of the existing one uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos]); if (existing_probe_len < distance) { - - if (hashes[pos] & DELETED_HASH_BIT) { - // we found a place where we can fit in! - _construct(pos, hash, key, value); - - return; - } - SWAP(hash, hashes[pos]); SWAP(key, keys[pos]); SWAP(value, values[pos]); @@ -173,9 +161,6 @@ private: if (old_hashes[i] == EMPTY_HASH) { continue; } - if (old_hashes[i] & DELETED_HASH_BIT) { - continue; - } _insert_with_hash(old_hashes[i], old_keys[i], old_values[i]); } @@ -205,10 +190,6 @@ public: continue; } - if (hashes[i] & DELETED_HASH_BIT) { - continue; - } - hashes[i] = EMPTY_HASH; values[i].~TValue(); keys[i].~TKey(); @@ -219,7 +200,7 @@ public: void insert(const TKey &p_key, const TValue &p_value) { - if ((float)num_elements / (float)capacity > 0.9) { + if (num_elements + 1 > 0.9 * capacity) { _resize_and_rehash(); } @@ -272,9 +253,20 @@ public: return; } - hashes[pos] |= DELETED_HASH_BIT; + uint32_t next_pos = (pos + 1) % capacity; + while (hashes[next_pos] != EMPTY_HASH && + _get_probe_length(next_pos, hashes[next_pos]) != 0) { + SWAP(hashes[next_pos], hashes[pos]); + SWAP(keys[next_pos], keys[pos]); + SWAP(values[next_pos], values[pos]); + pos = next_pos; + next_pos = (pos + 1) % capacity; + } + + hashes[pos] = EMPTY_HASH; values[pos].~TValue(); keys[pos].~TKey(); + num_elements--; } @@ -326,9 +318,6 @@ public: if (hashes[i] == EMPTY_HASH) { continue; } - if (hashes[i] & DELETED_HASH_BIT) { - continue; - } it.valid = true; it.key = &keys[i]; diff --git a/core/object.cpp b/core/object.cpp index 62bfa31480..6facf38733 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -1100,9 +1100,9 @@ void Object::get_meta_list(List<String> *p_list) const { void Object::add_user_signal(const MethodInfo &p_signal) { - ERR_FAIL_COND(p_signal.name == ""); - ERR_FAIL_COND(ClassDB::has_signal(get_class_name(), p_signal.name)); - ERR_FAIL_COND(signal_map.has(p_signal.name)); + ERR_FAIL_COND_MSG(p_signal.name == "", "Signal name cannot be empty."); + ERR_FAIL_COND_MSG(ClassDB::has_signal(get_class_name(), p_signal.name), "User signal's name conflicts with a built-in signal of '" + get_class_name() + "'."); + ERR_FAIL_COND_MSG(signal_map.has(p_signal.name), "Trying to add already existing signal '" + p_signal.name + "'."); Signal s; s.user = p_signal; signal_map[p_signal.name] = s; diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp index b444f0ae1e..e7496055ec 100644 --- a/core/os/dir_access.cpp +++ b/core/os/dir_access.cpp @@ -244,7 +244,7 @@ DirAccess *DirAccess::open(const String &p_path, Error *r_error) { DirAccess *da = create_for_path(p_path); - ERR_FAIL_COND_V(!da, NULL); + ERR_FAIL_COND_V_MSG(!da, NULL, "Cannot create DirAccess for path '" + p_path + "'."); Error err = da->change_dir(p_path); if (r_error) *r_error = err; @@ -384,39 +384,36 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag String target_dir = p_to + rel_path; if (!p_target_da->dir_exists(target_dir)) { Error err = p_target_da->make_dir(target_dir); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + target_dir + "'."); } Error err = change_dir(E->get()); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + E->get() + "'."); + err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags); if (err) { change_dir(".."); - ERR_PRINT("Failed to copy recursively"); - return err; + ERR_FAIL_V_MSG(err, "Failed to copy recursively."); } err = change_dir(".."); - if (err) { - ERR_PRINT("Failed to go back"); - return err; - } + ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to go back."); } return OK; } Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags) { - ERR_FAIL_COND_V(!dir_exists(p_from), ERR_FILE_NOT_FOUND); + ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist."); DirAccess *target_da = DirAccess::create_for_path(p_to); - ERR_FAIL_COND_V(!target_da, ERR_CANT_CREATE); + ERR_FAIL_COND_V_MSG(!target_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_to + "'."); if (!target_da->dir_exists(p_to)) { Error err = target_da->make_dir_recursive(p_to); if (err) { memdelete(target_da); } - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + p_to + "'."); } if (!p_to.ends_with("/")) { diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index 9a8315a3bb..738e597730 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -498,7 +498,7 @@ uint64_t FileAccess::get_modified_time(const String &p_file) { return 0; FileAccess *fa = create_for_path(p_file); - ERR_FAIL_COND_V(!fa, 0); + ERR_FAIL_COND_V_MSG(!fa, 0, "Cannot create FileAccess for path '" + p_file + "'."); uint64_t mt = fa->_get_modified_time(p_file); memdelete(fa); @@ -511,7 +511,7 @@ uint32_t FileAccess::get_unix_permissions(const String &p_file) { return 0; FileAccess *fa = create_for_path(p_file); - ERR_FAIL_COND_V(!fa, 0); + ERR_FAIL_COND_V_MSG(!fa, 0, "Cannot create FileAccess for path '" + p_file + "'."); uint32_t mt = fa->_get_unix_permissions(p_file); memdelete(fa); @@ -521,7 +521,7 @@ uint32_t FileAccess::get_unix_permissions(const String &p_file) { Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissions) { FileAccess *fa = create_for_path(p_file); - ERR_FAIL_COND_V(!fa, ERR_CANT_CREATE); + ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'."); Error err = fa->_set_unix_permissions(p_file, p_permissions); memdelete(fa); @@ -599,7 +599,7 @@ Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_err if (r_error) { // if error requested, do not throw error return Vector<uint8_t>(); } - ERR_FAIL_V_MSG(Vector<uint8_t>(), "Can't open file from path: " + String(p_path) + "."); + ERR_FAIL_V_MSG(Vector<uint8_t>(), "Can't open file from path '" + String(p_path) + "'."); } Vector<uint8_t> data; data.resize(f->get_len()); @@ -619,7 +619,7 @@ String FileAccess::get_file_as_string(const String &p_path, Error *r_error) { if (r_error) { return String(); } - ERR_FAIL_V_MSG(String(), "Can't get file as string from path: " + String(p_path) + "."); + ERR_FAIL_V_MSG(String(), "Can't get file as string from path '" + String(p_path) + "'."); } String ret; diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp index eca3b2a7f4..5587e827ba 100644 --- a/core/os/main_loop.cpp +++ b/core/os/main_loop.cpp @@ -63,6 +63,8 @@ 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); }; void MainLoop::set_init_script(const Ref<Script> &p_init_script) { diff --git a/core/os/main_loop.h b/core/os/main_loop.h index 54e61fd2fa..aca920efcb 100644 --- a/core/os/main_loop.h +++ b/core/os/main_loop.h @@ -60,6 +60,8 @@ public: NOTIFICATION_WM_ABOUT = 1011, NOTIFICATION_CRASH = 1012, NOTIFICATION_OS_IME_UPDATE = 1013, + NOTIFICATION_APP_RESUMED = 1014, + NOTIFICATION_APP_PAUSED = 1015, }; virtual void input_event(const Ref<InputEvent> &p_event); diff --git a/core/os/os.cpp b/core/os/os.cpp index 7531900480..b44487b908 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -722,7 +722,7 @@ int OS::get_audio_driver_count() const { const char *OS::get_audio_driver_name(int p_driver) const { AudioDriver *driver = AudioDriverManager::get_driver(p_driver); - ERR_FAIL_COND_V(!driver, ""); + ERR_FAIL_COND_V_MSG(!driver, "", "Cannot get audio driver at index '" + itos(p_driver) + "'."); return AudioDriverManager::get_driver(p_driver)->get_name(); } diff --git a/core/packed_data_container.cpp b/core/packed_data_container.cpp index 54bf12b314..003e7e7428 100644 --- a/core/packed_data_container.cpp +++ b/core/packed_data_container.cpp @@ -119,7 +119,7 @@ Variant PackedDataContainer::_get_at_ofs(uint32_t p_ofs, const uint8_t *p_buf, b if (rerr != OK) { err = true; - ERR_FAIL_COND_V(err != OK, Variant()); + ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to decode Variant."); } return v; } diff --git a/core/pool_vector.h b/core/pool_vector.h index 957a72483c..fbd4d630be 100644 --- a/core/pool_vector.h +++ b/core/pool_vector.h @@ -508,7 +508,7 @@ T PoolVector<T>::operator[](int p_index) const { template <class T> Error PoolVector<T>::resize(int p_size) { - ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(p_size < 0, ERR_INVALID_PARAMETER, "Size of PoolVector cannot be negative."); if (alloc == NULL) { @@ -536,7 +536,7 @@ Error PoolVector<T>::resize(int p_size) { } else { - ERR_FAIL_COND_V(alloc->lock > 0, ERR_LOCKED); //can't resize if locked! + ERR_FAIL_COND_V_MSG(alloc->lock > 0, ERR_LOCKED, "Can't resize PoolVector if locked."); //can't resize if locked! } size_t new_size = sizeof(T) * p_size; diff --git a/core/project_settings.cpp b/core/project_settings.cpp index ec2c5ecbb3..c2241ed926 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -113,12 +113,12 @@ String ProjectSettings::localize_path(const String &p_path) const { void ProjectSettings::set_initial_value(const String &p_name, const Variant &p_value) { - ERR_FAIL_COND(!props.has(p_name)); + ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); props[p_name].initial = p_value; } void ProjectSettings::set_restart_if_changed(const String &p_name, bool p_restart) { - ERR_FAIL_COND(!props.has(p_name)); + ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); props[p_name].restart_if_changed = p_restart; } @@ -264,12 +264,12 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const { } } -bool ProjectSettings::_load_resource_pack(const String &p_pack) { +bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_files) { if (PackedData::get_singleton()->is_disabled()) return false; - bool ok = PackedData::get_singleton()->add_pack(p_pack) == OK; + bool ok = PackedData::get_singleton()->add_pack(p_pack, p_replace_files) == OK; if (!ok) return false; @@ -336,7 +336,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b if (p_main_pack != "") { bool ok = _load_resource_pack(p_main_pack); - ERR_FAIL_COND_V(!ok, ERR_CANT_OPEN); + ERR_FAIL_COND_V_MSG(!ok, ERR_CANT_OPEN, "Cannot open resource pack '" + p_main_pack + "'."); Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); if (err == OK) { @@ -421,7 +421,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b // or, if requested (`p_upwards`) in parent directories. DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - ERR_FAIL_COND_V(!d, ERR_CANT_CREATE); + ERR_FAIL_COND_V_MSG(!d, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_path + "'."); d->change_dir(p_path); String current_dir = d->get_current_dir(); @@ -609,19 +609,19 @@ Error ProjectSettings::_load_settings_text_or_binary(const String &p_text_path, int ProjectSettings::get_order(const String &p_name) const { - ERR_FAIL_COND_V(!props.has(p_name), -1); + ERR_FAIL_COND_V_MSG(!props.has(p_name), -1, "Request for nonexistent project setting: " + p_name + "."); return props[p_name].order; } void ProjectSettings::set_order(const String &p_name, int p_order) { - ERR_FAIL_COND(!props.has(p_name)); + ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); props[p_name].order = p_order; } void ProjectSettings::set_builtin_order(const String &p_name) { - ERR_FAIL_COND(!props.has(p_name)); + ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); if (props[p_name].order >= NO_BUILTIN_ORDER_BASE) { props[p_name].order = last_builtin_order++; } @@ -629,7 +629,7 @@ void ProjectSettings::set_builtin_order(const String &p_name) { void ProjectSettings::clear(const String &p_name) { - ERR_FAIL_COND(!props.has(p_name)); + ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); props.erase(p_name); } @@ -706,7 +706,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str err = encode_variant(value, NULL, len, true); if (err != OK) memdelete(file); - ERR_FAIL_COND_V(err != OK, ERR_INVALID_DATA); + ERR_FAIL_COND_V_MSG(err != OK, ERR_INVALID_DATA, "Error when trying to encode Variant."); Vector<uint8_t> buff; buff.resize(len); @@ -714,7 +714,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str err = encode_variant(value, buff.ptrw(), len, true); if (err != OK) memdelete(file); - ERR_FAIL_COND_V(err != OK, ERR_INVALID_DATA); + ERR_FAIL_COND_V_MSG(err != OK, ERR_INVALID_DATA, "Error when trying to encode Variant."); file->store_32(len); file->store_buffer(buff.ptr(), buff.size()); } @@ -787,7 +787,7 @@ Error ProjectSettings::_save_custom_bnd(const String &p_file) { // add other par Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_custom, const Vector<String> &p_custom_features, bool p_merge_with_current) { - ERR_FAIL_COND_V(p_path == "", ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(p_path == "", ERR_INVALID_PARAMETER, "Project settings save path cannot be empty."); Set<_VCSort> vclist; @@ -979,7 +979,7 @@ void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("localize_path", "path"), &ProjectSettings::localize_path); ClassDB::bind_method(D_METHOD("globalize_path", "path"), &ProjectSettings::globalize_path); ClassDB::bind_method(D_METHOD("save"), &ProjectSettings::save); - ClassDB::bind_method(D_METHOD("load_resource_pack", "pack"), &ProjectSettings::_load_resource_pack); + ClassDB::bind_method(D_METHOD("load_resource_pack", "pack", "replace_files"), &ProjectSettings::_load_resource_pack, DEFVAL(true)); ClassDB::bind_method(D_METHOD("property_can_revert", "name"), &ProjectSettings::property_can_revert); ClassDB::bind_method(D_METHOD("property_get_revert", "name"), &ProjectSettings::property_get_revert); diff --git a/core/project_settings.h b/core/project_settings.h index a8deab028c..b32470361b 100644 --- a/core/project_settings.h +++ b/core/project_settings.h @@ -104,7 +104,7 @@ protected: void _convert_to_last_version(int p_from_version); - bool _load_resource_pack(const String &p_pack); + bool _load_resource_pack(const String &p_pack, bool p_replace_files = true); void _add_property_info_bind(const Dictionary &p_info); diff --git a/core/reference.cpp b/core/reference.cpp index 1984af9a34..92bbdacd5d 100644 --- a/core/reference.cpp +++ b/core/reference.cpp @@ -36,12 +36,7 @@ bool Reference::init_ref() { if (reference()) { - // this may fail in the scenario of two threads assigning the pointer for the FIRST TIME - // at the same time, which is never likely to happen (would be crazy to do) - // so don't do it. - - if (refcount_init.get() > 0) { - refcount_init.unref(); + if (!is_referenced() && refcount_init.unref()) { unreference(); // first referencing is already 1, so compensate for the ref above } @@ -64,9 +59,11 @@ int Reference::reference_get_count() const { } bool Reference::reference() { - bool success = refcount.ref(); - if (success && refcount.get() <= 2 /* higher is not relevant */) { + uint32_t rc_val = refcount.refval(); + bool success = rc_val != 0; + + if (success && rc_val <= 2 /* higher is not relevant */) { if (get_script_instance()) { get_script_instance()->refcount_incremented(); } @@ -84,9 +81,10 @@ bool Reference::reference() { bool Reference::unreference() { - bool die = refcount.unref(); + uint32_t rc_val = refcount.unrefval(); + bool die = rc_val == 0; - if (refcount.get() <= 1 /* higher is not relevant */) { + if (rc_val <= 1 /* higher is not relevant */) { if (get_script_instance()) { bool script_ret = get_script_instance()->refcount_decremented(); die = die && script_ret; diff --git a/core/reference.h b/core/reference.h index 20ee22ddfc..b8d00a94ad 100644 --- a/core/reference.h +++ b/core/reference.h @@ -47,7 +47,7 @@ protected: static void _bind_methods(); public: - _FORCE_INLINE_ bool is_referenced() const { return refcount_init.get() < 1; } + _FORCE_INLINE_ bool is_referenced() const { return refcount_init.get() != 1; } bool init_ref(); bool reference(); // returns false if refcount is at zero and didn't get increased bool unreference(); diff --git a/core/resource.cpp b/core/resource.cpp index 5a5efa4644..87c92ca5b1 100644 --- a/core/resource.cpp +++ b/core/resource.cpp @@ -75,7 +75,7 @@ void Resource::set_path(const String &p_path, bool p_take_over) { bool exists = ResourceCache::resources.has(p_path); ResourceCache::lock->read_unlock(); - ERR_FAIL_COND_MSG(exists, "Another resource is loaded from path: " + p_path + " (possible cyclic resource inclusion)."); + ERR_FAIL_COND_MSG(exists, "Another resource is loaded from path '" + p_path + "' (possible cyclic resource inclusion)."); } } path_cache = p_path; @@ -509,7 +509,7 @@ void ResourceCache::dump(const char *p_file, bool p_short) { FileAccess *f = NULL; if (p_file) { f = FileAccess::open(p_file, FileAccess::WRITE); - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "Cannot create file at path '" + String(p_file) + "'."); } const String *K = NULL; diff --git a/core/safe_refcount.h b/core/safe_refcount.h index 54f540b0c7..47161eed57 100644 --- a/core/safe_refcount.h +++ b/core/safe_refcount.h @@ -177,12 +177,12 @@ struct SafeRefCount { public: // destroy() is called when weak_count_ drops to zero. - _ALWAYS_INLINE_ bool ref() { //true on success + _ALWAYS_INLINE_ bool ref() { // true on success return atomic_conditional_increment(&count) != 0; } - _ALWAYS_INLINE_ uint32_t refval() { //true on success + _ALWAYS_INLINE_ uint32_t refval() { // none-zero on success return atomic_conditional_increment(&count); } @@ -192,6 +192,11 @@ public: return atomic_decrement(&count) == 0; } + _ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of + + return atomic_decrement(&count); + } + _ALWAYS_INLINE_ uint32_t get() const { // nothrow return count; diff --git a/core/script_language.cpp b/core/script_language.cpp index ee8589d76a..7201773ea5 100644 --- a/core/script_language.cpp +++ b/core/script_language.cpp @@ -114,7 +114,7 @@ void Script::_bind_methods() { ClassDB::bind_method(D_METHOD("get_script_method_list"), &Script::_get_script_method_list); ClassDB::bind_method(D_METHOD("get_script_signal_list"), &Script::_get_script_signal_list); ClassDB::bind_method(D_METHOD("get_script_constant_map"), &Script::_get_script_constant_map); - ClassDB::bind_method(D_METHOD("get_property_default_value"), &Script::_get_property_default_value); + ClassDB::bind_method(D_METHOD("get_property_default_value", "property"), &Script::_get_property_default_value); ClassDB::bind_method(D_METHOD("is_tool"), &Script::is_tool); diff --git a/core/ustring.cpp b/core/ustring.cpp index fb4bd6d802..07caa3a018 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -2147,13 +2147,13 @@ int64_t String::to_int(const CharType *p_str, int p_len) { if (c >= '0' && c <= '9') { - if (integer > INT32_MAX / 10) { + if (integer > INT64_MAX / 10) { String number(""); str = p_str; while (*str && str != limit) { number += *(str++); } - ERR_FAIL_V_MSG(sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + number + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + ERR_FAIL_V_MSG(sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + number + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); } integer *= 10; integer += c - '0'; @@ -3049,6 +3049,22 @@ String String::replacen(const String &p_key, const String &p_with) const { return new_string; } +String String::repeat(int p_count) const { + + ERR_FAIL_COND_V_MSG(p_count < 0, "", "Parameter count should be a positive number."); + + String new_string; + const CharType *src = this->c_str(); + + new_string.resize(length() * p_count + 1); + + for (int i = 0; i < p_count; i++) + for (int j = 0; j < length(); j++) + new_string[i * length() + j] = src[j]; + + return new_string; +} + String String::left(int p_pos) const { if (p_pos <= 0) @@ -3285,18 +3301,26 @@ static int _humanize_digits(int p_num) { String String::humanize_size(size_t p_size) { uint64_t _div = 1; - static const char *prefix[] = { " B", " KiB", " MiB", " GiB", " TiB", " PiB", " EiB", "" }; + Vector<String> prefixes; + prefixes.push_back(RTR("B")); + prefixes.push_back(RTR("KiB")); + prefixes.push_back(RTR("MiB")); + prefixes.push_back(RTR("GiB")); + prefixes.push_back(RTR("TiB")); + prefixes.push_back(RTR("PiB")); + prefixes.push_back(RTR("EiB")); + int prefix_idx = 0; - while (p_size > (_div * 1024) && prefix[prefix_idx][0]) { + while (prefix_idx < prefixes.size() && p_size > (_div * 1024)) { _div *= 1024; prefix_idx++; } - int digits = prefix_idx > 0 ? _humanize_digits(p_size / _div) : 0; - double divisor = prefix_idx > 0 ? _div : 1; + const int digits = prefix_idx > 0 ? _humanize_digits(p_size / _div) : 0; + const double divisor = prefix_idx > 0 ? _div : 1; - return String::num(p_size / divisor).pad_decimals(digits) + RTR(prefix[prefix_idx]); + return String::num(p_size / divisor).pad_decimals(digits) + " " + prefixes[prefix_idx]; } bool String::is_abs_path() const { diff --git a/core/ustring.h b/core/ustring.h index bbd0bcceb5..87a14bfad7 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -223,6 +223,7 @@ public: String replace(const String &p_key, const String &p_with) const; String replace(const char *p_key, const char *p_with) const; String replacen(const String &p_key, const String &p_with) const; + String repeat(int p_count) const; String insert(int p_at_pos, const String &p_string) const; String pad_decimals(int p_digits) const; String pad_zeros(int p_digits) const; diff --git a/core/variant.cpp b/core/variant.cpp index e7d0e58367..16bbf94c54 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -910,7 +910,15 @@ bool Variant::is_one() const { void Variant::reference(const Variant &p_variant) { - clear(); + switch (type) { + case NIL: + case BOOL: + case INT: + case REAL: + break; + default: + clear(); + } type = p_variant.type; diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 5e3876d6a4..53f64fcde6 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -256,6 +256,7 @@ struct _VariantCall { VCALL_LOCALMEM2R(String, format); VCALL_LOCALMEM2R(String, replace); VCALL_LOCALMEM2R(String, replacen); + VCALL_LOCALMEM1R(String, repeat); VCALL_LOCALMEM2R(String, insert); VCALL_LOCALMEM0R(String, capitalize); VCALL_LOCALMEM3R(String, split); @@ -534,6 +535,7 @@ struct _VariantCall { VCALL_LOCALMEM2R(Array, bsearch); VCALL_LOCALMEM4R(Array, bsearch_custom); VCALL_LOCALMEM1R(Array, duplicate); + VCALL_LOCALMEM4R(Array, slice); VCALL_LOCALMEM0(Array, invert); VCALL_LOCALMEM0R(Array, max); VCALL_LOCALMEM0R(Array, min); @@ -1529,6 +1531,7 @@ void register_variant_methods() { ADDFUNC2R(STRING, STRING, String, format, NIL, "values", STRING, "placeholder", varray("{_}")); ADDFUNC2R(STRING, STRING, String, replace, STRING, "what", STRING, "forwhat", varray()); ADDFUNC2R(STRING, STRING, String, replacen, STRING, "what", STRING, "forwhat", varray()); + ADDFUNC1R(STRING, STRING, String, repeat, INT, "count", varray()); ADDFUNC2R(STRING, STRING, String, insert, INT, "position", STRING, "what", varray()); ADDFUNC0R(STRING, STRING, String, capitalize, varray()); ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, split, STRING, "delimiter", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0)); @@ -1759,6 +1762,7 @@ void register_variant_methods() { ADDFUNC4R(ARRAY, INT, Array, bsearch_custom, NIL, "value", OBJECT, "obj", STRING, "func", BOOL, "before", varray(true)); ADDFUNC0NC(ARRAY, NIL, Array, invert, varray()); ADDFUNC1R(ARRAY, ARRAY, Array, duplicate, BOOL, "deep", varray(false)); + ADDFUNC4R(ARRAY, ARRAY, Array, slice, INT, "begin", INT, "end", INT, "step", BOOL, "deep", varray(1, false)); ADDFUNC0R(ARRAY, NIL, Array, max, varray()); ADDFUNC0R(ARRAY, NIL, Array, min, varray()); diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp index 07212ec669..fe2c981c3c 100644 --- a/core/variant_parser.cpp +++ b/core/variant_parser.cpp @@ -1522,7 +1522,7 @@ Error VariantParser::parse_tag_assign_eof(Stream *p_stream, int &line, String &r return err; if (tk.type != TK_STRING) { r_err_str = "Error reading quoted string"; - return err; + return ERR_INVALID_DATA; } what = tk.value; |