diff options
Diffstat (limited to 'core')
142 files changed, 3288 insertions, 1431 deletions
diff --git a/core/SCsub b/core/SCsub index c8267ae960..8bda230b87 100644 --- a/core/SCsub +++ b/core/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/config/SCsub b/core/config/SCsub index bf70285490..1417a258c1 100644 --- a/core/config/SCsub +++ b/core/config/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/config/engine.cpp b/core/config/engine.cpp index d77c913314..aac048e93f 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -36,6 +36,7 @@ #include "core/license.gen.h" #include "core/variant/typed_array.h" #include "core/version.h" +#include "servers/rendering/rendering_device.h" void Engine::set_physics_ticks_per_second(int p_ips) { ERR_FAIL_COND_MSG(p_ips <= 0, "Engine iterations per second must be greater than 0."); @@ -68,6 +69,11 @@ double Engine::get_physics_jitter_fix() const { void Engine::set_max_fps(int p_fps) { _max_fps = p_fps > 0 ? p_fps : 0; + + RenderingDevice *rd = RenderingDevice::get_singleton(); + if (rd) { + rd->_set_max_fps(_max_fps); + } } int Engine::get_max_fps() const { @@ -110,6 +116,10 @@ void Engine::set_time_scale(double p_scale) { } double Engine::get_time_scale() const { + return freeze_time_scale ? 0 : _time_scale; +} + +double Engine::get_unfrozen_time_scale() const { return _time_scale; } @@ -267,6 +277,12 @@ bool Engine::is_extra_gpu_memory_tracking_enabled() const { return extra_gpu_memory_tracking; } +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) +bool Engine::is_accurate_breadcrumbs_enabled() const { + return accurate_breadcrumbs; +} +#endif + void Engine::set_print_to_stdout(bool p_enabled) { CoreGlobals::print_line_enabled = p_enabled; } @@ -392,6 +408,10 @@ bool Engine::notify_frame_server_synced() { return server_syncs > SERVER_SYNC_FRAME_COUNT_WARNING; } +void Engine::set_freeze_time_scale(bool p_frozen) { + freeze_time_scale = p_frozen; +} + Engine::Engine() { singleton = this; } diff --git a/core/config/engine.h b/core/config/engine.h index a0b1ffa981..b38412308a 100644 --- a/core/config/engine.h +++ b/core/config/engine.h @@ -73,6 +73,9 @@ private: bool use_validation_layers = false; bool generate_spirv_debug_info = false; bool extra_gpu_memory_tracking = false; +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) + bool accurate_breadcrumbs = false; +#endif int32_t gpu_idx = -1; uint64_t _process_frames = 0; @@ -96,6 +99,8 @@ private: int server_syncs = 0; bool frame_server_synced = false; + bool freeze_time_scale = false; + public: static Engine *get_singleton(); @@ -127,6 +132,7 @@ public: void set_time_scale(double p_scale); double get_time_scale() const; + double get_unfrozen_time_scale() const; void set_print_to_stdout(bool p_enabled); bool is_printing_to_stdout() const; @@ -186,11 +192,16 @@ public: bool is_validation_layers_enabled() const; bool is_generate_spirv_debug_info_enabled() const; bool is_extra_gpu_memory_tracking_enabled() const; +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) + bool is_accurate_breadcrumbs_enabled() const; +#endif int32_t get_gpu_index() const; void increment_frames_drawn(); bool notify_frame_server_synced(); + void set_freeze_time_scale(bool p_frozen); + Engine(); virtual ~Engine(); }; diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 7951ee9edd..092177bc15 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -214,36 +214,36 @@ 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_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); + ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name)); // Duplicate so that if value is array or dictionary, changing the setting will not change the stored initial value. props[p_name].initial = p_value.duplicate(); } void ProjectSettings::set_restart_if_changed(const String &p_name, bool p_restart) { - ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); + ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name)); props[p_name].restart_if_changed = p_restart; } void ProjectSettings::set_as_basic(const String &p_name, bool p_basic) { - ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); + ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name)); props[p_name].basic = p_basic; } void ProjectSettings::set_as_internal(const String &p_name, bool p_internal) { - ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); + ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name)); props[p_name].internal = p_internal; } void ProjectSettings::set_ignore_value_in_docs(const String &p_name, bool p_ignore) { - ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); + ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name)); #ifdef DEBUG_METHODS_ENABLED props[p_name].ignore_value_in_docs = p_ignore; #endif } bool ProjectSettings::get_ignore_value_in_docs(const String &p_name) const { - ERR_FAIL_COND_V_MSG(!props.has(p_name), false, "Request for nonexistent project setting: " + p_name + "."); + ERR_FAIL_COND_V_MSG(!props.has(p_name), false, vformat("Request for nonexistent project setting: '%s'.", p_name)); #ifdef DEBUG_METHODS_ENABLED return props[p_name].ignore_value_in_docs; #else @@ -348,7 +348,6 @@ bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const { _THREAD_SAFE_METHOD_ if (!props.has(p_name)) { - WARN_PRINT("Property not found: " + String(p_name)); return false; } r_ret = props[p_name].variant; @@ -372,7 +371,7 @@ Variant ProjectSettings::get_setting_with_override(const StringName &p_name) con } if (!props.has(name)) { - WARN_PRINT("Property not found: " + String(name)); + WARN_PRINT(vformat("Property not found: '%s'.", String(name))); return Variant(); } return props[name].variant; @@ -495,6 +494,7 @@ bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_f } void ProjectSettings::_convert_to_last_version(int p_from_version) { +#ifndef DISABLE_DEPRECATED if (p_from_version <= 3) { // Converts the actions from array to dictionary (array of events to dictionary with deadzone + events) for (KeyValue<StringName, ProjectSettings::VariantContainer> &E : props) { @@ -508,6 +508,22 @@ void ProjectSettings::_convert_to_last_version(int p_from_version) { } } } + if (p_from_version <= 5) { + // Converts the device in events from -1 (emulated events) to -3 (all events). + for (KeyValue<StringName, ProjectSettings::VariantContainer> &E : props) { + if (String(E.key).begins_with("input/")) { + Dictionary action = E.value.variant; + Array events = action["events"]; + for (int i = 0; i < events.size(); i++) { + Ref<InputEvent> ev = events[i]; + if (ev.is_valid() && ev->get_device() == -1) { // -1 was the previous value (GH-97707). + ev->set_device(InputEvent::DEVICE_ID_ALL_DEVICES); + } + } + } + } + } +#endif // DISABLE_DEPRECATED } /* @@ -549,7 +565,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b if (!p_main_pack.is_empty()) { bool ok = _load_resource_pack(p_main_pack); - ERR_FAIL_COND_V_MSG(!ok, ERR_CANT_OPEN, "Cannot open resource pack '" + p_main_pack + "'."); + ERR_FAIL_COND_V_MSG(!ok, ERR_CANT_OPEN, vformat("Cannot open resource pack '%s'.", p_main_pack)); Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); if (err == OK && !p_ignore_override) { @@ -628,7 +644,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b // or, if requested (`p_upwards`) in parent directories. Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - ERR_FAIL_COND_V_MSG(d.is_null(), ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(d.is_null(), ERR_CANT_CREATE, vformat("Cannot create DirAccess for path '%s'.", p_path)); d->change_dir(p_path); String current_dir = d->get_current_dir(); @@ -732,7 +748,7 @@ Error ProjectSettings::_load_settings_binary(const String &p_path) { f->get_buffer(d.ptrw(), vlen); Variant value; err = decode_variant(value, d.ptr(), d.size(), nullptr, true); - ERR_CONTINUE_MSG(err != OK, "Error decoding property: " + key + "."); + ERR_CONTINUE_MSG(err != OK, vformat("Error decoding property: '%s'.", key)); set(key, value); } @@ -774,7 +790,7 @@ Error ProjectSettings::_load_settings_text(const String &p_path) { last_save_time = FileAccess::get_modified_time(get_resource_path().path_join("project.godot")); return OK; } - ERR_FAIL_COND_V_MSG(err != OK, err, "Error parsing " + p_path + " at line " + itos(lines) + ": " + error_text + " File might be corrupted."); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error parsing '%s' at line %d: %s File might be corrupted.", p_path, lines, error_text)); if (!assign.is_empty()) { if (section.is_empty() && assign == "config_version") { @@ -800,7 +816,7 @@ Error ProjectSettings::_load_settings_text_or_binary(const String &p_text_path, return OK; } else if (err != ERR_FILE_NOT_FOUND) { // If the file exists but can't be loaded, we want to know it. - ERR_PRINT("Couldn't load file '" + p_bin_path + "', error code " + itos(err) + "."); + ERR_PRINT(vformat("Couldn't load file '%s', error code %d.", p_bin_path, err)); } // Fallback to text-based project.godot file if binary was not found. @@ -808,7 +824,7 @@ Error ProjectSettings::_load_settings_text_or_binary(const String &p_text_path, if (err == OK) { return OK; } else if (err != ERR_FILE_NOT_FOUND) { - ERR_PRINT("Couldn't load file '" + p_text_path + "', error code " + itos(err) + "."); + ERR_PRINT(vformat("Couldn't load file '%s', error code %d.", p_text_path, err)); } return err; @@ -822,17 +838,17 @@ Error ProjectSettings::load_custom(const String &p_path) { } int ProjectSettings::get_order(const String &p_name) const { - ERR_FAIL_COND_V_MSG(!props.has(p_name), -1, "Request for nonexistent project setting: " + p_name + "."); + ERR_FAIL_COND_V_MSG(!props.has(p_name), -1, vformat("Request for nonexistent project setting: '%s'.", p_name)); return props[p_name].order; } void ProjectSettings::set_order(const String &p_name, int p_order) { - ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); + ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name)); props[p_name].order = p_order; } void ProjectSettings::set_builtin_order(const String &p_name) { - ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); + ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name)); if (props[p_name].order >= NO_BUILTIN_ORDER_BASE) { props[p_name].order = last_builtin_order++; } @@ -840,12 +856,12 @@ void ProjectSettings::set_builtin_order(const String &p_name) { bool ProjectSettings::is_builtin_setting(const String &p_name) const { // Return true because a false negative is worse than a false positive. - ERR_FAIL_COND_V_MSG(!props.has(p_name), true, "Request for nonexistent project setting: " + p_name + "."); + ERR_FAIL_COND_V_MSG(!props.has(p_name), true, vformat("Request for nonexistent project setting: '%s'.", p_name)); return props[p_name].order < NO_BUILTIN_ORDER_BASE; } void ProjectSettings::clear(const String &p_name) { - ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); + ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name)); props.erase(p_name); } @@ -860,7 +876,7 @@ Error ProjectSettings::save() { Error ProjectSettings::_save_settings_binary(const String &p_file, const RBMap<String, List<String>> &p_props, const CustomMap &p_custom, const String &p_custom_features) { Error err; Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err); - ERR_FAIL_COND_V_MSG(err != OK, err, "Couldn't save project.binary at " + p_file + "."); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Couldn't save project.binary at '%s'.", p_file)); uint8_t hdr[4] = { 'E', 'C', 'F', 'G' }; file->store_buffer(hdr, 4); @@ -930,7 +946,7 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const RBMap<Str Error err; Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err); - ERR_FAIL_COND_V_MSG(err != OK, err, "Couldn't save project.godot - " + p_file + "."); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Couldn't save project.godot - %s.", p_file)); file->store_line("; Engine configuration file."); file->store_line("; It's best edited using the editor UI and not directly,"); @@ -1103,7 +1119,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust } else if (p_path.ends_with(".binary")) { return _save_settings_binary(p_path, save_props, p_custom, save_features); } else { - ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unknown config file format: " + p_path); + ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, vformat("Unknown config file format: '%s'.", p_path)); } } @@ -1168,22 +1184,16 @@ bool ProjectSettings::is_project_loaded() const { } bool ProjectSettings::_property_can_revert(const StringName &p_name) const { - if (!props.has(p_name)) { - return false; - } - - return props[p_name].initial != props[p_name].variant; + return props.has(p_name); } bool ProjectSettings::_property_get_revert(const StringName &p_name, Variant &r_property) const { - if (!props.has(p_name)) { - return false; + const RBMap<StringName, ProjectSettings::VariantContainer>::Element *value = props.find(p_name); + if (value) { + r_property = value->value().initial.duplicate(); + return true; } - - // Duplicate so that if value is array or dictionary, changing the setting will not change the stored initial value. - r_property = props[p_name].initial.duplicate(); - - return true; + return false; } void ProjectSettings::set_setting(const String &p_setting, const Variant &p_value) { @@ -1398,7 +1408,7 @@ void ProjectSettings::_add_builtin_input_map() { } Dictionary action; - action["deadzone"] = Variant(0.5f); + action["deadzone"] = Variant(0.2f); action["events"] = events; String action_name = "input/" + E.key; @@ -1467,6 +1477,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("display/window/size/transparent", false); GLOBAL_DEF("display/window/size/extend_to_title", false); GLOBAL_DEF("display/window/size/no_focus", false); + GLOBAL_DEF("display/window/size/sharp_corners", false); GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_width_override", PROPERTY_HINT_RANGE, "0,7680,1,or_greater"), 0); // 8K resolution GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"), 0); // 8K resolution @@ -1492,6 +1503,10 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("display/window/subwindows/embed_subwindows", true); // Keep the enum values in sync with the `DisplayServer::VSyncMode` enum. custom_prop_info["display/window/vsync/vsync_mode"] = PropertyInfo(Variant::INT, "display/window/vsync/vsync_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Adaptive,Mailbox"); + + GLOBAL_DEF("display/window/frame_pacing/android/enable_frame_pacing", true); + GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/frame_pacing/android/swappy_mode", PROPERTY_HINT_ENUM, "pipeline_forced_on,auto_fps_pipeline_forced_on,auto_fps_auto_pipeline"), 2); + custom_prop_info["rendering/driver/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/driver/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded"); GLOBAL_DEF("physics/2d/run_on_separate_thread", false); GLOBAL_DEF("physics/3d/run_on_separate_thread", false); diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 891e3a28c9..6ef466bee0 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -75,7 +75,7 @@ Ref<Resource> ResourceLoader::load(const String &p_path, const String &p_type_hi Error err = OK; Ref<Resource> ret = ::ResourceLoader::load(p_path, p_type_hint, ResourceFormatLoader::CacheMode(p_cache_mode), &err); - ERR_FAIL_COND_V_MSG(err != OK, ret, "Error loading resource: '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err != OK, ret, vformat("Error loading resource: '%s'.", p_path)); return ret; } @@ -468,11 +468,11 @@ Error OS::set_thread_name(const String &p_name) { ::Thread::ID OS::get_thread_caller_id() const { return ::Thread::get_caller_id(); -}; +} ::Thread::ID OS::get_main_thread_id() const { return ::Thread::get_main_id(); -}; +} bool OS::has_feature(const String &p_feature) const { const bool *value_ptr = feature_cache.getptr(p_feature); @@ -920,6 +920,19 @@ Dictionary Geometry2D::make_atlas(const Vector<Size2> &p_rects) { return ret; } +TypedArray<Point2i> Geometry2D::bresenham_line(const Point2i &p_from, const Point2i &p_to) { + Vector<Point2i> points = ::Geometry2D::bresenham_line(p_from, p_to); + + TypedArray<Point2i> result; + result.resize(points.size()); + + for (int i = 0; i < points.size(); i++) { + result[i] = points[i]; + } + + return result; +} + void Geometry2D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_point_in_circle", "point", "circle_position", "circle_radius"), &Geometry2D::is_point_in_circle); ClassDB::bind_method(D_METHOD("segment_intersects_circle", "segment_from", "segment_to", "circle_position", "circle_radius"), &Geometry2D::segment_intersects_circle); @@ -954,6 +967,8 @@ void Geometry2D::_bind_methods() { ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &Geometry2D::make_atlas); + ClassDB::bind_method(D_METHOD("bresenham_line", "from", "to"), &Geometry2D::bresenham_line); + BIND_ENUM_CONSTANT(OPERATION_UNION); BIND_ENUM_CONSTANT(OPERATION_DIFFERENCE); BIND_ENUM_CONSTANT(OPERATION_INTERSECTION); @@ -1300,7 +1315,7 @@ void Thread::_start_func(void *ud) { } if (ce.error != Callable::CallError::CALL_OK) { - ERR_FAIL_MSG("Could not call function '" + func_name + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, nullptr, 0, ce) + "."); + ERR_FAIL_MSG(vformat("Could not call function '%s' to start thread %d: %s.", func_name, t->get_id(), Variant::get_callable_error_text(t->target_callable, nullptr, 0, ce))); } } @@ -1525,7 +1540,7 @@ TypedArray<Dictionary> ClassDB::class_get_method_list(const StringName &p_class, return ret; } -Variant ClassDB::class_call_static_method(const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) { +Variant ClassDB::class_call_static(const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) { if (p_argcount < 2) { r_call_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; return Variant::NIL; @@ -1666,7 +1681,7 @@ void ClassDB::_bind_methods() { ::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::class_get_method_list, DEFVAL(false)); - ::ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "class_call_static_method", &ClassDB::class_call_static_method, MethodInfo("class_call_static_method", PropertyInfo(Variant::STRING_NAME, "class"), PropertyInfo(Variant::STRING_NAME, "method"))); + ::ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "class_call_static", &ClassDB::class_call_static, MethodInfo("class_call_static", PropertyInfo(Variant::STRING_NAME, "class"), PropertyInfo(Variant::STRING_NAME, "method"))); ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &ClassDB::class_get_integer_constant_list, DEFVAL(false)); @@ -1799,8 +1814,8 @@ Object *Engine::get_singleton_object(const StringName &p_name) const { } void Engine::register_singleton(const StringName &p_name, Object *p_object) { - ERR_FAIL_COND_MSG(has_singleton(p_name), "Singleton already registered: " + String(p_name)); - ERR_FAIL_COND_MSG(!String(p_name).is_valid_ascii_identifier(), "Singleton name is not a valid identifier: " + p_name); + ERR_FAIL_COND_MSG(has_singleton(p_name), vformat("Singleton already registered: '%s'.", String(p_name))); + ERR_FAIL_COND_MSG(!String(p_name).is_valid_ascii_identifier(), vformat("Singleton name is not a valid identifier: '%s'.", p_name)); ::Engine::Singleton s; s.class_name = p_name; s.name = p_name; @@ -1810,8 +1825,8 @@ void Engine::register_singleton(const StringName &p_name, Object *p_object) { } void Engine::unregister_singleton(const StringName &p_name) { - ERR_FAIL_COND_MSG(!has_singleton(p_name), "Attempt to remove unregistered singleton: " + String(p_name)); - ERR_FAIL_COND_MSG(!::Engine::get_singleton()->is_singleton_user_created(p_name), "Attempt to remove non-user created singleton: " + String(p_name)); + ERR_FAIL_COND_MSG(!has_singleton(p_name), vformat("Attempt to remove unregistered singleton: '%s'.", String(p_name))); + ERR_FAIL_COND_MSG(!::Engine::get_singleton()->is_singleton_user_created(p_name), vformat("Attempt to remove non-user created singleton: '%s'.", String(p_name))); ::Engine::get_singleton()->remove_singleton(p_name); } @@ -1955,14 +1970,14 @@ bool EngineDebugger::is_active() { void EngineDebugger::register_profiler(const StringName &p_name, Ref<EngineProfiler> p_profiler) { ERR_FAIL_COND(p_profiler.is_null()); ERR_FAIL_COND_MSG(p_profiler->is_bound(), "Profiler already registered."); - ERR_FAIL_COND_MSG(profilers.has(p_name) || has_profiler(p_name), "Profiler name already in use: " + p_name); + ERR_FAIL_COND_MSG(profilers.has(p_name) || has_profiler(p_name), vformat("Profiler name already in use: '%s'.", p_name)); Error err = p_profiler->bind(p_name); - ERR_FAIL_COND_MSG(err != OK, "Profiler failed to register with error: " + itos(err)); + ERR_FAIL_COND_MSG(err != OK, vformat("Profiler failed to register with error: %d.", err)); profilers.insert(p_name, p_profiler); } void EngineDebugger::unregister_profiler(const StringName &p_name) { - ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + p_name); + ERR_FAIL_COND_MSG(!profilers.has(p_name), vformat("Profiler not registered: '%s'.", p_name)); profilers[p_name]->unbind(); profilers.erase(p_name); } @@ -1986,7 +2001,7 @@ void EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, c } void EngineDebugger::register_message_capture(const StringName &p_name, const Callable &p_callable) { - ERR_FAIL_COND_MSG(captures.has(p_name) || has_capture(p_name), "Capture already registered: " + p_name); + ERR_FAIL_COND_MSG(captures.has(p_name) || has_capture(p_name), vformat("Capture already registered: '%s'.", p_name)); captures.insert(p_name, p_callable); Callable &c = captures[p_name]; ::EngineDebugger::Capture capture(&c, &EngineDebugger::call_capture); @@ -1994,7 +2009,7 @@ void EngineDebugger::register_message_capture(const StringName &p_name, const Ca } void EngineDebugger::unregister_message_capture(const StringName &p_name) { - ERR_FAIL_COND_MSG(!captures.has(p_name), "Capture not registered: " + p_name); + ERR_FAIL_COND_MSG(!captures.has(p_name), vformat("Capture not registered: '%s'.", p_name)); ::EngineDebugger::unregister_message_capture(p_name); captures.erase(p_name); } @@ -2028,8 +2043,8 @@ Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Arra Variant retval; Callable::CallError err; capture.callp(args, 2, retval, err); - ERR_FAIL_COND_V_MSG(err.error != Callable::CallError::CALL_OK, FAILED, "Error calling 'capture' to callable: " + Variant::get_callable_error_text(capture, args, 2, err)); - ERR_FAIL_COND_V_MSG(retval.get_type() != Variant::BOOL, FAILED, "Error calling 'capture' to callable: " + String(capture) + ". Return type is not bool."); + ERR_FAIL_COND_V_MSG(err.error != Callable::CallError::CALL_OK, FAILED, vformat("Error calling 'capture' to callable: %s.", Variant::get_callable_error_text(capture, args, 2, err))); + ERR_FAIL_COND_V_MSG(retval.get_type() != Variant::BOOL, FAILED, vformat("Error calling 'capture' to callable: '%s'. Return type is not bool.", String(capture))); r_captured = retval; return OK; } diff --git a/core/core_bind.h b/core/core_bind.h index ce0bde3c05..430ecdc906 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -32,7 +32,6 @@ #define CORE_BIND_H #include "core/debugger/engine_profiler.h" -#include "core/io/image.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/object/script_language.h" @@ -325,6 +324,8 @@ public: Dictionary make_atlas(const Vector<Size2> &p_rects); + TypedArray<Point2i> bresenham_line(const Point2i &p_from, const Point2i &p_to); + Geometry2D() { singleton = this; } }; @@ -483,7 +484,7 @@ public: int class_get_method_argument_count(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false) const; TypedArray<Dictionary> class_get_method_list(const StringName &p_class, bool p_no_inheritance = false) const; - Variant class_call_static_method(const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error); + Variant class_call_static(const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error); PackedStringArray class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const; bool class_has_integer_constant(const StringName &p_class, const StringName &p_name) const; diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 68af5abf66..25da49fa5c 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -677,6 +677,7 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_TYPE); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_HIDE_QUATERNION_EDIT); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PASSWORD); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_TOOL_BUTTON); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NONE); diff --git a/core/crypto/SCsub b/core/crypto/SCsub index 8cff3cf679..3cea6bfb47 100644 --- a/core/crypto/SCsub +++ b/core/crypto/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/crypto/crypto.cpp b/core/crypto/crypto.cpp index 62bacadf91..30003e4ea8 100644 --- a/core/crypto/crypto.cpp +++ b/core/crypto/crypto.cpp @@ -240,7 +240,7 @@ Error ResourceFormatSaverCrypto::save(const Ref<Resource> &p_resource, const Str } else { ERR_FAIL_V(ERR_INVALID_PARAMETER); } - ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save Crypto resource to file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot save Crypto resource to file '%s'.", p_path)); return OK; } diff --git a/core/crypto/crypto_core.cpp b/core/crypto/crypto_core.cpp index 69a83284cc..13852d5177 100644 --- a/core/crypto/crypto_core.cpp +++ b/core/crypto/crypto_core.cpp @@ -70,7 +70,7 @@ int CryptoCore::RandomGenerator::_entropy_poll(void *p_data, unsigned char *r_bu Error CryptoCore::RandomGenerator::init() { int ret = mbedtls_ctr_drbg_seed((mbedtls_ctr_drbg_context *)ctx, mbedtls_entropy_func, (mbedtls_entropy_context *)entropy, nullptr, 0); if (ret) { - ERR_FAIL_COND_V_MSG(ret, FAILED, " failed\n ! mbedtls_ctr_drbg_seed returned an error" + itos(ret)); + ERR_FAIL_COND_V_MSG(ret, FAILED, vformat(" failed\n ! mbedtls_ctr_drbg_seed returned an error %d.", ret)); } return OK; } @@ -78,7 +78,7 @@ Error CryptoCore::RandomGenerator::init() { Error CryptoCore::RandomGenerator::get_random_bytes(uint8_t *r_buffer, size_t p_bytes) { ERR_FAIL_NULL_V(ctx, ERR_UNCONFIGURED); int ret = mbedtls_ctr_drbg_random((mbedtls_ctr_drbg_context *)ctx, r_buffer, p_bytes); - ERR_FAIL_COND_V_MSG(ret, FAILED, " failed\n ! mbedtls_ctr_drbg_seed returned an error" + itos(ret)); + ERR_FAIL_COND_V_MSG(ret, FAILED, vformat(" failed\n ! mbedtls_ctr_drbg_seed returned an error %d.", ret)); return OK; } diff --git a/core/debugger/SCsub b/core/debugger/SCsub index 19a6549225..ab81175894 100644 --- a/core/debugger/SCsub +++ b/core/debugger/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/debugger/debugger_marshalls.cpp b/core/debugger/debugger_marshalls.cpp index f4283e0ea9..cc36ca4816 100644 --- a/core/debugger/debugger_marshalls.cpp +++ b/core/debugger/debugger_marshalls.cpp @@ -147,3 +147,37 @@ bool DebuggerMarshalls::OutputError::deserialize(const Array &p_arr) { CHECK_END(p_arr, idx, "OutputError"); return true; } + +Array DebuggerMarshalls::serialize_key_shortcut(const Ref<Shortcut> &p_shortcut) { + ERR_FAIL_COND_V(p_shortcut.is_null(), Array()); + Array keys; + for (const Ref<InputEvent> ev : p_shortcut->get_events()) { + const Ref<InputEventKey> kev = ev; + ERR_CONTINUE(kev.is_null()); + if (kev->get_physical_keycode() != Key::NONE) { + keys.push_back(true); + keys.push_back(kev->get_physical_keycode_with_modifiers()); + } else { + keys.push_back(false); + keys.push_back(kev->get_keycode_with_modifiers()); + } + } + return keys; +} + +Ref<Shortcut> DebuggerMarshalls::deserialize_key_shortcut(const Array &p_keys) { + Array key_events; + ERR_FAIL_COND_V(p_keys.size() % 2 != 0, Ref<Shortcut>()); + for (int i = 0; i < p_keys.size(); i += 2) { + ERR_CONTINUE(p_keys[i].get_type() != Variant::BOOL); + ERR_CONTINUE(p_keys[i + 1].get_type() != Variant::INT); + key_events.push_back(InputEventKey::create_reference((Key)p_keys[i + 1].operator int(), p_keys[i].operator bool())); + } + if (key_events.is_empty()) { + return Ref<Shortcut>(); + } + Ref<Shortcut> shortcut; + shortcut.instantiate(); + shortcut->set_events(key_events); + return shortcut; +} diff --git a/core/debugger/debugger_marshalls.h b/core/debugger/debugger_marshalls.h index 1b81623688..1072ddaeb7 100644 --- a/core/debugger/debugger_marshalls.h +++ b/core/debugger/debugger_marshalls.h @@ -31,6 +31,7 @@ #ifndef DEBUGGER_MARSHALLS_H #define DEBUGGER_MARSHALLS_H +#include "core/input/shortcut.h" #include "core/object/script_language.h" struct DebuggerMarshalls { @@ -68,6 +69,9 @@ struct DebuggerMarshalls { Array serialize(); bool deserialize(const Array &p_arr); }; + + static Array serialize_key_shortcut(const Ref<Shortcut> &p_shortcut); + static Ref<Shortcut> deserialize_key_shortcut(const Array &p_keys); }; #endif // DEBUGGER_MARSHALLS_H diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp index 97a020e4c3..6d2868cef2 100644 --- a/core/debugger/engine_debugger.cpp +++ b/core/debugger/engine_debugger.cpp @@ -46,12 +46,12 @@ HashMap<String, EngineDebugger::CreatePeerFunc> EngineDebugger::protocols; void (*EngineDebugger::allow_focus_steal_fn)(); void EngineDebugger::register_profiler(const StringName &p_name, const Profiler &p_func) { - ERR_FAIL_COND_MSG(profilers.has(p_name), "Profiler already registered: " + p_name); + ERR_FAIL_COND_MSG(profilers.has(p_name), vformat("Profiler already registered: '%s'.", p_name)); profilers.insert(p_name, p_func); } void EngineDebugger::unregister_profiler(const StringName &p_name) { - ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + p_name); + ERR_FAIL_COND_MSG(!profilers.has(p_name), vformat("Profiler not registered: '%s'.", p_name)); Profiler &p = profilers[p_name]; if (p.active && p.toggle) { p.toggle(p.data, false, Array()); @@ -61,22 +61,22 @@ void EngineDebugger::unregister_profiler(const StringName &p_name) { } void EngineDebugger::register_message_capture(const StringName &p_name, Capture p_func) { - ERR_FAIL_COND_MSG(captures.has(p_name), "Capture already registered: " + p_name); + ERR_FAIL_COND_MSG(captures.has(p_name), vformat("Capture already registered: '%s'.", p_name)); captures.insert(p_name, p_func); } void EngineDebugger::unregister_message_capture(const StringName &p_name) { - ERR_FAIL_COND_MSG(!captures.has(p_name), "Capture not registered: " + p_name); + ERR_FAIL_COND_MSG(!captures.has(p_name), vformat("Capture not registered: '%s'.", p_name)); captures.erase(p_name); } void EngineDebugger::register_uri_handler(const String &p_protocol, CreatePeerFunc p_func) { - ERR_FAIL_COND_MSG(protocols.has(p_protocol), "Protocol handler already registered: " + p_protocol); + ERR_FAIL_COND_MSG(protocols.has(p_protocol), vformat("Protocol handler already registered: '%s'.", p_protocol)); protocols.insert(p_protocol, p_func); } void EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts) { - ERR_FAIL_COND_MSG(!profilers.has(p_name), "Can't change profiler state, no profiler: " + p_name); + ERR_FAIL_COND_MSG(!profilers.has(p_name), vformat("Can't change profiler state, no profiler: '%s'.", p_name)); Profiler &p = profilers[p_name]; if (p.toggle) { p.toggle(p.data, p_enabled, p_opts); @@ -85,7 +85,7 @@ void EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, c } void EngineDebugger::profiler_add_frame_data(const StringName &p_name, const Array &p_data) { - ERR_FAIL_COND_MSG(!profilers.has(p_name), "Can't add frame data, no profiler: " + p_name); + ERR_FAIL_COND_MSG(!profilers.has(p_name), vformat("Can't add frame data, no profiler: '%s'.", p_name)); Profiler &p = profilers[p_name]; if (p.add) { p.add(p.data, p_data); @@ -106,7 +106,7 @@ bool EngineDebugger::has_capture(const StringName &p_name) { Error EngineDebugger::capture_parse(const StringName &p_name, const String &p_msg, const Array &p_args, bool &r_captured) { r_captured = false; - ERR_FAIL_COND_V_MSG(!captures.has(p_name), ERR_UNCONFIGURED, "Capture not registered: " + p_name); + ERR_FAIL_COND_V_MSG(!captures.has(p_name), ERR_UNCONFIGURED, vformat("Capture not registered: '%s'.", p_name)); const Capture &cap = captures[p_name]; return cap.capture(cap.data, p_msg, p_args, r_captured); } @@ -164,7 +164,7 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, co for (int i = 0; i < p_breakpoints.size(); i++) { const String &bp = p_breakpoints[i]; int sp = bp.rfind(":"); - ERR_CONTINUE_MSG(sp == -1, "Invalid breakpoint: '" + bp + "', expected file:line format."); + ERR_CONTINUE_MSG(sp == -1, vformat("Invalid breakpoint: '%s', expected file:line format.", bp)); singleton_script_debugger->insert_breakpoint(bp.substr(sp + 1, bp.length()).to_int(), bp.substr(0, sp)); } diff --git a/core/debugger/engine_debugger.h b/core/debugger/engine_debugger.h index 16050778aa..3c4ac87408 100644 --- a/core/debugger/engine_debugger.h +++ b/core/debugger/engine_debugger.h @@ -106,7 +106,7 @@ public: _FORCE_INLINE_ static EngineDebugger *get_singleton() { return singleton; } _FORCE_INLINE_ static bool is_active() { return singleton != nullptr && script_debugger != nullptr; } - _FORCE_INLINE_ static ScriptDebugger *get_script_debugger() { return script_debugger; }; + _FORCE_INLINE_ static ScriptDebugger *get_script_debugger() { return script_debugger; } static void initialize(const String &p_uri, bool p_skip_breakpoints, const Vector<String> &p_breakpoints, void (*p_allow_focus_steal_fn)()); static void deinitialize(); diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index e2ed7245a2..a02354b377 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -37,6 +37,7 @@ #include "core/debugger/script_debugger.h" #include "core/input/input.h" #include "core/io/resource_loader.h" +#include "core/math/expression.h" #include "core/object/script_language.h" #include "core/os/os.h" #include "servers/display_server.h" @@ -78,7 +79,7 @@ public: for (int i = 0; i < custom_monitor_names.size(); i++) { Variant monitor_value = performance->call("get_custom_monitor", custom_monitor_names[i]); if (!monitor_value.is_num()) { - ERR_PRINT("Value of custom monitor '" + String(custom_monitor_names[i]) + "' is not a number"); + ERR_PRINT(vformat("Value of custom monitor '%s' is not a number.", String(custom_monitor_names[i]))); arr[i + max] = Variant(); } else { arr[i + max] = monitor_value; @@ -529,11 +530,46 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } else if (command == "set_skip_breakpoints") { ERR_FAIL_COND(data.is_empty()); script_debugger->set_skip_breakpoints(data[0]); + } else if (command == "evaluate") { + String expression_str = data[0]; + int frame = data[1]; + + ScriptInstance *breaked_instance = script_debugger->get_break_language()->debug_get_stack_level_instance(frame); + if (!breaked_instance) { + break; + } + + List<String> locals; + List<Variant> local_vals; + + script_debugger->get_break_language()->debug_get_stack_level_locals(frame, &locals, &local_vals); + ERR_FAIL_COND(locals.size() != local_vals.size()); + + PackedStringArray locals_vector; + for (const String &S : locals) { + locals_vector.append(S); + } + + Array local_vals_array; + for (const Variant &V : local_vals) { + local_vals_array.append(V); + } + + Expression expression; + expression.parse(expression_str, locals_vector); + const Variant return_val = expression.execute(local_vals_array, breaked_instance->get_owner()); + + DebuggerMarshalls::ScriptStackVariable stvar; + stvar.name = expression_str; + stvar.value = return_val; + stvar.type = 3; + + send_message("evaluation_return", stvar.serialize()); } else { bool captured = false; ERR_CONTINUE(_try_capture(command, data, captured) != OK); if (!captured) { - WARN_PRINT("Unknown message received from debugger: " + command); + WARN_PRINT(vformat("Unknown message received from debugger: %s.", command)); } } } else { diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp index 9dca47a0b4..793db7330f 100644 --- a/core/debugger/remote_debugger_peer.cpp +++ b/core/debugger/remote_debugger_peer.cpp @@ -178,7 +178,7 @@ Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_po } if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) { - ERR_PRINT("Remote Debugger: Unable to connect. Status: " + String::num(tcp_client->get_status()) + "."); + ERR_PRINT(vformat("Remote Debugger: Unable to connect. Status: %s.", String::num(tcp_client->get_status()))); return FAILED; } connected = true; diff --git a/core/error/SCsub b/core/error/SCsub index dfd6248a94..08089d31b0 100644 --- a/core/error/SCsub +++ b/core/error/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/error/error_macros.cpp b/core/error/error_macros.cpp index 813ee7684f..a2369992e6 100644 --- a/core/error/error_macros.cpp +++ b/core/error/error_macros.cpp @@ -107,6 +107,28 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co _global_unlock(); } +// For printing errors when we may crash at any point, so we must flush ASAP a lot of lines +// but we don't want to make it noisy by printing lots of file & line info (because it's already +// been printing by a preceding _err_print_error). +void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type) { + if (OS::get_singleton()) { + OS::get_singleton()->printerr("ERROR: %s\n", p_error.utf8().get_data()); + } else { + // Fallback if errors happen before OS init or after it's destroyed. + const char *err_details = p_error.utf8().get_data(); + fprintf(stderr, "ERROR: %s\n", err_details); + } + + _global_lock(); + ErrorHandlerList *l = error_handler_list; + while (l) { + l->errfunc(l->userdata, "", "", 0, p_error.utf8().get_data(), "", false, p_type); + l = l->next; + } + + _global_unlock(); +} + // Errors with message. (All combinations of p_error and p_message as String or char*.) void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) { _err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message, p_editor_notify, p_type); diff --git a/core/error/error_macros.h b/core/error/error_macros.h index 19c16667d0..752fd605e0 100644 --- a/core/error/error_macros.h +++ b/core/error/error_macros.h @@ -68,6 +68,7 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR); void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool p_editor_notify = false, bool fatal = false); void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify = false, bool fatal = false); void _err_flush_stdout(); diff --git a/core/extension/SCsub b/core/extension/SCsub index 6ab2d2b0a6..8688ca5b6e 100644 --- a/core/extension/SCsub +++ b/core/extension/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index 4042d6b80d..7263cafdf3 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -1017,6 +1017,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { d2["name"] = String(method_name); d2["is_const"] = (F.flags & METHOD_FLAG_CONST) ? true : false; d2["is_static"] = (F.flags & METHOD_FLAG_STATIC) ? true : false; + d2["is_required"] = (F.flags & METHOD_FLAG_VIRTUAL_REQUIRED) ? true : false; d2["is_vararg"] = false; d2["is_virtual"] = true; // virtual functions have no hash since no MethodBind is involved @@ -1362,7 +1363,7 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_ return true; // May just not have this array and its still good. Probably added recently. } bool failed = false; - ERR_FAIL_COND_V_MSG(!p_new_api.has(p_base_array), false, "New API lacks base array: " + p_base_array); + ERR_FAIL_COND_V_MSG(!p_new_api.has(p_base_array), false, vformat("New API lacks base array: %s", p_base_array)); Array new_api = p_new_api[p_base_array]; HashMap<String, Dictionary> new_api_assoc; @@ -1370,6 +1371,9 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_ Dictionary elem = var; ERR_FAIL_COND_V_MSG(!elem.has(p_name_field), false, vformat("Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug.", base_array, p_name_field)); String name = elem[p_name_field]; + if (name.is_valid_float()) { + name = name.trim_suffix(".0"); // Make "integers" stringified as integers. + } if (p_compare_operators && elem.has("right_type")) { name += " " + String(elem["right_type"]); } @@ -1385,6 +1389,9 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_ continue; } String name = old_elem[p_name_field]; + if (name.is_valid_float()) { + name = name.trim_suffix(".0"); // Make "integers" stringified as integers. + } if (p_compare_operators && old_elem.has("right_type")) { name += " " + String(old_elem["right_type"]); } @@ -1514,7 +1521,7 @@ static bool compare_sub_dict_array(HashSet<String> &r_removed_classes_registered return true; // May just not have this array and its still good. Probably added recently or optional. } bool failed = false; - ERR_FAIL_COND_V_MSG(!p_new_api.has(p_outer), false, "New API lacks base array: " + p_outer); + ERR_FAIL_COND_V_MSG(!p_new_api.has(p_outer), false, vformat("New API lacks base array: %s", p_outer)); Array new_api = p_new_api[p_outer]; HashMap<String, Dictionary> new_api_assoc; diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 7cba5cb161..05ba114a96 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -355,8 +355,8 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr StringName class_name = *reinterpret_cast<const StringName *>(p_class_name); StringName parent_class_name = *reinterpret_cast<const StringName *>(p_parent_class_name); - ERR_FAIL_COND_MSG(!String(class_name).is_valid_unicode_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier."); - ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), "Attempt to register extension class '" + class_name + "', which appears to be already registered."); + ERR_FAIL_COND_MSG(!String(class_name).is_valid_unicode_identifier(), vformat("Attempt to register extension class '%s', which is not a valid class identifier.", class_name)); + ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), vformat("Attempt to register extension class '%s', which appears to be already registered.", class_name)); Extension *parent_extension = nullptr; @@ -370,7 +370,7 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr //inheriting from engine class } } else { - ERR_FAIL_MSG("Attempt to register an extension class '" + String(class_name) + "' using non-existing parent class '" + String(parent_class_name) + "'."); + ERR_FAIL_MSG(vformat("Attempt to register an extension class '%s' using non-existing parent class '%s'.", String(class_name), String(parent_class_name))); } #ifdef TOOLS_ENABLED @@ -463,7 +463,7 @@ void GDExtension::_register_extension_class_method(GDExtensionClassLibraryPtr p_ StringName class_name = *reinterpret_cast<const StringName *>(p_class_name); StringName method_name = *reinterpret_cast<const StringName *>(p_method_info->name); - ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension method '" + String(method_name) + "' for unexisting class '" + class_name + "'."); + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension method '%s' for unexisting class '%s'.", String(method_name), class_name)); #ifdef TOOLS_ENABLED Extension *extension = &self->extension_classes[class_name]; @@ -513,7 +513,7 @@ void GDExtension::_register_extension_class_integer_constant(GDExtensionClassLib StringName class_name = *reinterpret_cast<const StringName *>(p_class_name); StringName enum_name = *reinterpret_cast<const StringName *>(p_enum_name); StringName constant_name = *reinterpret_cast<const StringName *>(p_constant_name); - ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension constant '" + constant_name + "' for unexisting class '" + class_name + "'."); + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension constant '%s' for unexisting class '%s'.", constant_name, class_name)); #ifdef TOOLS_ENABLED // If the extension is still marked as reloading, that means it failed to register again. @@ -537,7 +537,7 @@ void GDExtension::_register_extension_class_property_indexed(GDExtensionClassLib StringName setter = *reinterpret_cast<const StringName *>(p_setter); StringName getter = *reinterpret_cast<const StringName *>(p_getter); String property_name = *reinterpret_cast<const StringName *>(p_info->name); - ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property '" + property_name + "' for unexisting class '" + class_name + "'."); + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension class property '%s' for unexisting class '%s'.", property_name, class_name)); #ifdef TOOLS_ENABLED // If the extension is still marked as reloading, that means it failed to register again. @@ -558,7 +558,7 @@ void GDExtension::_register_extension_class_property_group(GDExtensionClassLibra StringName class_name = *reinterpret_cast<const StringName *>(p_class_name); String group_name = *reinterpret_cast<const String *>(p_group_name); String prefix = *reinterpret_cast<const String *>(p_prefix); - ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property group '" + group_name + "' for unexisting class '" + class_name + "'."); + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension class property group '%s' for unexisting class '%s'.", group_name, class_name)); #ifdef TOOLS_ENABLED // If the extension is still marked as reloading, that means it failed to register again. @@ -577,7 +577,7 @@ void GDExtension::_register_extension_class_property_subgroup(GDExtensionClassLi StringName class_name = *reinterpret_cast<const StringName *>(p_class_name); String subgroup_name = *reinterpret_cast<const String *>(p_subgroup_name); String prefix = *reinterpret_cast<const String *>(p_prefix); - ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property subgroup '" + subgroup_name + "' for unexisting class '" + class_name + "'."); + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension class property subgroup '%s' for unexisting class '%s'.", subgroup_name, class_name)); #ifdef TOOLS_ENABLED // If the extension is still marked as reloading, that means it failed to register again. @@ -595,7 +595,7 @@ void GDExtension::_register_extension_class_signal(GDExtensionClassLibraryPtr p_ StringName class_name = *reinterpret_cast<const StringName *>(p_class_name); StringName signal_name = *reinterpret_cast<const StringName *>(p_signal_name); - ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class signal '" + signal_name + "' for unexisting class '" + class_name + "'."); + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension class signal '%s' for unexisting class '%s'.", signal_name, class_name)); #ifdef TOOLS_ENABLED // If the extension is still marked as reloading, that means it failed to register again. @@ -618,7 +618,7 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra GDExtension *self = reinterpret_cast<GDExtension *>(p_library); StringName class_name = *reinterpret_cast<const StringName *>(p_class_name); - ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to unregister unexisting extension class '" + class_name + "'."); + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to unregister unexisting extension class '%s'.", class_name)); Extension *ext = &self->extension_classes[class_name]; #ifdef TOOLS_ENABLED @@ -626,7 +626,7 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra self->_clear_extension(ext); } #endif - ERR_FAIL_COND_MSG(ext->gdextension.children.size(), "Attempt to unregister class '" + class_name + "' while other extension classes inherit from it."); + ERR_FAIL_COND_MSG(ext->gdextension.children.size(), vformat("Attempt to unregister class '%s' while other extension classes inherit from it.", class_name)); #ifdef TOOLS_ENABLED ClassDB::unregister_extension_class(class_name, !ext->is_reloading); @@ -664,13 +664,13 @@ void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExte HashMap<StringName, GDExtensionInterfaceFunctionPtr> GDExtension::gdextension_interface_functions; void GDExtension::register_interface_function(const StringName &p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) { - ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), "Attempt to register interface function '" + p_function_name + "', which appears to be already registered."); + ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), vformat("Attempt to register interface function '%s', which appears to be already registered.", p_function_name)); gdextension_interface_functions.insert(p_function_name, p_function_pointer); } GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(const StringName &p_function_name) { GDExtensionInterfaceFunctionPtr *function = gdextension_interface_functions.getptr(p_function_name); - ERR_FAIL_NULL_V_MSG(function, nullptr, "Attempt to get non-existent interface function: " + String(p_function_name) + "."); + ERR_FAIL_NULL_V_MSG(function, nullptr, vformat("Attempt to get non-existent interface function: '%s'.", String(p_function_name))); return *function; } @@ -680,8 +680,8 @@ Error GDExtension::open_library(const String &p_path, const Ref<GDExtensionLoade Error err = loader->open_library(p_path); - ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + p_path); - ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + p_path); + ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, vformat("GDExtension dynamic library not found: '%s'.", p_path)); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Can't open GDExtension dynamic library: '%s'.", p_path)); err = loader->initialize(&gdextension_get_proc_address, this, &initialization); diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index ddf90f6130..203f7960dc 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -34,6 +34,7 @@ #include "core/extension/gdextension.h" #include "core/extension/gdextension_compat_hashes.h" #include "core/io/file_access.h" +#include "core/io/image.h" #include "core/io/xml_parser.h" #include "core/object/class_db.h" #include "core/object/script_language_extension.h" @@ -507,6 +508,14 @@ static GDExtensionBool gdextension_variant_has_key(GDExtensionConstVariantPtr p_ return ret; } +static GDObjectInstanceID gdextension_variant_get_object_instance_id(GDExtensionConstVariantPtr p_self) { + const Variant *self = (const Variant *)p_self; + if (likely(self->get_type() == Variant::OBJECT)) { + return self->operator ObjectID(); + } + return 0; +} + static void gdextension_variant_get_type_name(GDExtensionVariantType p_type, GDExtensionUninitializedVariantPtr r_ret) { String name = Variant::get_type_name((Variant::Type)p_type); memnew_placement(r_ret, String(name)); @@ -1517,7 +1526,7 @@ static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionC #endif if (!mb && exists) { - ERR_PRINT("Method '" + classname + "." + methodname + "' has changed and no compatibility fallback has been provided. Please open an issue."); + ERR_PRINT(vformat("Method '%s.%s' has changed and no compatibility fallback has been provided. Please open an issue.", classname, methodname)); return nullptr; } ERR_FAIL_NULL_V(mb, nullptr); @@ -1610,6 +1619,7 @@ void gdextension_setup_interface() { REGISTER_INTERFACE_FUNC(variant_has_method); REGISTER_INTERFACE_FUNC(variant_has_member); REGISTER_INTERFACE_FUNC(variant_has_key); + REGISTER_INTERFACE_FUNC(variant_get_object_instance_id); REGISTER_INTERFACE_FUNC(variant_get_type_name); REGISTER_INTERFACE_FUNC(variant_can_convert); REGISTER_INTERFACE_FUNC(variant_can_convert_strict); diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 9e3ce25698..374dbfd071 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -1308,6 +1308,21 @@ typedef GDExtensionBool (*GDExtensionInterfaceVariantHasMember)(GDExtensionVaria typedef GDExtensionBool (*GDExtensionInterfaceVariantHasKey)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionBool *r_valid); /** + * @name variant_get_object_instance_id + * @since 4.4 + * + * Gets the object instance ID from a variant of type GDEXTENSION_VARIANT_TYPE_OBJECT. + * + * If the variant isn't of type GDEXTENSION_VARIANT_TYPE_OBJECT, then zero will be returned. + * The instance ID will be returned even if the object is no longer valid - use `object_get_instance_by_id()` to check if the object is still valid. + * + * @param p_self A pointer to the Variant. + * + * @return The instance ID for the contained object. + */ +typedef GDObjectInstanceID (*GDExtensionInterfaceVariantGetObjectInstanceId)(GDExtensionConstVariantPtr p_self); + +/** * @name variant_get_type_name * @since 4.1 * diff --git a/core/extension/gdextension_library_loader.cpp b/core/extension/gdextension_library_loader.cpp index 5ba4933c35..17200916ba 100644 --- a/core/extension/gdextension_library_loader.cpp +++ b/core/extension/gdextension_library_loader.cpp @@ -219,7 +219,7 @@ Error GDExtensionLibraryLoader::initialize(GDExtensionInterfaceGetProcAddress p_ Error err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, entry_symbol, entry_funcptr, false); if (err != OK) { - ERR_PRINT("GDExtension entry point '" + entry_symbol + "' not found in library " + library_path); + ERR_PRINT(vformat("GDExtension entry point '%s' not found in library %s.", entry_symbol, library_path)); return err; } @@ -230,7 +230,7 @@ Error GDExtensionLibraryLoader::initialize(GDExtensionInterfaceGetProcAddress p_ if (ret) { return OK; } else { - ERR_PRINT("GDExtension initialization function '" + entry_symbol + "' returned an error."); + ERR_PRINT(vformat("GDExtension initialization function '%s' returned an error.", entry_symbol)); return FAILED; } } @@ -259,6 +259,10 @@ bool GDExtensionLibraryLoader::has_library_changed() const { return false; } +bool GDExtensionLibraryLoader::library_exists() const { + return FileAccess::exists(resource_path); +} + Error GDExtensionLibraryLoader::parse_gdextension_file(const String &p_path) { resource_path = p_path; @@ -268,12 +272,12 @@ Error GDExtensionLibraryLoader::parse_gdextension_file(const String &p_path) { Error err = config->load(p_path); if (err != OK) { - ERR_PRINT("Error loading GDExtension configuration file: " + p_path); + ERR_PRINT(vformat("Error loading GDExtension configuration file: '%s'.", p_path)); return err; } if (!config->has_section_key("configuration", "entry_symbol")) { - ERR_PRINT("GDExtension configuration file must contain a \"configuration/entry_symbol\" key: " + p_path); + ERR_PRINT(vformat("GDExtension configuration file must contain a \"configuration/entry_symbol\" key: '%s'.", p_path)); return ERR_INVALID_DATA; } @@ -292,7 +296,7 @@ Error GDExtensionLibraryLoader::parse_gdextension_file(const String &p_path) { } } } else { - ERR_PRINT("GDExtension configuration file must contain a \"configuration/compatibility_minimum\" key: " + p_path); + ERR_PRINT(vformat("GDExtension configuration file must contain a \"configuration/compatibility_minimum\" key: '%s'.", p_path)); return ERR_INVALID_DATA; } diff --git a/core/extension/gdextension_library_loader.h b/core/extension/gdextension_library_loader.h index f4372a75d4..f781611b30 100644 --- a/core/extension/gdextension_library_loader.h +++ b/core/extension/gdextension_library_loader.h @@ -77,6 +77,7 @@ public: virtual void close_library() override; virtual bool is_library_open() const override; virtual bool has_library_changed() const override; + virtual bool library_exists() const override; Error parse_gdextension_file(const String &p_path); }; diff --git a/core/extension/gdextension_loader.h b/core/extension/gdextension_loader.h index 7d779858b7..2289550329 100644 --- a/core/extension/gdextension_loader.h +++ b/core/extension/gdextension_loader.h @@ -42,6 +42,7 @@ public: virtual void close_library() = 0; virtual bool is_library_open() const = 0; virtual bool has_library_changed() const = 0; + virtual bool library_exists() const = 0; }; #endif // GDEXTENSION_LOADER_H diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp index 01efe0d96e..0dc13cf375 100644 --- a/core/extension/gdextension_manager.cpp +++ b/core/extension/gdextension_manager.cpp @@ -258,7 +258,7 @@ void GDExtensionManager::load_extensions() { String s = f->get_line().strip_edges(); if (!s.is_empty()) { LoadStatus err = load_extension(s); - ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, "Error loading extension: " + s); + ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, vformat("Error loading extension: '%s'.", s)); } } @@ -302,7 +302,8 @@ bool GDExtensionManager::ensure_extensions_loaded(const HashSet<String> &p_exten for (const String &loaded_extension : loaded_extensions) { if (!p_extensions.has(loaded_extension)) { // The extension may not have a .gdextension file. - if (!FileAccess::exists(loaded_extension)) { + const Ref<GDExtension> extension = GDExtensionManager::get_singleton()->get_extension(loaded_extension); + if (!extension->get_loader()->library_exists()) { extensions_removed.push_back(loaded_extension); } } diff --git a/core/extension/make_wrappers.py b/core/extension/make_wrappers.py index 54f4fd5579..665b6f0f91 100644 --- a/core/extension/make_wrappers.py +++ b/core/extension/make_wrappers.py @@ -55,10 +55,10 @@ def generate_mod_version(argcount, const=False, returns=False): proto_ex = """ #define EXBIND$VER($RETTYPE m_name$ARG) \\ -GDVIRTUAL$VER($RETTYPE_##m_name$ARG)\\ +GDVIRTUAL$VER_REQUIRED($RETTYPE_##m_name$ARG)\\ virtual $RETVAL m_name($FUNCARGS) $CONST override { \\ $RETPRE\\ - GDVIRTUAL_REQUIRED_CALL(_##m_name$CALLARGS$RETREF);\\ + GDVIRTUAL_CALL(_##m_name$CALLARGS$RETREF);\\ $RETPOST\\ } """ diff --git a/core/input/SCsub b/core/input/SCsub index d8e6f33156..521f7702e4 100644 --- a/core/input/SCsub +++ b/core/input/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/input/input.cpp b/core/input/input.cpp index eba7ded267..7e2227c729 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -87,11 +87,50 @@ Input *Input::get_singleton() { void Input::set_mouse_mode(MouseMode p_mode) { ERR_FAIL_INDEX((int)p_mode, 5); + + if (p_mode == mouse_mode) { + return; + } + + // Allow to be set even if overridden, to see if the platform allows the mode. set_mouse_mode_func(p_mode); + mouse_mode = get_mouse_mode_func(); + + if (mouse_mode_override_enabled) { + set_mouse_mode_func(mouse_mode_override); + } } Input::MouseMode Input::get_mouse_mode() const { - return get_mouse_mode_func(); + return mouse_mode; +} + +void Input::set_mouse_mode_override_enabled(bool p_enabled) { + if (p_enabled == mouse_mode_override_enabled) { + return; + } + + mouse_mode_override_enabled = p_enabled; + + if (p_enabled) { + set_mouse_mode_func(mouse_mode_override); + mouse_mode_override = get_mouse_mode_func(); + } else { + set_mouse_mode_func(mouse_mode); + } +} + +void Input::set_mouse_mode_override(MouseMode p_mode) { + ERR_FAIL_INDEX((int)p_mode, 5); + + if (p_mode == mouse_mode_override) { + return; + } + + if (mouse_mode_override_enabled) { + set_mouse_mode_func(p_mode); + mouse_mode_override = get_mouse_mode_func(); + } } void Input::_bind_methods() { @@ -252,6 +291,10 @@ Input::VelocityTrack::VelocityTrack() { bool Input::is_anything_pressed() const { _THREAD_SAFE_METHOD_ + if (disable_input) { + return false; + } + if (!keys_pressed.is_empty() || !joy_buttons_pressed.is_empty() || !mouse_button_mask.is_empty()) { return true; } @@ -267,21 +310,41 @@ bool Input::is_anything_pressed() const { bool Input::is_key_pressed(Key p_keycode) const { _THREAD_SAFE_METHOD_ + + if (disable_input) { + return false; + } + return keys_pressed.has(p_keycode); } bool Input::is_physical_key_pressed(Key p_keycode) const { _THREAD_SAFE_METHOD_ + + if (disable_input) { + return false; + } + return physical_keys_pressed.has(p_keycode); } bool Input::is_key_label_pressed(Key p_keycode) const { _THREAD_SAFE_METHOD_ + + if (disable_input) { + return false; + } + return key_label_pressed.has(p_keycode); } bool Input::is_mouse_button_pressed(MouseButton p_button) const { _THREAD_SAFE_METHOD_ + + if (disable_input) { + return false; + } + return mouse_button_mask.has_flag(mouse_button_to_mask(p_button)); } @@ -295,11 +358,21 @@ static JoyButton _combine_device(JoyButton p_value, int p_device) { bool Input::is_joy_button_pressed(int p_device, JoyButton p_button) const { _THREAD_SAFE_METHOD_ + + if (disable_input) { + return false; + } + return joy_buttons_pressed.has(_combine_device(p_button, p_device)); } bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); + + if (disable_input) { + return false; + } + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); if (!E) { return false; @@ -310,6 +383,11 @@ bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const { bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); + + if (disable_input) { + return false; + } + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); if (!E) { return false; @@ -331,6 +409,11 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); + + if (disable_input) { + return false; + } + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); if (!E) { return false; @@ -352,6 +435,11 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co float Input::get_action_strength(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action)); + + if (disable_input) { + return 0.0f; + } + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); if (!E) { return 0.0f; @@ -366,6 +454,11 @@ float Input::get_action_strength(const StringName &p_action, bool p_exact) const float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action)); + + if (disable_input) { + return 0.0f; + } + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); if (!E) { return 0.0f; @@ -410,6 +503,11 @@ Vector2 Input::get_vector(const StringName &p_negative_x, const StringName &p_po float Input::get_joy_axis(int p_device, JoyAxis p_axis) const { _THREAD_SAFE_METHOD_ + + if (disable_input) { + return 0; + } + JoyAxis c = _combine_device(p_axis, p_device); if (_joy_axis.has(c)) { return _joy_axis[c]; @@ -690,6 +788,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em button_event->set_canceled(st->is_canceled()); button_event->set_button_index(MouseButton::LEFT); button_event->set_double_click(st->is_double_tap()); + button_event->set_window_id(st->get_window_id()); BitField<MouseButtonMask> ev_bm = mouse_button_mask; if (st->is_pressed()) { @@ -727,6 +826,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em motion_event->set_velocity(sd->get_velocity()); motion_event->set_screen_velocity(sd->get_screen_velocity()); motion_event->set_button_mask(mouse_button_mask); + motion_event->set_window_id(sd->get_window_id()); _parse_input_event_impl(motion_event, true); } @@ -934,7 +1034,7 @@ void Input::action_release(const StringName &p_action) { // Create or retrieve existing action. ActionState &action_state = action_states[p_action]; - action_state.cache.pressed = 0; + action_state.cache.pressed = false; action_state.cache.strength = 0.0; action_state.cache.raw_strength = 0.0; // As input may come in part way through a physics tick, the earliest we can react to it is the next physics tick. @@ -1662,6 +1762,14 @@ int Input::get_unused_joy_id() { return -1; } +void Input::set_disable_input(bool p_disable) { + disable_input = p_disable; +} + +bool Input::is_input_disabled() const { + return disable_input; +} + Input::Input() { singleton = this; diff --git a/core/input/input.h b/core/input/input.h index 95dd623cc0..a189ae7d9a 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -103,6 +103,11 @@ private: Vector2 mouse_pos; int64_t mouse_window = 0; bool legacy_just_pressed_behavior = false; + bool disable_input = false; + + MouseMode mouse_mode = MOUSE_MODE_VISIBLE; + bool mouse_mode_override_enabled = false; + MouseMode mouse_mode_override = MOUSE_MODE_VISIBLE; struct ActionState { uint64_t pressed_physics_frame = UINT64_MAX; @@ -279,6 +284,8 @@ protected: public: void set_mouse_mode(MouseMode p_mode); MouseMode get_mouse_mode() const; + void set_mouse_mode_override_enabled(bool p_enabled); + void set_mouse_mode_override(MouseMode p_mode); #ifdef TOOLS_ENABLED void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; @@ -380,6 +387,9 @@ public: void set_event_dispatch_function(EventDispatchFunc p_function); + void set_disable_input(bool p_disable); + bool is_input_disabled() const; + Input(); ~Input(); }; diff --git a/core/input/input_builders.py b/core/input/input_builders.py index ae848f4e7c..3685e726b4 100644 --- a/core/input/input_builders.py +++ b/core/input/input_builders.py @@ -33,7 +33,7 @@ def make_default_controller_mappings(target, source, env): guid = line_parts[0] if guid in platform_mappings[current_platform]: g.write( - "// WARNING - DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format( + "// WARNING: DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format( src_path, current_platform, platform_mappings[current_platform][guid] ) ) diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index 905526bbbd..d125bad252 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -35,9 +35,6 @@ #include "core/os/keyboard.h" #include "core/os/os.h" -const int InputEvent::DEVICE_ID_EMULATION = -1; -const int InputEvent::DEVICE_ID_INTERNAL = -2; - void InputEvent::set_device(int p_device) { device = p_device; emit_changed(); diff --git a/core/input/input_event.h b/core/input/input_event.h index 19176f748e..80bca28fbf 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -62,8 +62,9 @@ protected: static void _bind_methods(); public: - static const int DEVICE_ID_EMULATION; - static const int DEVICE_ID_INTERNAL; + inline static constexpr int DEVICE_ID_EMULATION = -1; + inline static constexpr int DEVICE_ID_INTERNAL = -2; + inline static constexpr int DEVICE_ID_ALL_DEVICES = -3; // Signify that a given Action can be triggered by any device. void set_device(int p_device); int get_device() const; diff --git a/core/input/input_map.compat.inc b/core/input/input_map.compat.inc new file mode 100644 index 0000000000..da4bd962b6 --- /dev/null +++ b/core/input/input_map.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* input_map.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +void InputMap::_add_action_bind_compat_97281(const StringName &p_action, float p_deadzone) { + add_action(p_action, p_deadzone); +} + +void InputMap::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::_add_action_bind_compat_97281, DEFVAL(0.5f)); +} + +#endif // DISABLE_DEPRECATED diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index 9a772c87c9..6378f18545 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "input_map.h" +#include "input_map.compat.inc" #include "core/config/project_settings.h" #include "core/input/input.h" @@ -38,12 +39,10 @@ InputMap *InputMap::singleton = nullptr; -int InputMap::ALL_DEVICES = -1; - void InputMap::_bind_methods() { ClassDB::bind_method(D_METHOD("has_action", "action"), &InputMap::has_action); ClassDB::bind_method(D_METHOD("get_actions"), &InputMap::_get_actions); - ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(0.5f)); + ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(0.2f)); ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action); ClassDB::bind_method(D_METHOD("action_set_deadzone", "action", "deadzone"), &InputMap::action_set_deadzone); @@ -115,7 +114,7 @@ void InputMap::get_argument_options(const StringName &p_function, int p_idx, Lis #endif void InputMap::add_action(const StringName &p_action, float p_deadzone) { - ERR_FAIL_COND_MSG(input_map.has(p_action), "InputMap already has action \"" + String(p_action) + "\"."); + ERR_FAIL_COND_MSG(input_map.has(p_action), vformat("InputMap already has action \"%s\".", String(p_action))); input_map[p_action] = Action(); static int last_id = 1; input_map[p_action].id = last_id; @@ -162,7 +161,7 @@ List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Re int i = 0; for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) { int device = E->get()->get_device(); - if (device == ALL_DEVICES || device == p_event->get_device()) { + if (device == InputEvent::DEVICE_ID_ALL_DEVICES || device == p_event->get_device()) { if (E->get()->action_match(p_event, p_exact_match, p_action.deadzone, r_pressed, r_strength, r_raw_strength)) { if (r_event_index) { *r_event_index = i; @@ -306,7 +305,7 @@ void InputMap::load_from_project_settings() { String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length()); Dictionary action = GLOBAL_GET(pi.name); - float deadzone = action.has("deadzone") ? (float)action["deadzone"] : 0.5f; + float deadzone = action.has("deadzone") ? (float)action["deadzone"] : 0.2f; Array events = action["events"]; add_action(name, deadzone); diff --git a/core/input/input_map.h b/core/input/input_map.h index 3774a131e6..45798490f7 100644 --- a/core/input/input_map.h +++ b/core/input/input_map.h @@ -43,11 +43,6 @@ class InputMap : public Object { GDCLASS(InputMap, Object); public: - /** - * A special value used to signify that a given Action can be triggered by any device - */ - static int ALL_DEVICES; - struct Action { int id; float deadzone; @@ -69,12 +64,17 @@ private: protected: static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + void _add_action_bind_compat_97281(const StringName &p_action, float p_deadzone = 0.5); + static void _bind_compatibility_methods(); +#endif // DISABLE_DEPRECATED + public: static _FORCE_INLINE_ InputMap *get_singleton() { return singleton; } bool has_action(const StringName &p_action) const; List<StringName> get_actions() const; - void add_action(const StringName &p_action, float p_deadzone = 0.5); + void add_action(const StringName &p_action, float p_deadzone = 0.2); void erase_action(const StringName &p_action); float action_get_deadzone(const StringName &p_action); diff --git a/core/io/SCsub b/core/io/SCsub index 19a6549225..ab81175894 100644 --- a/core/io/SCsub +++ b/core/io/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp index 2f3fe4deeb..5d2f65ca99 100644 --- a/core/io/dir_access.cpp +++ b/core/io/dir_access.cpp @@ -177,7 +177,7 @@ Error DirAccess::make_dir_recursive(const String &p_dir) { curpath = curpath.path_join(subdirs[i]); Error err = make_dir(curpath); if (err != OK && err != ERR_ALREADY_EXISTS) { - ERR_FAIL_V_MSG(err, "Could not create directory: " + curpath); + ERR_FAIL_V_MSG(err, vformat("Could not create directory: '%s'.", curpath)); } } @@ -239,7 +239,7 @@ Ref<DirAccess> DirAccess::create_for_path(const String &p_path) { Ref<DirAccess> DirAccess::open(const String &p_path, Error *r_error) { Ref<DirAccess> da = create_for_path(p_path); - ERR_FAIL_COND_V_MSG(da.is_null(), nullptr, "Cannot create DirAccess for path '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(da.is_null(), nullptr, vformat("Cannot create DirAccess for path '%s'.", p_path)); Error err = da->change_dir(p_path); if (r_error) { *r_error = err; @@ -345,10 +345,10 @@ Error DirAccess::copy(const String &p_from, const String &p_to, int p_chmod_flag Error err; { Ref<FileAccess> fsrc = FileAccess::open(p_from, FileAccess::READ, &err); - ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to open " + p_from); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to open '%s'.", p_from)); Ref<FileAccess> fdst = FileAccess::open(p_to, FileAccess::WRITE, &err); - ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to open " + p_to); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to open '%s'.", p_to)); const size_t copy_buffer_limit = 65536; // 64 KB @@ -444,11 +444,11 @@ Error DirAccess::_copy_dir(Ref<DirAccess> &p_target_da, const String &p_to, int 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_MSG(err != OK, err, "Cannot create directory '" + target_dir + "'."); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot create directory '%s'.", target_dir)); } Error err = change_dir(rel_path); - ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + rel_path + "'."); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot change current directory to '%s'.", rel_path)); err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags, p_copy_links); if (err) { @@ -466,11 +466,11 @@ Error DirAccess::copy_dir(const String &p_from, String p_to, int p_chmod_flags, ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist."); Ref<DirAccess> target_da = DirAccess::create_for_path(p_to); - ERR_FAIL_COND_V_MSG(target_da.is_null(), ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_to + "'."); + ERR_FAIL_COND_V_MSG(target_da.is_null(), ERR_CANT_CREATE, vformat("Cannot create DirAccess for path '%s'.", p_to)); if (!target_da->dir_exists(p_to)) { Error err = target_da->make_dir_recursive(p_to); - ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + p_to + "'."); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot create directory '%s'.", p_to)); } if (!p_to.ends_with("/")) { diff --git a/core/io/dir_access.h b/core/io/dir_access.h index e9c864c56b..2392944f76 100644 --- a/core/io/dir_access.h +++ b/core/io/dir_access.h @@ -96,8 +96,8 @@ public: virtual bool file_exists(String p_file) = 0; virtual bool dir_exists(String p_dir) = 0; - virtual bool is_readable(String p_dir) { return true; }; - virtual bool is_writable(String p_dir) { return true; }; + virtual bool is_readable(String p_dir) { return true; } + virtual bool is_writable(String p_dir) { return true; } static bool exists(const String &p_dir); virtual uint64_t get_space_left() = 0; @@ -116,10 +116,10 @@ public: Ref<DirAccess> da = create(ACCESS_FILESYSTEM); if (da->file_exists(p_path)) { if (da->remove(p_path) != OK) { - ERR_FAIL_MSG("Cannot remove file or directory: " + p_path); + ERR_FAIL_MSG(vformat("Cannot remove file or directory: '%s'.", p_path)); } } else { - ERR_FAIL_MSG("Cannot remove non-existent file or directory: " + p_path); + ERR_FAIL_MSG(vformat("Cannot remove non-existent file or directory: '%s'.", p_path)); } } diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index d919243e6b..d8bf645a7d 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -459,7 +459,7 @@ Vector<uint8_t> FileAccess::get_buffer(int64_t p_length) const { } Error err = data.resize(p_length); - ERR_FAIL_COND_V_MSG(err != OK, data, "Can't resize data to " + itos(p_length) + " elements."); + ERR_FAIL_COND_V_MSG(err != OK, data, vformat("Can't resize data to %d elements.", p_length)); uint8_t *w = data.ptrw(); int64_t len = get_buffer(w, p_length); @@ -540,7 +540,7 @@ uint64_t FileAccess::get_modified_time(const String &p_file) { } Ref<FileAccess> fa = create_for_path(p_file); - ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "Cannot create FileAccess for path '" + p_file + "'."); + ERR_FAIL_COND_V_MSG(fa.is_null(), 0, vformat("Cannot create FileAccess for path '%s'.", p_file)); uint64_t mt = fa->_get_modified_time(p_file); return mt; @@ -552,7 +552,7 @@ BitField<FileAccess::UnixPermissionFlags> FileAccess::get_unix_permissions(const } Ref<FileAccess> fa = create_for_path(p_file); - ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "Cannot create FileAccess for path '" + p_file + "'."); + ERR_FAIL_COND_V_MSG(fa.is_null(), 0, vformat("Cannot create FileAccess for path '%s'.", p_file)); return fa->_get_unix_permissions(p_file); } @@ -563,7 +563,7 @@ Error FileAccess::set_unix_permissions(const String &p_file, BitField<FileAccess } Ref<FileAccess> fa = create_for_path(p_file); - ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'."); + ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file)); Error err = fa->_set_unix_permissions(p_file, p_permissions); return err; @@ -575,7 +575,7 @@ bool FileAccess::get_hidden_attribute(const String &p_file) { } Ref<FileAccess> fa = create_for_path(p_file); - ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'."); + ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("Cannot create FileAccess for path '%s'.", p_file)); return fa->_get_hidden_attribute(p_file); } @@ -586,7 +586,7 @@ Error FileAccess::set_hidden_attribute(const String &p_file, bool p_hidden) { } Ref<FileAccess> fa = create_for_path(p_file); - ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'."); + ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file)); Error err = fa->_set_hidden_attribute(p_file, p_hidden); return err; @@ -598,7 +598,7 @@ bool FileAccess::get_read_only_attribute(const String &p_file) { } Ref<FileAccess> fa = create_for_path(p_file); - ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'."); + ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("Cannot create FileAccess for path '%s'.", p_file)); return fa->_get_read_only_attribute(p_file); } @@ -609,7 +609,7 @@ Error FileAccess::set_read_only_attribute(const String &p_file, bool p_ro) { } Ref<FileAccess> fa = create_for_path(p_file); - ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'."); + ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file)); Error err = fa->_set_read_only_attribute(p_file, p_ro); return err; @@ -697,7 +697,7 @@ Vector<uint8_t> FileAccess::get_file_as_bytes(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>(), vformat("Can't open file from path '%s'.", String(p_path))); } Vector<uint8_t> data; data.resize(f->get_length()); @@ -715,7 +715,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(), vformat("Can't get file as string from path '%s'.", String(p_path))); } String ret; diff --git a/core/io/file_access.h b/core/io/file_access.h index 2f4d1a8604..7f5687fe03 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -215,8 +215,8 @@ public: static bool get_read_only_attribute(const String &p_file); static Error set_read_only_attribute(const String &p_file, bool p_ro); - static void set_backup_save(bool p_enable) { backup_save = p_enable; }; - static bool is_backup_save_enabled() { return backup_save; }; + static void set_backup_save(bool p_enable) { backup_save = p_enable; } + static bool is_backup_save_enabled() { return backup_save; } static String get_md5(const String &p_file); static String get_sha256(const String &p_file); diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index 3602baf8c5..f7f2852e0a 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -58,7 +58,7 @@ Error FileAccessCompressed::open_after_magic(Ref<FileAccess> p_base) { block_size = f->get_32(); if (block_size == 0) { f.unref(); - ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Can't open compressed file '" + p_base->get_path() + "' with block size 0, it is corrupted."); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, vformat("Can't open compressed file '%s' with block size 0, it is corrupted.", p_base->get_path())); } read_total = f->get_32(); uint32_t bc = (read_total / block_size) + 1; diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index 13d1e0c8fc..24be9ef230 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -37,7 +37,7 @@ #include <stdio.h> Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic) { - ERR_FAIL_COND_V_MSG(file.is_valid(), ERR_ALREADY_IN_USE, "Can't open file while another file from path '" + file->get_path_absolute() + "' is open."); + ERR_FAIL_COND_V_MSG(file.is_valid(), ERR_ALREADY_IN_USE, vformat("Can't open file while another file from path '%s' is open.", file->get_path_absolute())); ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER); pos = 0; diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index 1541a5ed4a..8d74011632 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -85,7 +85,7 @@ Error FileAccessMemory::open_internal(const String &p_path, int p_mode_flags) { //name = DirAccess::normalize_path(name); HashMap<String, Vector<uint8_t>>::Iterator E = files->find(name); - ERR_FAIL_COND_V_MSG(!E, ERR_FILE_NOT_FOUND, "Can't find file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(!E, ERR_FILE_NOT_FOUND, vformat("Can't find file '%s'.", p_path)); data = E->value.ptrw(); length = E->value.size(); diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index eec27ce0aa..bfd1a53f3e 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -102,6 +102,22 @@ void PackedData::add_pack_source(PackSource *p_source) { } } +uint8_t *PackedData::get_file_hash(const String &p_path) { + PathMD5 pmd5(p_path.md5_buffer()); + HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5); + if (!E || E->value.offset == 0) { + return nullptr; + } + + return E->value.md5; +} + +void PackedData::clear() { + files.clear(); + _free_packed_dirs(root); + root = memnew(PackedDir); +} + PackedData *PackedData::singleton = nullptr; PackedData::PackedData() { @@ -207,8 +223,8 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, uint32_t ver_minor = f->get_32(); f->get_32(); // patch number, not used for validation. - ERR_FAIL_COND_V_MSG(version != PACK_FORMAT_VERSION, false, "Pack version unsupported: " + itos(version) + "."); - ERR_FAIL_COND_V_MSG(ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), false, "Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor) + "."); + ERR_FAIL_COND_V_MSG(version != PACK_FORMAT_VERSION, false, vformat("Pack version unsupported: %d.", version)); + ERR_FAIL_COND_V_MSG(ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), false, vformat("Pack created with a newer version of the engine: %d.%d.", ver_major, ver_minor)); uint32_t pack_flags = f->get_32(); uint64_t file_base = f->get_64(); @@ -370,7 +386,7 @@ void FileAccessPack::close() { FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) : pf(p_file), f(FileAccess::open(pf.pack, FileAccess::READ)) { - ERR_FAIL_COND_MSG(f.is_null(), "Can't open pack-referenced file '" + String(pf.pack) + "'."); + ERR_FAIL_COND_MSG(f.is_null(), vformat("Can't open pack-referenced file '%s'.", String(pf.pack))); f->seek(pf.offset); off = pf.offset; @@ -378,7 +394,7 @@ FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFil if (pf.encrypted) { Ref<FileAccessEncrypted> fae; fae.instantiate(); - ERR_FAIL_COND_MSG(fae.is_null(), "Can't open encrypted pack-referenced file '" + String(pf.pack) + "'."); + ERR_FAIL_COND_MSG(fae.is_null(), vformat("Can't open encrypted pack-referenced file '%s'.", String(pf.pack))); Vector<uint8_t> key; key.resize(32); @@ -387,7 +403,7 @@ FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFil } Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false); - ERR_FAIL_COND_MSG(err, "Can't open encrypted pack-referenced file '" + String(pf.pack) + "'."); + ERR_FAIL_COND_MSG(err, vformat("Can't open encrypted pack-referenced file '%s'.", String(pf.pack))); f = fae; off = 0; } diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 595a36bca4..57b7a5f87f 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -111,6 +111,7 @@ private: public: void add_pack_source(PackSource *p_source); void add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false); // for PackSource + uint8_t *get_file_hash(const String &p_path); void set_disabled(bool p_disabled) { disabled = p_disabled; } _FORCE_INLINE_ bool is_disabled() const { return disabled; } @@ -118,6 +119,8 @@ public: static PackedData *get_singleton() { return singleton; } Error add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset); + void clear(); + _FORCE_INLINE_ Ref<FileAccess> try_open_path(const String &p_path); _FORCE_INLINE_ bool has_path(const String &p_path); diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index b33b7b35c3..41907d1a3f 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -116,7 +116,7 @@ void ZipArchive::close_handle(unzFile p_file) const { } unzFile ZipArchive::get_file_handle(const String &p_file) const { - ERR_FAIL_COND_V_MSG(!file_exists(p_file), nullptr, "File '" + p_file + " doesn't exist."); + ERR_FAIL_COND_V_MSG(!file_exists(p_file), nullptr, vformat("File '%s' doesn't exist.", p_file)); File file = files[p_file]; zlib_filefunc_def io; @@ -136,7 +136,7 @@ unzFile ZipArchive::get_file_handle(const String &p_file) const { io.free_mem = godot_free; unzFile pkg = unzOpen2(packages[file.package].filename.utf8().get_data(), &io); - ERR_FAIL_NULL_V_MSG(pkg, nullptr, "Cannot open file '" + packages[file.package].filename + "'."); + ERR_FAIL_NULL_V_MSG(pkg, nullptr, vformat("Cannot open file '%s'.", packages[file.package].filename)); int unz_err = unzGoToFilePos(pkg, &file.file_pos); if (unz_err != UNZ_OK || unzOpenCurrentFile(pkg) != UNZ_OK) { unzClose(pkg); diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index fc91341bed..9e426c4908 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -100,9 +100,9 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) { Error HTTPClient::verify_headers(const Vector<String> &p_headers) { for (int i = 0; i < p_headers.size(); i++) { String sanitized = p_headers[i].strip_edges(); - ERR_FAIL_COND_V_MSG(sanitized.is_empty(), ERR_INVALID_PARAMETER, "Invalid HTTP header at index " + itos(i) + ": empty."); + ERR_FAIL_COND_V_MSG(sanitized.is_empty(), ERR_INVALID_PARAMETER, vformat("Invalid HTTP header at index %d: empty.", i)); ERR_FAIL_COND_V_MSG(sanitized.find(":") < 1, ERR_INVALID_PARAMETER, - "Invalid HTTP header at index " + itos(i) + ": String must contain header-value pair, delimited by ':', but was: " + p_headers[i]); + vformat("Invalid HTTP header at index %d: String must contain header-value pair, delimited by ':', but was: '%s'.", i, p_headers[i])); } return OK; diff --git a/core/io/image.cpp b/core/io/image.cpp index aa391b77dd..9b5bb058ef 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -535,7 +535,7 @@ static bool _are_formats_compatible(Image::Format p_format0, Image::Format p_for } void Image::convert(Format p_new_format) { - ERR_FAIL_INDEX_MSG(p_new_format, FORMAT_MAX, "The Image format specified (" + itos(p_new_format) + ") is out of range. See Image's Format enum."); + ERR_FAIL_INDEX_MSG(p_new_format, FORMAT_MAX, vformat("The Image format specified (%d) is out of range. See Image's Format enum.", p_new_format)); if (data.size() == 0) { return; } @@ -1132,9 +1132,9 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { ERR_FAIL_COND_MSG(p_width <= 0, "Image width must be greater than 0."); ERR_FAIL_COND_MSG(p_height <= 0, "Image height must be greater than 0."); - ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + "."); - ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + "."); - ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS)); + ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, vformat("Image width cannot be greater than %d pixels.", MAX_WIDTH)); + ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, vformat("Image height cannot be greater than %d pixels.", MAX_HEIGHT)); + ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, vformat("Too many pixels for image, maximum is %d pixels.", MAX_PIXELS)); if (p_width == width && p_height == height) { return; @@ -1435,8 +1435,8 @@ void Image::crop_from_point(int p_x, int p_y, int p_width, int p_height) { 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) + "."); + ERR_FAIL_COND_MSG(p_x + p_width > MAX_WIDTH, vformat("End x position cannot be greater than %d.", MAX_WIDTH)); + ERR_FAIL_COND_MSG(p_y + p_height > MAX_HEIGHT, vformat("End y position cannot be greater than %d.", 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 @@ -1484,8 +1484,8 @@ void Image::crop(int p_width, int p_height) { void Image::rotate_90(ClockDirection p_direction) { ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot rotate in compressed or custom image formats."); - ERR_FAIL_COND_MSG(width <= 0, "The Image width specified (" + itos(width) + " pixels) must be greater than 0 pixels."); - ERR_FAIL_COND_MSG(height <= 0, "The Image height specified (" + itos(height) + " pixels) must be greater than 0 pixels."); + ERR_FAIL_COND_MSG(width <= 0, vformat("The Image width specified (%d pixels) must be greater than 0 pixels.", width)); + ERR_FAIL_COND_MSG(height <= 0, vformat("The Image height specified (%d pixels) must be greater than 0 pixels.", height)); bool used_mipmaps = has_mipmaps(); if (used_mipmaps) { @@ -1602,8 +1602,8 @@ void Image::rotate_90(ClockDirection p_direction) { void Image::rotate_180() { ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot rotate in compressed or custom image formats."); - ERR_FAIL_COND_MSG(width <= 0, "The Image width specified (" + itos(width) + " pixels) must be greater than 0 pixels."); - ERR_FAIL_COND_MSG(height <= 0, "The Image height specified (" + itos(height) + " pixels) must be greater than 0 pixels."); + ERR_FAIL_COND_MSG(width <= 0, vformat("The Image width specified (%d pixels) must be greater than 0 pixels.", width)); + ERR_FAIL_COND_MSG(height <= 0, vformat("The Image height specified (%d pixels) must be greater than 0 pixels.", height)); bool used_mipmaps = has_mipmaps(); if (used_mipmaps) { @@ -2249,15 +2249,15 @@ void Image::set_data(int p_width, int p_height, bool p_use_mipmaps, Format p_for } void Image::initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { - ERR_FAIL_COND_MSG(p_width <= 0, "The Image width specified (" + itos(p_width) + " pixels) must be greater than 0 pixels."); - ERR_FAIL_COND_MSG(p_height <= 0, "The Image height specified (" + itos(p_height) + " pixels) must be greater than 0 pixels."); + ERR_FAIL_COND_MSG(p_width <= 0, vformat("The Image width specified (%d pixels) must be greater than 0 pixels.", p_width)); + ERR_FAIL_COND_MSG(p_height <= 0, vformat("The Image height specified (%d pixels) must be greater than 0 pixels.", p_height)); ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, - "The Image width specified (" + itos(p_width) + " pixels) cannot be greater than " + itos(MAX_WIDTH) + "pixels."); + vformat("The Image width specified (%d pixels) cannot be greater than %d pixels.", p_width, MAX_WIDTH)); ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, - "The Image height specified (" + itos(p_height) + " pixels) cannot be greater than " + itos(MAX_HEIGHT) + "pixels."); + vformat("The Image height specified (%d pixels) cannot be greater than %d pixels.", p_height, MAX_HEIGHT)); ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, - "Too many pixels for Image. Maximum is " + itos(MAX_WIDTH) + "x" + itos(MAX_HEIGHT) + " = " + itos(MAX_PIXELS) + "pixels."); - ERR_FAIL_INDEX_MSG(p_format, FORMAT_MAX, "The Image format specified (" + itos(p_format) + ") is out of range. See Image's Format enum."); + vformat("Too many pixels for Image. Maximum is %dx%d = %d pixels.", MAX_WIDTH, MAX_HEIGHT, MAX_PIXELS)); + ERR_FAIL_INDEX_MSG(p_format, FORMAT_MAX, vformat("The Image format specified (%d) is out of range. See Image's Format enum.", p_format)); int mm = 0; int64_t size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); @@ -2275,15 +2275,15 @@ void Image::initialize_data(int p_width, int p_height, bool p_use_mipmaps, Forma } void Image::initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { - ERR_FAIL_COND_MSG(p_width <= 0, "The Image width specified (" + itos(p_width) + " pixels) must be greater than 0 pixels."); - ERR_FAIL_COND_MSG(p_height <= 0, "The Image height specified (" + itos(p_height) + " pixels) must be greater than 0 pixels."); + ERR_FAIL_COND_MSG(p_width <= 0, vformat("The Image width specified (%d pixels) must be greater than 0 pixels.", p_width)); + ERR_FAIL_COND_MSG(p_height <= 0, vformat("The Image height specified (%d pixels) must be greater than 0 pixels.", p_height)); ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, - "The Image width specified (" + itos(p_width) + " pixels) cannot be greater than " + itos(MAX_WIDTH) + " pixels."); + vformat("The Image width specified (%d pixels) cannot be greater than %d pixels.", p_width, MAX_WIDTH)); ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, - "The Image height specified (" + itos(p_height) + " pixels) cannot be greater than " + itos(MAX_HEIGHT) + " pixels."); + vformat("The Image height specified (%d pixels) cannot be greater than %d pixels.", p_height, MAX_HEIGHT)); ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, - "Too many pixels for Image. Maximum is " + itos(MAX_WIDTH) + "x" + itos(MAX_HEIGHT) + " = " + itos(MAX_PIXELS) + "pixels ."); - ERR_FAIL_INDEX_MSG(p_format, FORMAT_MAX, "The Image format specified (" + itos(p_format) + ") is out of range. See Image's Format enum."); + vformat("Too many pixels for Image. Maximum is %dx%d = %d pixels.", MAX_WIDTH, MAX_HEIGHT, MAX_PIXELS)); + ERR_FAIL_INDEX_MSG(p_format, FORMAT_MAX, vformat("The Image format specified (%d) is out of range. See Image's Format enum.", p_format)); int mm; int64_t size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); @@ -2577,7 +2577,7 @@ Image::AlphaMode Image::detect_alpha() const { Error Image::load(const String &p_path) { #ifdef DEBUG_ENABLED if (p_path.begins_with("res://") && ResourceLoader::exists(p_path)) { - WARN_PRINT("Loaded resource as image file, this will not work on export: '" + p_path + "'. Instead, import the image file as an Image resource and load it normally as a resource."); + WARN_PRINT(vformat("Loaded resource as image file, this will not work on export: '%s'. Instead, import the image file as an Image resource and load it normally as a resource.", p_path)); } #endif return ImageLoader::load_image(p_path, this); @@ -2586,7 +2586,7 @@ Error Image::load(const String &p_path) { Ref<Image> Image::load_from_file(const String &p_path) { #ifdef DEBUG_ENABLED if (p_path.begins_with("res://") && ResourceLoader::exists(p_path)) { - WARN_PRINT("Loaded resource as image file, this will not work on export: '" + p_path + "'. Instead, import the image file as an Image resource and load it normally as a resource."); + WARN_PRINT(vformat("Loaded resource as image file, this will not work on export: '%s'. Instead, import the image file as an Image resource and load it normally as a resource.", p_path)); } #endif Ref<Image> image; @@ -2649,7 +2649,7 @@ Error Image::save_webp(const String &p_path, const bool p_lossy, const float p_q if (save_webp_func == nullptr) { return ERR_UNAVAILABLE; } - ERR_FAIL_COND_V_MSG(p_lossy && !(0.0f <= p_quality && p_quality <= 1.0f), ERR_INVALID_PARAMETER, "The WebP lossy quality was set to " + rtos(p_quality) + ", which is not valid. WebP lossy quality must be between 0.0 and 1.0 (inclusive)."); + ERR_FAIL_COND_V_MSG(p_lossy && !(0.0f <= p_quality && p_quality <= 1.0f), ERR_INVALID_PARAMETER, vformat("The WebP lossy quality was set to %f, which is not valid. WebP lossy quality must be between 0.0 and 1.0 (inclusive).", p_quality)); return save_webp_func(p_path, Ref<Image>((Image *)this), p_lossy, p_quality); } @@ -2658,7 +2658,7 @@ Vector<uint8_t> Image::save_webp_to_buffer(const bool p_lossy, const float p_qua if (save_webp_buffer_func == nullptr) { return Vector<uint8_t>(); } - ERR_FAIL_COND_V_MSG(p_lossy && !(0.0f <= p_quality && p_quality <= 1.0f), Vector<uint8_t>(), "The WebP lossy quality was set to " + rtos(p_quality) + ", which is not valid. WebP lossy quality must be between 0.0 and 1.0 (inclusive)."); + ERR_FAIL_COND_V_MSG(p_lossy && !(0.0f <= p_quality && p_quality <= 1.0f), Vector<uint8_t>(), vformat("The WebP lossy quality was set to %f, which is not valid. WebP lossy quality must be between 0.0 and 1.0 (inclusive).", p_quality)); return save_webp_buffer_func(Ref<Image>((Image *)this), p_lossy, p_quality); } @@ -2751,7 +2751,7 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels case COMPRESS_S3TC: { // BC3 is unsupported currently. - if ((p_channels == USED_CHANNELS_RGB || p_channels == USED_CHANNELS_L) && _image_compress_bc_rd_func) { + if ((p_channels == USED_CHANNELS_R || p_channels == USED_CHANNELS_RGB || p_channels == USED_CHANNELS_L) && _image_compress_bc_rd_func) { Error result = _image_compress_bc_rd_func(this, p_channels); // If the image was compressed successfully, we return here. If not, we fall back to the default compression scheme. diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index 92c690dc2a..50c4704aa3 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -87,7 +87,7 @@ Error ImageLoader::load_image(const String &p_file, Ref<Image> p_image, Ref<File if (f.is_null()) { Error err; f = FileAccess::open(p_file, FileAccess::READ, &err); - ERR_FAIL_COND_V_MSG(f.is_null(), err, "Error opening file '" + p_file + "'."); + ERR_FAIL_COND_V_MSG(f.is_null(), err, vformat("Error opening file '%s'.", p_file)); } String extension = p_file.get_extension(); @@ -98,7 +98,7 @@ Error ImageLoader::load_image(const String &p_file, Ref<Image> p_image, Ref<File } Error err = loader.write[i]->load_image(p_image, f, p_flags, p_scale); if (err != OK) { - ERR_PRINT("Error loading image: " + p_file); + ERR_PRINT(vformat("Error loading image: '%s'.", p_file)); } if (err != ERR_FILE_UNRECOGNIZED) { diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 38c71b19fa..3c67a8f894 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -51,7 +51,7 @@ struct _IP_ResolverPrivate { response.clear(); type = IP::TYPE_NONE; hostname = ""; - }; + } QueueItem() { clear(); @@ -201,7 +201,7 @@ IPAddress IP::get_resolve_item_address(ResolverID p_id) const { MutexLock lock(resolver->mutex); if (resolver->queue[p_id].status.get() != IP::RESOLVER_STATUS_DONE) { - ERR_PRINT("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet."); + ERR_PRINT(vformat("Resolve of '%s' didn't complete yet.", resolver->queue[p_id].hostname)); return IPAddress(); } @@ -220,7 +220,7 @@ Array IP::get_resolve_item_addresses(ResolverID p_id) const { MutexLock lock(resolver->mutex); if (resolver->queue[p_id].status.get() != IP::RESOLVER_STATUS_DONE) { - ERR_PRINT("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet."); + ERR_PRINT(vformat("Resolve of '%s' didn't complete yet.", resolver->queue[p_id].hostname)); return Array(); } diff --git a/core/io/json.cpp b/core/io/json.cpp index 664ff7857b..e73677be9c 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -121,7 +121,7 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_ d.get_key_list(&keys); if (p_sort_keys) { - keys.sort(); + keys.sort_custom<StringLikeVariantOrder>(); } bool first_key = true; @@ -1402,7 +1402,7 @@ Error ResourceFormatSaverJSON::save(const Ref<Resource> &p_resource, const Strin Error err; Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); - ERR_FAIL_COND_V_MSG(err, err, "Cannot save json '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err, err, vformat("Cannot save json '%s'.", p_path)); file->store_string(source); if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 67469de5cc..c4d11b8a32 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -698,9 +698,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (str == "script" && value.get_type() != Variant::NIL) { ERR_FAIL_COND_V_MSG(value.get_type() != Variant::STRING, ERR_INVALID_DATA, "Invalid value for \"script\" property, expected script path as String."); String path = value; - ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'."); + ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, vformat("Invalid script path: '%s'.", path)); Ref<Script> script = ResourceLoader::load(path, "Script"); - ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'."); + ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, vformat("Can't load script at path: '%s'.", path)); obj->set_script(script); } else { obj->set(str, value); @@ -820,9 +820,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int builtin_type = Variant::OBJECT; if (p_allow_objects) { - ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'."); + ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, vformat("Invalid script path: '%s'.", path)); script = ResourceLoader::load(path, "Script"); - ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'."); + ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, vformat("Can't load script at path: '%s'.", path)); class_name = script->get_instance_base_type(); } else { class_name = EncodedObjectAsID::get_class_static(); diff --git a/core/io/missing_resource.cpp b/core/io/missing_resource.cpp index c78195bc46..1c15cc7dd3 100644 --- a/core/io/missing_resource.cpp +++ b/core/io/missing_resource.cpp @@ -74,6 +74,10 @@ bool MissingResource::is_recording_properties() const { return recording_properties; } +String MissingResource::get_save_class() const { + return original_class; +} + void MissingResource::_bind_methods() { ClassDB::bind_method(D_METHOD("set_original_class", "name"), &MissingResource::set_original_class); ClassDB::bind_method(D_METHOD("get_original_class"), &MissingResource::get_original_class); diff --git a/core/io/missing_resource.h b/core/io/missing_resource.h index f32d818ccb..4cded5ca24 100644 --- a/core/io/missing_resource.h +++ b/core/io/missing_resource.h @@ -57,6 +57,8 @@ public: void set_recording_properties(bool p_enable); bool is_recording_properties() const; + virtual String get_save_class() const override; + MissingResource(); }; diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 0329ace313..614f81c42a 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -299,7 +299,7 @@ Ref<StreamPeer> PacketPeerStream::get_stream_peer() const { void PacketPeerStream::set_input_buffer_max_size(int p_max_size) { ERR_FAIL_COND_MSG(p_max_size < 0, "Max size of input buffer size cannot be smaller than 0."); - //warning may lose packets + // WARNING: May lose packets. ERR_FAIL_COND_MSG(ring_buffer.data_left(), "Buffer in use, resizing would cause loss of data."); ring_buffer.resize(nearest_shift(next_power_of_2(p_max_size + 4)) - 1); input_buffer.resize(next_power_of_2(p_max_size + 4)); diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 93179d9a11..8ccf74261f 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -84,7 +84,7 @@ Error PCKPacker::pck_start(const String &p_pck_path, int p_alignment, const Stri enc_dir = p_encrypt_directory; file = FileAccess::open(p_pck_path, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(file.is_null(), ERR_CANT_CREATE, "Can't open file to write: " + String(p_pck_path) + "."); + ERR_FAIL_COND_V_MSG(file.is_null(), ERR_CANT_CREATE, vformat("Can't open file to write: '%s'.", String(p_pck_path))); alignment = p_alignment; diff --git a/core/io/plist.cpp b/core/io/plist.cpp index 8d91e6dec2..32e83c31f2 100644 --- a/core/io/plist.cpp +++ b/core/io/plist.cpp @@ -450,7 +450,7 @@ PList::PList() { PList::PList(const String &p_string) { String err_str; bool ok = load_string(p_string, err_str); - ERR_FAIL_COND_MSG(!ok, "PList: " + err_str); + ERR_FAIL_COND_MSG(!ok, vformat("PList: %s.", err_str)); } uint64_t PList::read_bplist_var_size_int(Ref<FileAccess> p_file, uint8_t p_size) { diff --git a/core/io/remote_filesystem_client.cpp b/core/io/remote_filesystem_client.cpp index 1198810441..c3f9a0016c 100644 --- a/core/io/remote_filesystem_client.cpp +++ b/core/io/remote_filesystem_client.cpp @@ -96,7 +96,7 @@ Error RemoteFilesystemClient::_store_file(const String &p_path, const LocalVecto } Ref<FileAccess> f = FileAccess::open(full_path, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, "Unable to open file for writing to remote filesystem cache: " + p_path); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, vformat("Unable to open file for writing to remote filesystem cache: '%s'.", p_path)); f->store_buffer(p_file.ptr(), p_file.size()); Error err = f->get_error(); if (err) { @@ -115,10 +115,10 @@ Error RemoteFilesystemClient::_store_cache_file(const Vector<FileCache> &p_cache String full_path = cache_path.path_join(FILES_CACHE_FILE); String base_file_dir = full_path.get_base_dir(); Error err = DirAccess::make_dir_recursive_absolute(base_file_dir); - ERR_FAIL_COND_V_MSG(err != OK && err != ERR_ALREADY_EXISTS, err, "Unable to create base directory to store cache file: " + base_file_dir); + ERR_FAIL_COND_V_MSG(err != OK && err != ERR_ALREADY_EXISTS, err, vformat("Unable to create base directory to store cache file: '%s'.", base_file_dir)); Ref<FileAccess> f = FileAccess::open(full_path, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, "Unable to open the remote cache file for writing: " + full_path); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, vformat("Unable to open the remote cache file for writing: '%s'.", full_path)); f->store_line(itos(FILESYSTEM_CACHE_VERSION)); for (int i = 0; i < p_cache.size(); i++) { String l = p_cache[i].path + "::" + itos(p_cache[i].server_modified_time) + "::" + itos(p_cache[i].modified_time); @@ -151,10 +151,10 @@ Error RemoteFilesystemClient::_synchronize_with_server(const String &p_host, int tcp_client.instantiate(); IPAddress ip = p_host.is_valid_ip_address() ? IPAddress(p_host) : IP::get_singleton()->resolve_hostname(p_host); - ERR_FAIL_COND_V_MSG(!ip.is_valid(), ERR_INVALID_PARAMETER, "Unable to resolve remote filesystem server hostname: " + p_host); + ERR_FAIL_COND_V_MSG(!ip.is_valid(), ERR_INVALID_PARAMETER, vformat("Unable to resolve remote filesystem server hostname: '%s'.", p_host)); print_verbose(vformat("Remote Filesystem: Connecting to host %s, port %d.", ip, p_port)); Error err = tcp_client->connect_to_host(ip, p_port); - ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to open connection to remote file server (" + String(p_host) + ", port " + itos(p_port) + ") failed."); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Unable to open connection to remote file server (%s, port %d) failed.", String(p_host), p_port)); while (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTING) { tcp_client->poll(); @@ -162,7 +162,7 @@ Error RemoteFilesystemClient::_synchronize_with_server(const String &p_host, int } if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) { - ERR_FAIL_V_MSG(ERR_CANT_CONNECT, "Connection to remote file server (" + String(p_host) + ", port " + itos(p_port) + ") failed."); + ERR_FAIL_V_MSG(ERR_CANT_CONNECT, vformat("Connection to remote file server (%s, port %d) failed.", String(p_host), p_port)); } // Connection OK, now send the current file state. @@ -280,7 +280,7 @@ Error RemoteFilesystemClient::_synchronize_with_server(const String &p_host, int err = tcp_client->get_data(file_buffer.ptr(), file_size); if (err != OK) { - ERR_PRINT("Error retrieving file from remote filesystem: " + file); + ERR_PRINT(vformat("Error retrieving file from remote filesystem: '%s'.", file)); server_disconnected = true; } diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 5f8a4b85a4..c65484b6c6 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -76,7 +76,7 @@ void Resource::set_path(const String &p_path, bool p_take_over) { existing->path_cache = String(); ResourceCache::resources.erase(p_path); } else { - ERR_FAIL_MSG("Another resource is loaded from path '" + p_path + "' (possible cyclic resource inclusion)."); + ERR_FAIL_MSG(vformat("Another resource is loaded from path '%s' (possible cyclic resource inclusion).", p_path)); } } @@ -99,31 +99,42 @@ void Resource::set_path_cache(const String &p_path) { GDVIRTUAL_CALL(_set_path_cache, p_path); } +static thread_local RandomPCG unique_id_gen(0, RandomPCG::DEFAULT_INC); + +void Resource::seed_scene_unique_id(uint32_t p_seed) { + unique_id_gen.seed(p_seed); +} + String Resource::generate_scene_unique_id() { // Generate a unique enough hash, but still user-readable. // If it's not unique it does not matter because the saver will try again. - OS::DateTime dt = OS::get_singleton()->get_datetime(); - uint32_t hash = hash_murmur3_one_32(OS::get_singleton()->get_ticks_usec()); - hash = hash_murmur3_one_32(dt.year, hash); - hash = hash_murmur3_one_32(dt.month, hash); - hash = hash_murmur3_one_32(dt.day, hash); - hash = hash_murmur3_one_32(dt.hour, hash); - hash = hash_murmur3_one_32(dt.minute, hash); - hash = hash_murmur3_one_32(dt.second, hash); - hash = hash_murmur3_one_32(Math::rand(), hash); + if (unique_id_gen.get_seed() == 0) { + OS::DateTime dt = OS::get_singleton()->get_datetime(); + uint32_t hash = hash_murmur3_one_32(OS::get_singleton()->get_ticks_usec()); + hash = hash_murmur3_one_32(dt.year, hash); + hash = hash_murmur3_one_32(dt.month, hash); + hash = hash_murmur3_one_32(dt.day, hash); + hash = hash_murmur3_one_32(dt.hour, hash); + hash = hash_murmur3_one_32(dt.minute, hash); + hash = hash_murmur3_one_32(dt.second, hash); + hash = hash_murmur3_one_32(Math::rand(), hash); + unique_id_gen.seed(hash); + } + + uint32_t random_num = unique_id_gen.rand(); static constexpr uint32_t characters = 5; static constexpr uint32_t char_count = ('z' - 'a'); static constexpr uint32_t base = char_count + ('9' - '0'); String id; for (uint32_t i = 0; i < characters; i++) { - uint32_t c = hash % base; + uint32_t c = random_num % base; if (c < char_count) { id += String::chr('a' + c); } else { id += String::chr('0' + (c - char_count)); } - hash /= base; + random_num /= base; } return id; diff --git a/core/io/resource.h b/core/io/resource.h index 8966c0233c..015f7ad197 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -114,6 +114,7 @@ public: virtual void set_path_cache(const String &p_path); // Set raw path without involving resource cache. _FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.contains("::") || path_cache.begins_with("local://"); } + static void seed_scene_unique_id(uint32_t p_seed); static String generate_scene_unique_id(); void set_scene_unique_id(const String &p_id); String get_scene_unique_id() const; diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index b4826c356e..e6136603d4 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -411,7 +411,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { //always use internal cache for loading internal resources if (!internal_index_cache.has(path)) { - WARN_PRINT(String("Couldn't load resource (no cache): " + path).utf8().get_data()); + WARN_PRINT(vformat("Couldn't load resource (no cache): %s.", path)); r_v = Variant(); } else { r_v = internal_index_cache[path]; @@ -435,7 +435,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { Ref<Resource> res = ResourceLoader::load(path, exttype, cache_mode_for_external); if (res.is_null()) { - WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data()); + WARN_PRINT(vformat("Couldn't load resource: %s.", path)); } r_v = res; @@ -458,7 +458,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { ResourceLoader::notify_dependency_error(local_path, external_resources[erindex].path, external_resources[erindex].type); } else { error = ERR_FILE_MISSING_DEPENDENCIES; - ERR_FAIL_V_MSG(error, "Can't load dependency: " + external_resources[erindex].path + "."); + ERR_FAIL_V_MSG(error, vformat("Can't load dependency: '%s'.", external_resources[erindex].path)); } } } else { @@ -704,7 +704,7 @@ Error ResourceLoaderBinary::load() { ResourceLoader::notify_dependency_error(local_path, path, external_resources[i].type); } else { error = ERR_FILE_MISSING_DEPENDENCIES; - ERR_FAIL_V_MSG(error, "Can't load dependency: " + path + "."); + ERR_FAIL_V_MSG(error, vformat("Can't load dependency: '%s'.", path)); } } } @@ -780,7 +780,7 @@ Error ResourceLoaderBinary::load() { obj = missing_resource; } else { error = ERR_FILE_CORRUPT; - ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource of unrecognized type in file: " + t + "."); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, vformat("'%s': Resource of unrecognized type in file: '%s'.", local_path, t)); } } @@ -789,7 +789,7 @@ Error ResourceLoaderBinary::load() { String obj_class = obj->get_class(); error = ERR_FILE_CORRUPT; memdelete(obj); //bye - ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource type in resource field not a resource, type is: " + obj_class + "."); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, vformat("'%s': Resource type in resource field not a resource, type is: %s.", local_path, obj_class)); } res = Ref<Resource>(r); @@ -833,7 +833,7 @@ Error ResourceLoaderBinary::load() { } bool set_valid = true; - if (value.get_type() == Variant::OBJECT && missing_resource != nullptr) { + if (value.get_type() == Variant::OBJECT && missing_resource == nullptr && ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) { // If the property being set is a missing resource (and the parent is not), // then setting it will most likely not work. // Instead, save it as metadata. @@ -845,29 +845,27 @@ Error ResourceLoaderBinary::load() { } } - if (ClassDB::has_property(res->get_class_name(), name)) { - if (value.get_type() == Variant::ARRAY) { - Array set_array = value; - bool is_get_valid = false; - Variant get_value = res->get(name, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::ARRAY) { - Array get_array = get_value; - if (!set_array.is_same_typed(get_array)) { - value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); - } + if (value.get_type() == Variant::ARRAY) { + Array set_array = value; + bool is_get_valid = false; + Variant get_value = res->get(name, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::ARRAY) { + Array get_array = get_value; + if (!set_array.is_same_typed(get_array)) { + value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); } } + } - if (value.get_type() == Variant::DICTIONARY) { - Dictionary set_dict = value; - bool is_get_valid = false; - Variant get_value = res->get(name, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { - Dictionary get_dict = get_value; - if (!set_dict.is_same_typed(get_dict)) { - value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), - get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); - } + if (value.get_type() == Variant::DICTIONARY) { + Dictionary set_dict = value; + bool is_get_valid = false; + Variant get_value = res->get(name, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { + Dictionary get_dict = get_value; + if (!set_dict.is_same_typed(get_dict)) { + value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), + get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); } } } @@ -1001,7 +999,7 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p error = fac->open_after_magic(f); if (error != OK) { f.unref(); - ERR_FAIL_MSG("Failed to open binary resource file: " + local_path + "."); + ERR_FAIL_MSG(vformat("Failed to open binary resource file: '%s'.", local_path)); } f = fac; @@ -1009,7 +1007,7 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p // Not normal. error = ERR_FILE_UNRECOGNIZED; f.unref(); - ERR_FAIL_MSG("Unrecognized binary resource file: " + local_path + "."); + ERR_FAIL_MSG(vformat("Unrecognized binary resource file: '%s'.", local_path)); } bool big_endian = f->get_32(); @@ -1095,10 +1093,10 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p #ifdef TOOLS_ENABLED // Silence a warning that can happen during the initial filesystem scan due to cache being regenerated. if (ResourceLoader::get_resource_uid(res_path) != er.uid) { - WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data()); + WARN_PRINT(vformat("'%s': In external resource #%d, invalid UID: '%s' - using text path instead: '%s'.", res_path, i, ResourceUID::get_singleton()->id_to_text(er.uid), er.path)); } #else - WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data()); + WARN_PRINT(vformat("'%s': In external resource #%d, invalid UID: '%s' - using text path instead: '%s'.", res_path, i, ResourceUID::get_singleton()->id_to_text(er.uid), er.path)); #endif } } @@ -1122,7 +1120,7 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p if (f->eof_reached()) { error = ERR_FILE_CORRUPT; f.unref(); - ERR_FAIL_MSG("Premature end of file (EOF): " + local_path + "."); + ERR_FAIL_MSG(vformat("Premature end of file (EOF): '%s'.", local_path)); } } @@ -1226,7 +1224,7 @@ Ref<Resource> ResourceFormatLoaderBinary::load(const String &p_path, const Strin Error err; Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); - ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot open file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), vformat("Cannot open file '%s'.", p_path)); ResourceLoaderBinary loader; switch (p_cache_mode) { @@ -1270,6 +1268,11 @@ void ResourceFormatLoaderBinary::get_recognized_extensions_for_type(const String return; } + // res files not supported for GDExtension. + if (p_type == "GDExtension") { + return; + } + List<String> extensions; ClassDB::get_extensions_for_type(p_type, &extensions); @@ -1298,7 +1301,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) { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file '" + p_path + "'."); + ERR_FAIL_COND_MSG(f.is_null(), vformat("Cannot open file '%s'.", p_path)); ResourceLoaderBinary loader; loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); @@ -1308,7 +1311,7 @@ void ResourceFormatLoaderBinary::get_dependencies(const String &p_path, List<Str Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, const HashMap<String, String> &p_map) { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, "Cannot open file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Cannot open file '%s'.", p_path)); Ref<FileAccess> fw; @@ -1321,23 +1324,23 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons Ref<FileAccessCompressed> fac; fac.instantiate(); Error err = fac->open_after_magic(f); - ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot open file '%s'.", p_path)); f = fac; Ref<FileAccessCompressed> facw; facw.instantiate(); facw->configure("RSCC"); err = facw->open_internal(p_path + ".depren", FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Cannot create file '" + p_path + ".depren'."); + ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, vformat("Cannot create file '%s.depren'.", p_path)); fw = facw; } else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') { // Not normal. - ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unrecognized binary resource file '" + local_path + "'."); + ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, vformat("Unrecognized binary resource file '%s'.", local_path)); } else { fw = FileAccess::open(p_path + ".depren", FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(fw.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + ".depren'."); + ERR_FAIL_COND_V_MSG(fw.is_null(), ERR_CANT_CREATE, vformat("Cannot create file '%s.depren'.", p_path)); uint8_t magic[4] = { 'R', 'S', 'R', 'C' }; fw->store_buffer(magic, 4); @@ -1369,12 +1372,12 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons // Use the old approach. - WARN_PRINT("This file is old, so it can't refactor dependencies, opening and resaving '" + p_path + "'."); + WARN_PRINT(vformat("This file is old, so it can't refactor dependencies, opening and resaving '%s'.", p_path)); Error err; f = FileAccess::open(p_path, FileAccess::READ, &err); - ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_OPEN, "Cannot open file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_OPEN, vformat("Cannot open file '%s'.", p_path)); ResourceLoaderBinary loader; loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); @@ -1520,7 +1523,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons void ResourceFormatLoaderBinary::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file '" + p_path + "'."); + ERR_FAIL_COND_MSG(f.is_null(), vformat("Cannot open file '%s'.", p_path)); ResourceLoaderBinary loader; loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); @@ -2024,7 +2027,7 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant if (!p_main && (!bundle_resources) && !res->is_built_in()) { if (res->get_path() == path) { - ERR_PRINT("Circular reference to resource being saved found: '" + local_path + "' will be null next time it's loaded."); + ERR_PRINT(vformat("Circular reference to resource being saved found: '%s' will be null next time it's loaded.", local_path)); return; } int idx = external_resources.size(); @@ -2136,6 +2139,8 @@ static String _resource_get_class(Ref<Resource> p_resource) { } Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) { + Resource::seed_scene_unique_id(p_path.hash()); + Error err; Ref<FileAccess> f; if (p_flags & ResourceSaver::FLAG_COMPRESS) { @@ -2148,7 +2153,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re f = FileAccess::open(p_path, FileAccess::WRITE, &err); } - ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot create file '%s'.", p_path)); relative_paths = p_flags & ResourceSaver::FLAG_RELATIVE_PATHS; skip_editor = p_flags & ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES; @@ -2220,10 +2225,10 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re List<ResourceData> resources; - Dictionary missing_resource_properties = p_resource->get_meta(META_MISSING_RESOURCES, Dictionary()); - { for (const Ref<Resource> &E : saved_resources) { + Dictionary missing_resource_properties = E->get_meta(META_MISSING_RESOURCES, Dictionary()); + ResourceData &rd = resources.push_back(ResourceData())->get(); rd.type = _resource_get_class(E); @@ -2238,7 +2243,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re continue; } - if ((F.usage & PROPERTY_USAGE_STORAGE)) { + if ((F.usage & PROPERTY_USAGE_STORAGE) || missing_resource_properties.has(F.name)) { Property p; p.name_idx = get_string_index(F.name); @@ -2253,7 +2258,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re p.value = E->get(F.name); } - if (p.pi.type == Variant::OBJECT && missing_resource_properties.has(F.name)) { + if (F.type == Variant::OBJECT && missing_resource_properties.has(F.name)) { // Was this missing resource overridden? If so do not save the old value. Ref<Resource> res = p.value; if (res.is_null()) { @@ -2379,7 +2384,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re Error ResourceFormatSaverBinaryInstance::set_uid(const String &p_path, ResourceUID::ID p_uid) { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, "Cannot open file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Cannot open file '%s'.", p_path)); Ref<FileAccess> fw; @@ -2392,14 +2397,14 @@ Error ResourceFormatSaverBinaryInstance::set_uid(const String &p_path, ResourceU Ref<FileAccessCompressed> fac; fac.instantiate(); Error err = fac->open_after_magic(f); - ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot open file '%s'.", p_path)); f = fac; Ref<FileAccessCompressed> facw; facw.instantiate(); facw->configure("RSCC"); err = facw->open_internal(p_path + ".uidren", FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Cannot create file '" + p_path + ".uidren'."); + ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, vformat("Cannot create file '%s.uidren'.", p_path)); fw = facw; @@ -2408,7 +2413,7 @@ Error ResourceFormatSaverBinaryInstance::set_uid(const String &p_path, ResourceU return ERR_FILE_UNRECOGNIZED; } else { fw = FileAccess::open(p_path + ".uidren", FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(fw.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + ".uidren'."); + ERR_FAIL_COND_V_MSG(fw.is_null(), ERR_CANT_CREATE, vformat("Cannot create file '%s.uidren'.", p_path)); uint8_t magich[4] = { 'R', 'S', 'R', 'C' }; fw->store_buffer(magich, 4); @@ -2439,7 +2444,7 @@ Error ResourceFormatSaverBinaryInstance::set_uid(const String &p_path, ResourceU // Use the old approach. - WARN_PRINT("This file is old, so it does not support UIDs, opening and resaving '" + p_path + "'."); + WARN_PRINT(vformat("This file is old, so it does not support UIDs, opening and resaving '%s'.", p_path)); return ERR_UNAVAILABLE; } diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index a572dd562e..e603f9dfde 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -75,7 +75,7 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy if (err == ERR_FILE_EOF) { return OK; } else if (err != OK) { - ERR_PRINT("ResourceFormatImporter::load - " + p_path + ".import:" + itos(lines) + " error: " + error_text); + ERR_PRINT(vformat("ResourceFormatImporter::load - %s.import:%d error: %s.", p_path, lines, error_text)); return err; } @@ -335,7 +335,7 @@ void ResourceFormatImporter::get_internal_resource_path_list(const String &p_pat if (err == ERR_FILE_EOF) { return; } else if (err != OK) { - ERR_PRINT("ResourceFormatImporter::get_internal_resource_path_list - " + p_path + ".import:" + itos(lines) + " error: " + error_text); + ERR_PRINT(vformat("ResourceFormatImporter::get_internal_resource_path_list - %s.import:%d error: %s.", p_path, lines, error_text)); return; } @@ -507,7 +507,7 @@ bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) con for (int i = 0; i < importers.size(); i++) { if (importers[i]->get_importer_name() == pat.importer) { - if (!importers[i]->are_import_settings_valid(p_path)) { //importer thinks this is not valid + if (!importers[i]->are_import_settings_valid(p_path, pat.metadata)) { //importer thinks this is not valid return false; } } diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index 6ea5d0972a..3ca8f7c05d 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -148,12 +148,12 @@ public: virtual String get_option_group_file() const { return String(); } virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) = 0; - virtual bool can_import_threaded() const { return true; } + virtual bool can_import_threaded() const { return false; } virtual void import_threaded_begin() {} virtual void import_threaded_end() {} virtual Error import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) { return ERR_UNAVAILABLE; } - virtual bool are_import_settings_valid(const String &p_path) const { return true; } + virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const { return true; } virtual String get_import_settings_string() const { return String(); } }; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index f2ed5c3b14..59de2879e2 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -162,7 +162,7 @@ Ref<Resource> ResourceFormatLoader::load(const String &p_path, const String &p_o } } - ERR_FAIL_V_MSG(Ref<Resource>(), "Failed to load resource '" + p_path + "'. ResourceFormatLoader::load was not implemented for this resource type."); + ERR_FAIL_V_MSG(Ref<Resource>(), vformat("Failed to load resource '%s'. ResourceFormatLoader::load was not implemented for this resource type.", p_path)); } void ResourceFormatLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { @@ -1172,7 +1172,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem // An extra remap may still be necessary afterwards due to the text -> binary converter on export. String locale = TranslationServer::get_singleton()->get_locale(); - ERR_FAIL_COND_V_MSG(locale.length() < 2, p_path, "Could not remap path '" + p_path + "' for translation as configured locale '" + locale + "' is invalid."); + ERR_FAIL_COND_V_MSG(locale.length() < 2, p_path, vformat("Could not remap path '%s' for translation as configured locale '%s' is invalid.", p_path, locale)); Vector<String> &res_remaps = *translation_remaps.getptr(new_path); @@ -1231,7 +1231,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem if (err == ERR_FILE_EOF) { break; } else if (err != OK) { - ERR_PRINT("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text + "."); + ERR_PRINT(vformat("Parse error: %s.remap:%d error: %s.", p_path, lines, error_text)); break; } diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index caaf9f8f45..0d802ed1f4 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -222,7 +222,7 @@ public: static ThreadLoadStatus load_threaded_get_status(const String &p_path, float *r_progress = nullptr); static Ref<Resource> load_threaded_get(const String &p_path, Error *r_error = nullptr); - static bool is_within_load() { return load_nesting > 0; }; + static bool is_within_load() { return load_nesting > 0; } static void resource_changed_connect(Resource *p_source, const Callable &p_callable, uint32_t p_flags); static void resource_changed_disconnect(Resource *p_source, const Callable &p_callable); diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 1dc1245355..d49037dbe0 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -98,7 +98,7 @@ void ResourceFormatSaver::_bind_methods() { } Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) { - ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, "Can't save empty resource to path '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, vformat("Can't save empty resource to path '%s'.", p_path)); String path = p_path; if (path.is_empty()) { path = p_resource->get_path(); diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 578cd91c52..7a11d06df6 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -31,7 +31,6 @@ #include "translation_loader_po.h" #include "core/io/file_access.h" -#include "core/string/translation.h" #include "core/string/translation_po.h" Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_error) { @@ -170,14 +169,14 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_ // If we reached last line and it's not a content line, break, otherwise let processing that last loop if (is_eof && l.is_empty()) { if (status == STATUS_READING_ID || status == STATUS_READING_CONTEXT || (status == STATUS_READING_PLURAL && plural_index != plural_forms - 1)) { - ERR_FAIL_V_MSG(Ref<Resource>(), "Unexpected EOF while reading PO file at: " + path + ":" + itos(line)); + ERR_FAIL_V_MSG(Ref<Resource>(), vformat("Unexpected EOF while reading PO file at: %s:%d.", path, line)); } else { break; } } if (l.begins_with("msgctxt")) { - ERR_FAIL_COND_V_MSG(status != STATUS_READING_STRING && status != STATUS_READING_PLURAL, Ref<Resource>(), "Unexpected 'msgctxt', was expecting 'msgid_plural' or 'msgstr' before 'msgctxt' while parsing: " + path + ":" + itos(line)); + ERR_FAIL_COND_V_MSG(status != STATUS_READING_STRING && status != STATUS_READING_PLURAL, Ref<Resource>(), vformat("Unexpected 'msgctxt', was expecting 'msgid_plural' or 'msgstr' before 'msgctxt' while parsing: %s:%d.", path, line)); // In PO file, "msgctxt" appears before "msgid". If we encounter a "msgctxt", we add what we have read // and set "entered_context" to true to prevent adding twice. @@ -185,7 +184,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_ if (status == STATUS_READING_STRING) { translation->add_message(msg_id, msg_str, msg_context); } else if (status == STATUS_READING_PLURAL) { - ERR_FAIL_COND_V_MSG(plural_index != plural_forms - 1, Ref<Resource>(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); + ERR_FAIL_COND_V_MSG(plural_index != plural_forms - 1, Ref<Resource>(), vformat("Number of 'msgstr[]' doesn't match with number of plural forms: %s:%d.", path, line)); translation->add_plural_message(msg_id, msgs_plural, msg_context); } } @@ -197,9 +196,9 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_ if (l.begins_with("msgid_plural")) { if (plural_forms == 0) { - ERR_FAIL_V_MSG(Ref<Resource>(), "PO file uses 'msgid_plural' but 'Plural-Forms' is invalid or missing in header: " + path + ":" + itos(line)); + ERR_FAIL_V_MSG(Ref<Resource>(), vformat("PO file uses 'msgid_plural' but 'Plural-Forms' is invalid or missing in header: %s:%d.", path, line)); } else if (status != STATUS_READING_ID) { - ERR_FAIL_V_MSG(Ref<Resource>(), "Unexpected 'msgid_plural', was expecting 'msgid' before 'msgid_plural' while parsing: " + path + ":" + itos(line)); + ERR_FAIL_V_MSG(Ref<Resource>(), vformat("Unexpected 'msgid_plural', was expecting 'msgid' before 'msgid_plural' while parsing: %s:%d.", path, line)); } // We don't record the message in "msgid_plural" itself as tr_n(), TTRN(), RTRN() interfaces provide the plural string already. // We just have to reset variables related to plurals for "msgstr[]" later on. @@ -209,14 +208,14 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_ msgs_plural.resize(plural_forms); status = STATUS_READING_PLURAL; } else if (l.begins_with("msgid")) { - ERR_FAIL_COND_V_MSG(status == STATUS_READING_ID, Ref<Resource>(), "Unexpected 'msgid', was expecting 'msgstr' while parsing: " + path + ":" + itos(line)); + ERR_FAIL_COND_V_MSG(status == STATUS_READING_ID, Ref<Resource>(), vformat("Unexpected 'msgid', was expecting 'msgstr' while parsing: %s:%d.", path, line)); if (!msg_id.is_empty()) { if (!skip_this && !entered_context) { if (status == STATUS_READING_STRING) { translation->add_message(msg_id, msg_str, msg_context); } else if (status == STATUS_READING_PLURAL) { - ERR_FAIL_COND_V_MSG(plural_index != plural_forms - 1, Ref<Resource>(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); + ERR_FAIL_COND_V_MSG(plural_index != plural_forms - 1, Ref<Resource>(), vformat("Number of 'msgstr[]' doesn't match with number of plural forms: %s:%d.", path, line)); translation->add_plural_message(msg_id, msgs_plural, msg_context); } } @@ -245,11 +244,11 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_ } if (l.begins_with("msgstr[")) { - ERR_FAIL_COND_V_MSG(status != STATUS_READING_PLURAL, Ref<Resource>(), "Unexpected 'msgstr[]', was expecting 'msgid_plural' before 'msgstr[]' while parsing: " + path + ":" + itos(line)); + ERR_FAIL_COND_V_MSG(status != STATUS_READING_PLURAL, Ref<Resource>(), vformat("Unexpected 'msgstr[]', was expecting 'msgid_plural' before 'msgstr[]' while parsing: %s:%d.", path, line)); plural_index++; // Increment to add to the next slot in vector msgs_plural. l = l.substr(9, l.length()).strip_edges(); } else if (l.begins_with("msgstr")) { - ERR_FAIL_COND_V_MSG(status != STATUS_READING_ID, Ref<Resource>(), "Unexpected 'msgstr', was expecting 'msgid' before 'msgstr' while parsing: " + path + ":" + itos(line)); + ERR_FAIL_COND_V_MSG(status != STATUS_READING_ID, Ref<Resource>(), vformat("Unexpected 'msgstr', was expecting 'msgid' before 'msgstr' while parsing: %s:%d.", path, line)); l = l.substr(6, l.length()).strip_edges(); status = STATUS_READING_STRING; } @@ -262,7 +261,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_ continue; // Nothing to read or comment. } - ERR_FAIL_COND_V_MSG(!l.begins_with("\"") || status == STATUS_NONE, Ref<Resource>(), "Invalid line '" + l + "' while parsing: " + path + ":" + itos(line)); + ERR_FAIL_COND_V_MSG(!l.begins_with("\"") || status == STATUS_NONE, Ref<Resource>(), vformat("Invalid line '%s' while parsing: %s:%d.", l, path, line)); l = l.substr(1, l.length()); // Find final quote, ignoring escaped ones (\"). @@ -284,7 +283,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_ escape_next = false; } - ERR_FAIL_COND_V_MSG(end_pos == -1, Ref<Resource>(), "Expected '\"' at end of message while parsing: " + path + ":" + itos(line)); + ERR_FAIL_COND_V_MSG(end_pos == -1, Ref<Resource>(), vformat("Expected '\"' at end of message while parsing: %s:%d.", path, line)); l = l.substr(0, end_pos); l = l.c_unescape(); @@ -296,7 +295,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_ } else if (status == STATUS_READING_CONTEXT) { msg_context += l; } else if (status == STATUS_READING_PLURAL && plural_index >= 0) { - ERR_FAIL_COND_V_MSG(plural_index >= plural_forms, Ref<Resource>(), "Unexpected plural form while parsing: " + path + ":" + itos(line)); + ERR_FAIL_COND_V_MSG(plural_index >= plural_forms, Ref<Resource>(), vformat("Unexpected plural form while parsing: %s:%d.", path, line)); msgs_plural.write[plural_index] = msgs_plural[plural_index] + l; } @@ -314,13 +313,13 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_ } } else if (status == STATUS_READING_PLURAL) { if (!skip_this && !msg_id.is_empty()) { - ERR_FAIL_COND_V_MSG(plural_index != plural_forms - 1, Ref<Resource>(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); + ERR_FAIL_COND_V_MSG(plural_index != plural_forms - 1, Ref<Resource>(), vformat("Number of 'msgstr[]' doesn't match with number of plural forms: %s:%d.", path, line)); translation->add_plural_message(msg_id, msgs_plural, msg_context); } } } - ERR_FAIL_COND_V_MSG(config.is_empty(), Ref<Resource>(), "No config found in file: " + path + "."); + ERR_FAIL_COND_V_MSG(config.is_empty(), Ref<Resource>(), vformat("No config found in file: '%s'.", path)); Vector<String> configs = config.split("\n"); for (int i = 0; i < configs.size(); i++) { @@ -350,7 +349,7 @@ Ref<Resource> TranslationLoaderPO::load(const String &p_path, const String &p_or } Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(f.is_null(), Ref<Resource>(), "Cannot open file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(f.is_null(), Ref<Resource>(), vformat("Cannot open file '%s'.", p_path)); return load_translation(f, r_error); } @@ -361,7 +360,7 @@ void TranslationLoaderPO::get_recognized_extensions(List<String> *p_extensions) } bool TranslationLoaderPO::handles_type(const String &p_type) const { - return (p_type == "Translation"); + return (p_type == "Translation") || (p_type == "TranslationPO"); } String TranslationLoaderPO::get_resource_type(const String &p_path) const { diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index 06888c7cda..6923ca1269 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -429,7 +429,7 @@ String XMLParser::get_named_attribute_value(const String &p_name) const { } } - ERR_FAIL_COND_V_MSG(idx < 0, "", "Attribute not found: " + p_name + "."); + ERR_FAIL_COND_V_MSG(idx < 0, "", vformat("Attribute not found: '%s'.", p_name)); return attributes[idx].value; } @@ -493,7 +493,7 @@ Error XMLParser::open(const String &p_path) { Error err; Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err); - ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot open file '%s'.", p_path)); length = file->get_length(); ERR_FAIL_COND_V(length < 1, ERR_FILE_CORRUPT); diff --git a/core/math/SCsub b/core/math/SCsub index c8fdac207e..6ea3ab6b12 100644 --- a/core/math/SCsub +++ b/core/math/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/math/basis.h b/core/math/basis.h index 236d666103..2d4994de19 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -223,7 +223,7 @@ struct [[nodiscard]] Basis { static Basis looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0), bool p_use_model_front = false); - Basis(const Quaternion &p_quaternion) { set_quaternion(p_quaternion); }; + Basis(const Quaternion &p_quaternion) { set_quaternion(p_quaternion); } Basis(const Quaternion &p_quaternion, const Vector3 &p_scale) { set_quaternion_scale(p_quaternion, p_scale); } Basis(const Vector3 &p_axis, real_t p_angle) { set_axis_angle(p_axis, p_angle); } diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp index 80662c1b07..620a7541e4 100644 --- a/core/math/convex_hull.cpp +++ b/core/math/convex_hull.cpp @@ -204,7 +204,7 @@ public: static Int128 mul(uint64_t a, uint64_t b); Int128 operator-() const { - return Int128((uint64_t) - (int64_t)low, ~high + (low == 0)); + return Int128(uint64_t(-int64_t(low)), ~high + (low == 0)); } Int128 operator+(const Int128 &b) const { diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 0692ece1e6..35303fe9ac 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -1491,7 +1491,7 @@ Error Expression::parse(const String &p_expression, const Vector<String> &p_inpu } Variant Expression::execute(const Array &p_inputs, Object *p_base, bool p_show_error, bool p_const_calls_only) { - ERR_FAIL_COND_V_MSG(error_set, Variant(), "There was previously a parse error: " + error_str + "."); + ERR_FAIL_COND_V_MSG(error_set, Variant(), vformat("There was previously a parse error: %s.", error_str)); execution_error = false; Variant output; diff --git a/core/math/geometry_2d.cpp b/core/math/geometry_2d.cpp index d60619b27f..376d5d0b43 100644 --- a/core/math/geometry_2d.cpp +++ b/core/math/geometry_2d.cpp @@ -35,7 +35,8 @@ #define STB_RECT_PACK_IMPLEMENTATION #include "thirdparty/misc/stb_rect_pack.h" -#define PRECISION 5 // Based on CMP_EPSILON. +const int clipper_precision = 5; // Based on CMP_EPSILON. +const double clipper_scale = Math::pow(10.0, clipper_precision); Vector<Vector<Vector2>> Geometry2D::decompose_polygon_in_convex(const Vector<Point2> &polygon) { Vector<Vector<Vector2>> decomp; @@ -75,7 +76,7 @@ struct _AtlasWorkRect { Size2i s; Point2i p; int idx = 0; - _FORCE_INLINE_ bool operator<(const _AtlasWorkRect &p_r) const { return s.width > p_r.s.width; }; + _FORCE_INLINE_ bool operator<(const _AtlasWorkRect &p_r) const { return s.width > p_r.s.width; } }; struct _AtlasWorkRectResult { @@ -224,7 +225,7 @@ Vector<Vector<Point2>> Geometry2D::_polypaths_do_operation(PolyBooleanOperation path_b[i] = PointD(p_polypath_b[i].x, p_polypath_b[i].y); } - ClipperD clp(PRECISION); // Scale points up internally to attain the desired precision. + ClipperD clp(clipper_precision); // Scale points up internally to attain the desired precision. clp.PreserveCollinear(false); // Remove redundant vertices. if (is_a_open) { clp.AddOpenSubject({ path_a }); @@ -298,9 +299,10 @@ Vector<Vector<Point2>> Geometry2D::_polypath_offset(const Vector<Point2> &p_poly } // Inflate/deflate. - PathsD paths = InflatePaths({ polypath }, p_delta, jt, et, 2.0, PRECISION, 0.0); - // Here the miter_limit = 2.0 and arc_tolerance = 0.0 are Clipper2 defaults, - // and the PRECISION is used to scale points up internally, to attain the desired precision. + PathsD paths = InflatePaths({ polypath }, p_delta, jt, et, 2.0, clipper_precision, 0.25 * clipper_scale); + // Here the points are scaled up internally and + // the arc_tolerance is scaled accordingly + // to attain the desired precision. Vector<Vector<Point2>> polypaths; for (PathsD::size_type i = 0; i < paths.size(); ++i) { diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h index 83ebdc5a84..abd395d8df 100644 --- a/core/math/geometry_2d.h +++ b/core/math/geometry_2d.h @@ -451,17 +451,17 @@ public: return H; } - static Vector<Point2i> bresenham_line(const Point2i &p_start, const Point2i &p_end) { + static Vector<Point2i> bresenham_line(const Point2i &p_from, const Point2i &p_to) { Vector<Point2i> points; - Vector2i delta = (p_end - p_start).abs() * 2; - Vector2i step = (p_end - p_start).sign(); - Vector2i current = p_start; + Vector2i delta = (p_to - p_from).abs() * 2; + Vector2i step = (p_to - p_from).sign(); + Vector2i current = p_from; if (delta.x > delta.y) { int err = delta.x / 2; - for (; current.x != p_end.x; current.x += step.x) { + for (; current.x != p_to.x; current.x += step.x) { points.push_back(current); err -= delta.y; @@ -473,7 +473,7 @@ public: } else { int err = delta.y / 2; - for (; current.y != p_end.y; current.y += step.y) { + for (; current.y != p_to.y; current.y += step.y) { points.push_back(current); err -= delta.x; diff --git a/core/math/plane.h b/core/math/plane.h index 6529fea60a..65783ff4cf 100644 --- a/core/math/plane.h +++ b/core/math/plane.h @@ -40,7 +40,7 @@ struct [[nodiscard]] Plane { real_t d = 0; void set_normal(const Vector3 &p_normal); - _FORCE_INLINE_ Vector3 get_normal() const { return normal; }; + _FORCE_INLINE_ Vector3 get_normal() const { return normal; } void normalize(); Plane normalized() const; diff --git a/core/math/rect2.cpp b/core/math/rect2.cpp index c55226a57e..7f77b0786c 100644 --- a/core/math/rect2.cpp +++ b/core/math/rect2.cpp @@ -283,7 +283,7 @@ next4: } Rect2::operator String() const { - return "[P: " + position.operator String() + ", S: " + size + "]"; + return "[P: " + position.operator String() + ", S: " + size.operator String() + "]"; } Rect2::operator Rect2i() const { diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index 476577508f..1ee7d3d84f 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -39,16 +39,19 @@ class String; struct [[nodiscard]] Transform2D { - // Warning #1: basis of Transform2D is stored differently from Basis. In terms of columns array, the basis matrix looks like "on paper": + // WARNING: The basis of Transform2D is stored differently from Basis. + // In terms of columns array, the basis matrix looks like "on paper": // M = (columns[0][0] columns[1][0]) // (columns[0][1] columns[1][1]) - // This is such that the columns, which can be interpreted as basis vectors of the coordinate system "painted" on the object, can be accessed as columns[i]. - // Note that this is the opposite of the indices in mathematical texts, meaning: $M_{12}$ in a math book corresponds to columns[1][0] here. + // This is such that the columns, which can be interpreted as basis vectors + // of the coordinate system "painted" on the object, can be accessed as columns[i]. + // NOTE: This is the opposite of the indices in mathematical texts, + // meaning: $M_{12}$ in a math book corresponds to columns[1][0] here. // This requires additional care when working with explicit indices. // See https://en.wikipedia.org/wiki/Row-_and_column-major_order for further reading. - // Warning #2: 2D be aware that unlike 3D code, 2D code uses a left-handed coordinate system: Y-axis points down, - // and angle is measure from +X to +Y in a clockwise-fashion. + // WARNING: Be aware that unlike 3D code, 2D code uses a left-handed coordinate system: + // Y-axis points down, and angle is measure from +X to +Y in a clockwise-fashion. Vector2 columns[3]; diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index e86b97d6a8..0590ee8a37 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -203,7 +203,7 @@ bool Vector2::is_finite() const { } Vector2::operator String() const { - return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")"; + return "(" + String::num_real(x, true) + ", " + String::num_real(y, true) + ")"; } Vector2::operator Vector2i() const { diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index 1e90002665..e18ac3b011 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -165,7 +165,7 @@ bool Vector3::is_finite() const { } Vector3::operator String() const { - return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ")"; + return "(" + String::num_real(x, true) + ", " + String::num_real(y, true) + ", " + String::num_real(z, true) + ")"; } Vector3::operator Vector3i() const { diff --git a/core/math/vector4.h b/core/math/vector4.h index 8632f69f57..9197e3587a 100644 --- a/core/math/vector4.h +++ b/core/math/vector4.h @@ -55,16 +55,16 @@ struct [[nodiscard]] Vector4 { real_t z; real_t w; }; - real_t components[4] = { 0, 0, 0, 0 }; + real_t coord[4] = { 0, 0, 0, 0 }; }; _FORCE_INLINE_ real_t &operator[](int p_axis) { DEV_ASSERT((unsigned int)p_axis < 4); - return components[p_axis]; + return coord[p_axis]; } _FORCE_INLINE_ const real_t &operator[](int p_axis) const { DEV_ASSERT((unsigned int)p_axis < 4); - return components[p_axis]; + return coord[p_axis]; } Vector4::Axis min_axis_index() const; diff --git a/core/object/SCsub b/core/object/SCsub index 7c00bb719e..3d0d2c14dd 100644 --- a/core/object/SCsub +++ b/core/object/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h index 1b29e1778a..86c66593bd 100644 --- a/core/object/callable_method_pointer.h +++ b/core/object/callable_method_pointer.h @@ -37,6 +37,8 @@ #include "core/variant/binder_common.h" #include "core/variant/callable.h" +#include <type_traits> + class CallableCustomMethodPointerBase : public CallableCustom { uint32_t *comp_ptr = nullptr; uint32_t comp_size; @@ -77,12 +79,13 @@ public: virtual uint32_t hash() const; }; -template <typename T, typename... P> +template <typename T, typename R, typename... P> class CallableCustomMethodPointer : public CallableCustomMethodPointerBase { struct Data { T *instance; uint64_t object_id; - void (T::*method)(P...); + R(T::*method) + (P...); } data; public: @@ -100,10 +103,14 @@ public: virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); - call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error); + if constexpr (std::is_same<R, void>::value) { + call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error); + } else { + call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); + } } - CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) { + CallableCustomMethodPointer(T *p_instance, R (T::*p_method)(P...)) { memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; data.object_id = p_instance->get_instance_id(); @@ -118,7 +125,7 @@ Callable create_custom_callable_function_pointer(T *p_instance, const char *p_func_text, #endif void (T::*p_method)(P...)) { - typedef CallableCustomMethodPointer<T, P...> CCMP; // Messes with memnew otherwise. + typedef CallableCustomMethodPointer<T, void, P...> CCMP; // Messes with memnew otherwise. CCMP *ccmp = memnew(CCMP(p_instance, p_method)); #ifdef DEBUG_METHODS_ENABLED ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. @@ -126,51 +133,13 @@ Callable create_custom_callable_function_pointer(T *p_instance, return Callable(ccmp); } -// VERSION WITH RETURN - -template <typename T, typename R, typename... P> -class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase { - struct Data { - T *instance; - uint64_t object_id; - R(T::*method) - (P...); - } data; - -public: - virtual ObjectID get_object() const { - if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) { - return ObjectID(); - } - return data.instance->get_instance_id(); - } - - virtual int get_argument_count(bool &r_is_valid) const { - r_is_valid = true; - return sizeof...(P); - } - - virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { - ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); - call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); - } - - CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) { - memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. - data.instance = p_instance; - data.object_id = p_instance->get_instance_id(); - data.method = p_method; - _setup((uint32_t *)&data, sizeof(Data)); - } -}; - template <typename T, typename R, typename... P> Callable create_custom_callable_function_pointer(T *p_instance, #ifdef DEBUG_METHODS_ENABLED const char *p_func_text, #endif R (T::*p_method)(P...)) { - typedef CallableCustomMethodPointerRet<T, R, P...> CCMP; // Messes with memnew otherwise. + typedef CallableCustomMethodPointer<T, R, P...> CCMP; // Messes with memnew otherwise. CCMP *ccmp = memnew(CCMP(p_instance, p_method)); #ifdef DEBUG_METHODS_ENABLED ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. @@ -178,10 +147,10 @@ Callable create_custom_callable_function_pointer(T *p_instance, return Callable(ccmp); } -// CONST VERSION WITH RETURN +// CONST VERSION template <typename T, typename R, typename... P> -class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase { +class CallableCustomMethodPointerC : public CallableCustomMethodPointerBase { struct Data { T *instance; uint64_t object_id; @@ -204,10 +173,14 @@ public: virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override { ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); - call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); + if constexpr (std::is_same<R, void>::value) { + call_with_variant_argsc(data.instance, data.method, p_arguments, p_argcount, r_call_error); + } else { + call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); + } } - CallableCustomMethodPointerRetC(T *p_instance, R (T::*p_method)(P...) const) { + CallableCustomMethodPointerC(T *p_instance, R (T::*p_method)(P...) const) { memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; data.object_id = p_instance->get_instance_id(); @@ -216,13 +189,27 @@ public: } }; +template <typename T, typename... P> +Callable create_custom_callable_function_pointer(T *p_instance, +#ifdef DEBUG_METHODS_ENABLED + const char *p_func_text, +#endif + void (T::*p_method)(P...) const) { + typedef CallableCustomMethodPointerC<T, void, P...> CCMP; // Messes with memnew otherwise. + CCMP *ccmp = memnew(CCMP(p_instance, p_method)); +#ifdef DEBUG_METHODS_ENABLED + ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. +#endif + return Callable(ccmp); +} + template <typename T, typename R, typename... P> Callable create_custom_callable_function_pointer(T *p_instance, #ifdef DEBUG_METHODS_ENABLED const char *p_func_text, #endif R (T::*p_method)(P...) const) { - typedef CallableCustomMethodPointerRetC<T, R, P...> CCMP; // Messes with memnew otherwise. + typedef CallableCustomMethodPointerC<T, R, P...> CCMP; // Messes with memnew otherwise. CCMP *ccmp = memnew(CCMP(p_instance, p_method)); #ifdef DEBUG_METHODS_ENABLED ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. @@ -238,10 +225,11 @@ Callable create_custom_callable_function_pointer(T *p_instance, // STATIC VERSIONS -template <typename... P> +template <typename R, typename... P> class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase { struct Data { - void (*method)(P...); + R(*method) + (P...); } data; public: @@ -259,24 +247,27 @@ public: } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override { - call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); - r_return_value = Variant(); + if constexpr (std::is_same<R, void>::value) { + call_with_variant_args_static(data.method, p_arguments, p_argcount, r_call_error); + } else { + call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); + } } - CallableCustomStaticMethodPointer(void (*p_method)(P...)) { + CallableCustomStaticMethodPointer(R (*p_method)(P...)) { memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.method = p_method; _setup((uint32_t *)&data, sizeof(Data)); } }; -template <typename T, typename... P> +template <typename... P> Callable create_custom_callable_static_function_pointer( #ifdef DEBUG_METHODS_ENABLED const char *p_func_text, #endif void (*p_method)(P...)) { - typedef CallableCustomStaticMethodPointer<P...> CCMP; // Messes with memnew otherwise. + typedef CallableCustomStaticMethodPointer<void, P...> CCMP; // Messes with memnew otherwise. CCMP *ccmp = memnew(CCMP(p_method)); #ifdef DEBUG_METHODS_ENABLED ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. @@ -285,44 +276,12 @@ Callable create_custom_callable_static_function_pointer( } template <typename R, typename... P> -class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerBase { - struct Data { - R(*method) - (P...); - } data; - -public: - virtual bool is_valid() const override { - return true; - } - - virtual ObjectID get_object() const override { - return ObjectID(); - } - - virtual int get_argument_count(bool &r_is_valid) const override { - r_is_valid = true; - return sizeof...(P); - } - - virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override { - call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); - } - - CallableCustomStaticMethodPointerRet(R (*p_method)(P...)) { - memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. - data.method = p_method; - _setup((uint32_t *)&data, sizeof(Data)); - } -}; - -template <typename R, typename... P> Callable create_custom_callable_static_function_pointer( #ifdef DEBUG_METHODS_ENABLED const char *p_func_text, #endif R (*p_method)(P...)) { - typedef CallableCustomStaticMethodPointerRet<R, P...> CCMP; // Messes with memnew otherwise. + typedef CallableCustomStaticMethodPointer<R, P...> CCMP; // Messes with memnew otherwise. CCMP *ccmp = memnew(CCMP(p_method)); #ifdef DEBUG_METHODS_ENABLED ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 9826d73a9d..d48e1a3622 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -352,7 +352,7 @@ StringName ClassDB::get_compatibility_remapped_class(const StringName &p_class) StringName ClassDB::_get_parent_class(const StringName &p_class) { ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_NULL_V_MSG(ti, StringName(), "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_NULL_V_MSG(ti, StringName(), vformat("Cannot get class '%s'.", String(p_class))); return ti->inherits; } @@ -367,7 +367,7 @@ ClassDB::APIType ClassDB::get_api_type(const StringName &p_class) { ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_NULL_V_MSG(ti, API_NONE, "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_NULL_V_MSG(ti, API_NONE, vformat("Cannot get class '%s'.", String(p_class))); return ti->api; } @@ -390,7 +390,7 @@ uint32_t ClassDB::get_api_hash(APIType p_api) { for (const StringName &E : class_list) { ClassInfo *t = classes.getptr(E); - ERR_FAIL_NULL_V_MSG(t, 0, "Cannot get class '" + String(E) + "'."); + ERR_FAIL_NULL_V_MSG(t, 0, vformat("Cannot get class '%s'.", String(E))); if (t->api != p_api || !t->exposed) { continue; } @@ -547,14 +547,14 @@ Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require ti = classes.getptr(compat_classes[p_class]); } } - ERR_FAIL_NULL_V_MSG(ti, nullptr, "Cannot get class '" + String(p_class) + "'."); - ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled."); - ERR_FAIL_NULL_V_MSG(ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated."); + ERR_FAIL_NULL_V_MSG(ti, nullptr, vformat("Cannot get class '%s'.", String(p_class))); + ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, vformat("Class '%s' is disabled.", String(p_class))); + ERR_FAIL_NULL_V_MSG(ti->creation_func, nullptr, vformat("Class '%s' or its base class cannot be instantiated.", String(p_class))); } #ifdef TOOLS_ENABLED if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) { - ERR_PRINT("Class '" + String(p_class) + "' can only be instantiated by editor."); + ERR_PRINT(vformat("Class '%s' can only be instantiated by editor.", String(p_class))); return nullptr; } #endif @@ -653,8 +653,8 @@ ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class) ti = classes.getptr(compat_classes[p_class]); } } - ERR_FAIL_NULL_V_MSG(ti, nullptr, "Cannot get class '" + String(p_class) + "'."); - ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled."); + ERR_FAIL_NULL_V_MSG(ti, nullptr, vformat("Cannot get class '%s'.", String(p_class))); + ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, vformat("Class '%s' is disabled.", String(p_class))); } // Make a "fake" extension to act as a placeholder. @@ -734,9 +734,9 @@ void ClassDB::set_object_extension_instance(Object *p_object, const StringName & ti = classes.getptr(compat_classes[p_class]); } } - ERR_FAIL_NULL_MSG(ti, "Cannot get class '" + String(p_class) + "'."); - ERR_FAIL_COND_MSG(ti->disabled, "Class '" + String(p_class) + "' is disabled."); - ERR_FAIL_NULL_MSG(ti->gdextension, "Class '" + String(p_class) + "' has no native extension."); + ERR_FAIL_NULL_MSG(ti, vformat("Cannot get class '%s'.", String(p_class))); + ERR_FAIL_COND_MSG(ti->disabled, vformat("Class '%s' is disabled.", String(p_class))); + ERR_FAIL_NULL_MSG(ti->gdextension, vformat("Class '%s' has no native extension.", String(p_class))); } p_object->_extension = ti->gdextension; @@ -750,69 +750,87 @@ void ClassDB::set_object_extension_instance(Object *p_object, const StringName & } bool ClassDB::can_instantiate(const StringName &p_class) { - OBJTYPE_RLOCK; + String script_path; + { + OBJTYPE_RLOCK; - ClassInfo *ti = classes.getptr(p_class); - if (!ti) { - if (!ScriptServer::is_global_class(p_class)) { - ERR_FAIL_V_MSG(false, "Cannot get class '" + String(p_class) + "'."); + ClassInfo *ti = classes.getptr(p_class); + if (!ti) { + if (!ScriptServer::is_global_class(p_class)) { + ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class))); + } + script_path = ScriptServer::get_global_class_path(p_class); + goto use_script; // Open the lock for resource loading. } - String path = ScriptServer::get_global_class_path(p_class); - Ref<Script> scr = ResourceLoader::load(path); - return scr.is_valid() && scr->is_valid() && !scr->is_abstract(); - } #ifdef TOOLS_ENABLED - if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) { - return false; - } + if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) { + return false; + } #endif - return _can_instantiate(ti); + return _can_instantiate(ti); + } + +use_script: + Ref<Script> scr = ResourceLoader::load(script_path); + return scr.is_valid() && scr->is_valid() && !scr->is_abstract(); } bool ClassDB::is_abstract(const StringName &p_class) { - OBJTYPE_RLOCK; + String script_path; + { + OBJTYPE_RLOCK; - ClassInfo *ti = classes.getptr(p_class); - if (!ti) { - if (!ScriptServer::is_global_class(p_class)) { - ERR_FAIL_V_MSG(false, "Cannot get class '" + String(p_class) + "'."); + ClassInfo *ti = classes.getptr(p_class); + if (!ti) { + if (!ScriptServer::is_global_class(p_class)) { + ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class))); + } + script_path = ScriptServer::get_global_class_path(p_class); + goto use_script; // Open the lock for resource loading. } - String path = ScriptServer::get_global_class_path(p_class); - Ref<Script> scr = ResourceLoader::load(path); - return scr.is_valid() && scr->is_valid() && scr->is_abstract(); - } - if (ti->creation_func != nullptr) { - return false; - } - if (!ti->gdextension) { - return true; - } + if (ti->creation_func != nullptr) { + return false; + } + if (!ti->gdextension) { + return true; + } #ifndef DISABLE_DEPRECATED - return ti->gdextension->create_instance2 == nullptr && ti->gdextension->create_instance == nullptr; + return ti->gdextension->create_instance2 == nullptr && ti->gdextension->create_instance == nullptr; #else - return ti->gdextension->create_instance2 == nullptr; + return ti->gdextension->create_instance2 == nullptr; #endif // DISABLE_DEPRECATED + } + +use_script: + Ref<Script> scr = ResourceLoader::load(script_path); + return scr.is_valid() && scr->is_valid() && scr->is_abstract(); } bool ClassDB::is_virtual(const StringName &p_class) { - OBJTYPE_RLOCK; + String script_path; + { + OBJTYPE_RLOCK; - ClassInfo *ti = classes.getptr(p_class); - if (!ti) { - if (!ScriptServer::is_global_class(p_class)) { - ERR_FAIL_V_MSG(false, "Cannot get class '" + String(p_class) + "'."); + ClassInfo *ti = classes.getptr(p_class); + if (!ti) { + if (!ScriptServer::is_global_class(p_class)) { + ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class))); + } + script_path = ScriptServer::get_global_class_path(p_class); + goto use_script; // Open the lock for resource loading. } - String path = ScriptServer::get_global_class_path(p_class); - Ref<Script> scr = ResourceLoader::load(path); - return scr.is_valid() && scr->is_valid() && scr->is_abstract(); - } #ifdef TOOLS_ENABLED - if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) { - return false; - } + if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) { + return false; + } #endif - return (_can_instantiate(ti) && ti->is_virtual); + return (_can_instantiate(ti) && ti->is_virtual); + } + +use_script: + Ref<Script> scr = ResourceLoader::load(script_path); + return scr.is_valid() && scr->is_valid() && scr->is_abstract(); } void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) { @@ -820,7 +838,7 @@ void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherit const StringName &name = p_class; - ERR_FAIL_COND_MSG(classes.has(name), "Class '" + String(p_class) + "' already exists."); + ERR_FAIL_COND_MSG(classes.has(name), vformat("Class '%s' already exists.", String(p_class))); classes[name] = ClassInfo(); ClassInfo &ti = classes[name]; @@ -1328,7 +1346,7 @@ void ClassDB::add_signal(const 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), "Class '" + String(p_class) + "' already has signal '" + String(sname) + "'."); + ERR_FAIL_COND_MSG(check->signal_map.has(sname), vformat("Class '%s' already has signal '%s'.", String(p_class), String(sname))); check = check->inherits_ptr; } #endif @@ -1442,10 +1460,10 @@ void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinf mb_set = get_method(p_class, p_setter); #ifdef DEBUG_METHODS_ENABLED - ERR_FAIL_NULL_MSG(mb_set, "Invalid setter '" + p_class + "::" + p_setter + "' for property '" + p_pinfo.name + "'."); + ERR_FAIL_NULL_MSG(mb_set, vformat("Invalid setter '%s::%s' for property '%s'.", p_class, p_setter, 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, vformat("Invalid function for setter '%s::%s' for property '%s'.", p_class, p_setter, p_pinfo.name)); #endif } @@ -1454,15 +1472,15 @@ void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinf mb_get = get_method(p_class, p_getter); #ifdef DEBUG_METHODS_ENABLED - ERR_FAIL_NULL_MSG(mb_get, "Invalid getter '" + p_class + "::" + p_getter + "' for property '" + p_pinfo.name + "'."); + ERR_FAIL_NULL_MSG(mb_get, vformat("Invalid getter '%s::%s' for property '%s'.", p_class, p_getter, 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, vformat("Invalid function for getter '%s::%s' for property '%s'.", p_class, p_getter, 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), vformat("Object '%s' already has property '%s'.", p_class, p_pinfo.name)); #endif OBJTYPE_WLOCK @@ -1847,7 +1865,7 @@ void ClassDB::_bind_method_custom(const StringName &p_class, MethodBind *p_metho ClassInfo *type = classes.getptr(p_class); if (!type) { - ERR_FAIL_MSG("Couldn't bind custom method '" + p_method->get_name() + "' for instance '" + p_class + "'."); + ERR_FAIL_MSG(vformat("Couldn't bind custom method '%s' for instance '%s'.", p_method->get_name(), p_class)); } if (p_compatibility) { @@ -1857,7 +1875,7 @@ void ClassDB::_bind_method_custom(const StringName &p_class, MethodBind *p_metho if (type->method_map.has(p_method->get_name())) { // overloading not supported - ERR_FAIL_MSG("Method already bound '" + p_class + "::" + p_method->get_name() + "'."); + ERR_FAIL_MSG(vformat("Method already bound '%s::%s'.", p_class, p_method->get_name())); } #ifdef DEBUG_METHODS_ENABLED @@ -1888,7 +1906,7 @@ MethodBind *ClassDB::_bind_vararg_method(MethodBind *p_bind, const StringName &p if (type->method_map.has(p_name)) { memdelete(bind); // Overloading not supported - ERR_FAIL_V_MSG(nullptr, "Method already bound: " + instance_type + "::" + p_name + "."); + ERR_FAIL_V_MSG(nullptr, vformat("Method already bound: '%s::%s'.", instance_type, p_name)); } type->method_map[p_name] = bind; #ifdef DEBUG_METHODS_ENABLED @@ -1916,26 +1934,26 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, bool p_ #ifdef DEBUG_ENABLED - ERR_FAIL_COND_V_MSG(!p_compatibility && has_method(instance_type, mdname), nullptr, "Class " + String(instance_type) + " already has a method " + String(mdname) + "."); + ERR_FAIL_COND_V_MSG(!p_compatibility && has_method(instance_type, mdname), nullptr, vformat("Class '%s' already has a method '%s'.", String(instance_type), String(mdname))); #endif ClassInfo *type = classes.getptr(instance_type); if (!type) { memdelete(p_bind); - ERR_FAIL_V_MSG(nullptr, "Couldn't bind method '" + mdname + "' for instance '" + instance_type + "'."); + ERR_FAIL_V_MSG(nullptr, vformat("Couldn't bind method '%s' for instance '%s'.", mdname, instance_type)); } if (!p_compatibility && type->method_map.has(mdname)) { memdelete(p_bind); // overloading not supported - ERR_FAIL_V_MSG(nullptr, "Method already bound '" + instance_type + "::" + mdname + "'."); + ERR_FAIL_V_MSG(nullptr, vformat("Method already bound '%s::%s'.", instance_type, mdname)); } #ifdef DEBUG_METHODS_ENABLED if (method_name.args.size() > p_bind->get_argument_count()) { memdelete(p_bind); - ERR_FAIL_V_MSG(nullptr, "Method definition provides more arguments than the method actually has '" + instance_type + "::" + mdname + "'."); + ERR_FAIL_V_MSG(nullptr, vformat("Method definition provides more arguments than the method actually has '%s::%s'.", instance_type, mdname)); } p_bind->set_argument_names(method_name.args); @@ -1964,7 +1982,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, bool p_ } void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual, const Vector<String> &p_arg_names, bool p_object_core) { - ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); + ERR_FAIL_COND_MSG(!classes.has(p_class), vformat("Request for nonexistent class '%s'.", p_class)); OBJTYPE_WLOCK; @@ -1979,7 +1997,7 @@ void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_ if (!p_object_core) { if (p_arg_names.size() != mi.arguments.size()) { - WARN_PRINT("Mismatch argument name count for virtual method: " + String(p_class) + "::" + p_method.name); + WARN_PRINT(vformat("Mismatch argument name count for virtual method: '%s::%s'.", String(p_class), p_method.name)); } else { List<PropertyInfo>::Iterator itr = mi.arguments.begin(); for (int i = 0; i < p_arg_names.size(); ++itr, ++i) { @@ -1990,7 +2008,7 @@ void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_ if (classes[p_class].virtual_methods_map.has(p_method.name)) { // overloading not supported - ERR_FAIL_MSG("Virtual method already bound '" + String(p_class) + "::" + p_method.name + "'."); + ERR_FAIL_MSG(vformat("Virtual method already bound '%s::%s'.", String(p_class), p_method.name)); } classes[p_class].virtual_methods.push_back(mi); classes[p_class].virtual_methods_map[p_method.name] = mi; @@ -1999,7 +2017,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_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); + ERR_FAIL_COND_MSG(!classes.has(p_class), vformat("Request for nonexistent class '%s'.", p_class)); #ifdef DEBUG_METHODS_ENABLED @@ -2020,7 +2038,7 @@ void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p } void ClassDB::add_extension_class_virtual_method(const StringName &p_class, const GDExtensionClassVirtualMethodInfo *p_method_info) { - ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); + ERR_FAIL_COND_MSG(!classes.has(p_class), vformat("Request for nonexistent class '%s'.", p_class)); #ifdef DEBUG_METHODS_ENABLED PackedStringArray arg_names; @@ -2044,7 +2062,7 @@ void ClassDB::add_extension_class_virtual_method(const StringName &p_class, cons void ClassDB::set_class_enabled(const StringName &p_class, bool p_enable) { OBJTYPE_WLOCK; - ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); + ERR_FAIL_COND_MSG(!classes.has(p_class), vformat("Request for nonexistent class '%s'.", p_class)); classes[p_class].disabled = !p_enable; } @@ -2058,7 +2076,7 @@ bool ClassDB::is_class_enabled(const StringName &p_class) { } } - ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_NULL_V_MSG(ti, false, vformat("Cannot get class '%s'.", String(p_class))); return !ti->disabled; } @@ -2066,7 +2084,7 @@ bool ClassDB::is_class_exposed(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_NULL_V_MSG(ti, false, vformat("Cannot get class '%s'.", String(p_class))); return ti->exposed; } @@ -2074,7 +2092,7 @@ bool ClassDB::is_class_reloadable(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_NULL_V_MSG(ti, false, vformat("Cannot get class '%s'.", String(p_class))); return ti->reloadable; } @@ -2082,7 +2100,7 @@ bool ClassDB::is_class_runtime(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_NULL_V_MSG(ti, false, vformat("Cannot get class '%s'.", String(p_class))); return ti->is_runtime; } @@ -2192,14 +2210,14 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con void ClassDB::register_extension_class(ObjectGDExtension *p_extension) { GLOBAL_LOCK_FUNCTION; - ERR_FAIL_COND_MSG(classes.has(p_extension->class_name), "Class already registered: " + String(p_extension->class_name)); - ERR_FAIL_COND_MSG(!classes.has(p_extension->parent_class_name), "Parent class name for extension class not found: " + String(p_extension->parent_class_name)); + ERR_FAIL_COND_MSG(classes.has(p_extension->class_name), vformat("Class already registered: '%s'.", String(p_extension->class_name))); + ERR_FAIL_COND_MSG(!classes.has(p_extension->parent_class_name), vformat("Parent class name for extension class not found: '%s'.", String(p_extension->parent_class_name))); ClassInfo *parent = classes.getptr(p_extension->parent_class_name); #ifdef TOOLS_ENABLED // @todo This is a limitation of the current implementation, but it should be possible to remove. - ERR_FAIL_COND_MSG(p_extension->is_runtime && parent->gdextension && !parent->is_runtime, "Extension runtime class " + String(p_extension->class_name) + " cannot descend from " + parent->name + " which isn't also a runtime class"); + ERR_FAIL_COND_MSG(p_extension->is_runtime && parent->gdextension && !parent->is_runtime, vformat("Extension runtime class '%s' cannot descend from '%s' which isn't also a runtime class.", String(p_extension->class_name), parent->name)); #endif ClassInfo c; @@ -2215,7 +2233,7 @@ void ClassDB::register_extension_class(ObjectGDExtension *p_extension) { concrete_ancestor->gdextension != nullptr) { concrete_ancestor = concrete_ancestor->inherits_ptr; } - ERR_FAIL_NULL_MSG(concrete_ancestor->creation_func, "Extension class " + String(p_extension->class_name) + " cannot extend native abstract class " + String(concrete_ancestor->name)); + ERR_FAIL_NULL_MSG(concrete_ancestor->creation_func, vformat("Extension class '%s' cannot extend native abstract class '%s'.", String(p_extension->class_name), String(concrete_ancestor->name))); c.creation_func = concrete_ancestor->creation_func; } c.inherits = parent->name; @@ -2239,7 +2257,7 @@ void ClassDB::register_extension_class(ObjectGDExtension *p_extension) { void ClassDB::unregister_extension_class(const StringName &p_class, bool p_free_method_binds) { ClassInfo *c = classes.getptr(p_class); - ERR_FAIL_NULL_MSG(c, "Class '" + String(p_class) + "' does not exist."); + ERR_FAIL_NULL_MSG(c, vformat("Class '%s' does not exist.", String(p_class))); if (p_free_method_binds) { for (KeyValue<StringName, MethodBind *> &F : c->method_map) { memdelete(F.value); diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py index 7a85753072..3df26e3b73 100644 --- a/core/object/make_virtuals.py +++ b/core/object/make_virtuals.py @@ -2,7 +2,6 @@ proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\ StringName _gdvirtual_##m_name##_sn = #m_name;\\ mutable bool _gdvirtual_##m_name##_initialized = false;\\ mutable void *_gdvirtual_##m_name = nullptr;\\ - template <bool required>\\ _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST {\\ ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\ if (_script_instance) {\\ @@ -36,10 +35,8 @@ proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\ }\\ return true;\\ }\\ - if (required) {\\ - ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\ - $RVOID\\ - }\\ + $REQCHECK\\ + $RVOID\\ return false;\\ }\\ _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const {\\ @@ -73,10 +70,11 @@ proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\ """ -def generate_version(argcount, const=False, returns=False): +def generate_version(argcount, const=False, returns=False, required=False): s = proto sproto = str(argcount) method_info = "" + method_flags = "METHOD_FLAG_VIRTUAL" if returns: sproto += "R" s = s.replace("$RET", "m_ret,") @@ -86,17 +84,27 @@ def generate_version(argcount, const=False, returns=False): method_info += "\t\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;" else: s = s.replace("$RET ", "") - s = s.replace("\t\t\t$RVOID\\\n", "") + s = s.replace("\t\t$RVOID\\\n", "") s = s.replace("\t\t\t$CALLPTRRETDEF\\\n", "") if const: sproto += "C" + method_flags += " | METHOD_FLAG_CONST" s = s.replace("$CONST", "const") - s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL | METHOD_FLAG_CONST") else: s = s.replace("$CONST ", "") - s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL") + if required: + sproto += "_REQUIRED" + method_flags += " | METHOD_FLAG_VIRTUAL_REQUIRED" + s = s.replace( + "$REQCHECK", + 'ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");', + ) + else: + s = s.replace("\t\t$REQCHECK\\\n", "") + + s = s.replace("$METHOD_FLAGS", method_flags) s = s.replace("$VER", sproto) argtext = "" callargtext = "" @@ -198,6 +206,10 @@ def run(target, source, env): txt += generate_version(i, False, True) txt += generate_version(i, True, False) txt += generate_version(i, True, True) + txt += generate_version(i, False, False, True) + txt += generate_version(i, False, True, True) + txt += generate_version(i, True, False, True) + txt += generate_version(i, True, True, True) txt += "#endif // GDVIRTUAL_GEN_H\n" diff --git a/core/object/message_queue.h b/core/object/message_queue.h index 673eb3845b..64e244bda8 100644 --- a/core/object/message_queue.h +++ b/core/object/message_queue.h @@ -153,7 +153,7 @@ public: bool is_flushing() const; int get_max_buffer_usage() const; - CallQueue(Allocator *p_custom_allocator = 0, uint32_t p_max_pages = 8192, const String &p_error_text = String()); + CallQueue(Allocator *p_custom_allocator = nullptr, uint32_t p_max_pages = 8192, const String &p_error_text = String()); virtual ~CallQueue(); }; diff --git a/core/object/method_bind.h b/core/object/method_bind.h index 2f9a2d1679..e06eb1f8fa 100644 --- a/core/object/method_bind.h +++ b/core/object/method_bind.h @@ -109,7 +109,7 @@ public: _FORCE_INLINE_ StringName get_instance_class() const { return instance_class; } _FORCE_INLINE_ void set_instance_class(const StringName &p_class) { instance_class = p_class; } - _FORCE_INLINE_ int get_argument_count() const { return argument_count; }; + _FORCE_INLINE_ int get_argument_count() const { return argument_count; } #ifdef TOOLS_ENABLED virtual bool is_valid() const { return true; } diff --git a/core/object/object.cpp b/core/object/object.cpp index b3a4ec6e2e..ef1ca8132c 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -750,7 +750,7 @@ Variant Object::callv(const StringName &p_method, const Array &p_args) { Callable::CallError ce; const Variant ret = callp(p_method, argptrs, p_args.size(), ce); if (ce.error != Callable::CallError::CALL_OK) { - ERR_FAIL_V_MSG(Variant(), "Error calling method from 'callv': " + Variant::get_call_error_text(this, p_method, argptrs, p_args.size(), ce) + "."); + ERR_FAIL_V_MSG(Variant(), vformat("Error calling method from 'callv': %s.", Variant::get_call_error_text(this, p_method, argptrs, p_args.size(), ce))); } return ret; } @@ -999,7 +999,7 @@ void Object::set_meta(const StringName &p_name, const Variant &p_value) { if (E) { E->value = p_value; } else { - ERR_FAIL_COND_MSG(!p_name.operator String().is_valid_ascii_identifier(), "Invalid metadata identifier: '" + p_name + "'."); + ERR_FAIL_COND_MSG(!p_name.operator String().is_valid_ascii_identifier(), vformat("Invalid metadata identifier: '%s'.", p_name)); Variant *V = &metadata.insert(p_name, p_value)->value; const String &sname = p_name; @@ -1015,7 +1015,7 @@ Variant Object::get_meta(const StringName &p_name, const Variant &p_default) con if (p_default != Variant()) { return p_default; } else { - ERR_FAIL_V_MSG(Variant(), "The object does not have any 'meta' values with the key '" + p_name + "'."); + ERR_FAIL_V_MSG(Variant(), vformat("The object does not have any 'meta' values with the key '%s'.", p_name)); } } return metadata[p_name]; @@ -1071,8 +1071,8 @@ void Object::get_meta_list(List<StringName> *p_list) const { void Object::add_user_signal(const MethodInfo &p_signal) { ERR_FAIL_COND_MSG(p_signal.name.is_empty(), "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 + "'."); + ERR_FAIL_COND_MSG(ClassDB::has_signal(get_class_name(), p_signal.name), vformat("User signal's name conflicts with a built-in signal of '%s'.", get_class_name())); + ERR_FAIL_COND_MSG(signal_map.has(p_signal.name), vformat("Trying to add already existing signal '%s'.", p_signal.name)); SignalData s; s.user = p_signal; signal_map[p_signal.name] = s; @@ -1137,7 +1137,7 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int #ifdef DEBUG_ENABLED bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_name); //check in script - ERR_FAIL_COND_V_MSG(!signal_is_valid && !script.is_null() && !Ref<Script>(script)->has_script_signal(p_name), ERR_UNAVAILABLE, "Can't emit non-existing signal " + String("\"") + p_name + "\"."); + ERR_FAIL_COND_V_MSG(!signal_is_valid && !script.is_null() && !Ref<Script>(script)->has_script_signal(p_name), ERR_UNAVAILABLE, vformat("Can't emit non-existing signal \"%s\".", p_name)); #endif //not connected? just return return ERR_UNAVAILABLE; @@ -1210,7 +1210,7 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && target && !ClassDB::class_exists(target->get_class_name())) { //most likely object is not initialized yet, do not throw error. } else { - ERR_PRINT("Error calling from signal '" + String(p_name) + "' to callable: " + Variant::get_callable_error_text(callable, args, argc, ce) + "."); + ERR_PRINT(vformat("Error calling from signal '%s' to callable: %s.", String(p_name), Variant::get_callable_error_text(callable, args, argc, ce))); err = ERR_METHOD_NOT_FOUND; } } @@ -1371,15 +1371,15 @@ void Object::get_signals_connected_to_this(List<Connection> *p_connections) cons } Error Object::connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags) { - ERR_FAIL_COND_V_MSG(p_callable.is_null(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is null."); + ERR_FAIL_COND_V_MSG(p_callable.is_null(), ERR_INVALID_PARAMETER, vformat("Cannot connect to '%s': the provided callable is null.", p_signal)); if (p_callable.is_standard()) { // FIXME: This branch should probably removed in favor of the `is_valid()` branch, but there exist some classes // that call `connect()` before they are fully registered with ClassDB. Until all such classes can be found // and registered soon enough this branch is needed to allow `connect()` to succeed. - ERR_FAIL_NULL_V_MSG(p_callable.get_object(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "' to callable '" + p_callable + "': the callable object is null."); + ERR_FAIL_NULL_V_MSG(p_callable.get_object(), ERR_INVALID_PARAMETER, vformat("Cannot connect to '%s' to callable '%s': the callable object is null.", p_signal, p_callable)); } else { - ERR_FAIL_COND_V_MSG(!p_callable.is_valid(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is not valid: " + p_callable); + ERR_FAIL_COND_V_MSG(!p_callable.is_valid(), ERR_INVALID_PARAMETER, vformat("Cannot connect to '%s': the provided callable is not valid: '%s'.", p_signal, p_callable)); } SignalData *s = signal_map.getptr(p_signal); @@ -1400,7 +1400,7 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui #endif } - ERR_FAIL_COND_V_MSG(!signal_is_valid, ERR_INVALID_PARAMETER, "In Object of type '" + String(get_class()) + "': Attempt to connect nonexistent signal '" + p_signal + "' to callable '" + p_callable + "'."); + ERR_FAIL_COND_V_MSG(!signal_is_valid, ERR_INVALID_PARAMETER, vformat("In Object of type '%s': Attempt to connect nonexistent signal '%s' to callable '%s'.", String(get_class()), p_signal, p_callable)); signal_map[p_signal] = SignalData(); s = &signal_map[p_signal]; @@ -1412,7 +1412,7 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui s->slot_map[*p_callable.get_base_comparator()].reference_count++; return OK; } else { - ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Signal '" + p_signal + "' is already connected to given callable '" + p_callable + "' in that object."); + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("Signal '%s' is already connected to given callable '%s' in that object.", p_signal, p_callable)); } } @@ -1439,7 +1439,7 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui } bool Object::is_connected(const StringName &p_signal, const Callable &p_callable) const { - ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, "Cannot determine if connected to '" + p_signal + "': the provided callable is null."); // Should use `is_null`, see note in `connect` about the use of `is_valid`. + ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, vformat("Cannot determine if connected to '%s': the provided callable is null.", p_signal)); // Should use `is_null`, see note in `connect` about the use of `is_valid`. const SignalData *s = signal_map.getptr(p_signal); if (!s) { bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal); @@ -1451,7 +1451,7 @@ bool Object::is_connected(const StringName &p_signal, const Callable &p_callable return false; } - ERR_FAIL_V_MSG(false, "Nonexistent signal: " + p_signal + "."); + ERR_FAIL_V_MSG(false, vformat("Nonexistent signal: '%s'.", p_signal)); } return s->slot_map.has(*p_callable.get_base_comparator()); @@ -1469,7 +1469,7 @@ bool Object::has_connections(const StringName &p_signal) const { return false; } - ERR_FAIL_V_MSG(false, "Nonexistent signal: " + p_signal + "."); + ERR_FAIL_V_MSG(false, vformat("Nonexistent signal: '%s'.", p_signal)); } return !s->slot_map.is_empty(); @@ -1480,17 +1480,17 @@ void Object::disconnect(const StringName &p_signal, const Callable &p_callable) } bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) { - ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, "Cannot disconnect from '" + p_signal + "': the provided callable is null."); // Should use `is_null`, see note in `connect` about the use of `is_valid`. + ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, vformat("Cannot disconnect from '%s': the provided callable is null.", p_signal)); // Should use `is_null`, see note in `connect` about the use of `is_valid`. SignalData *s = signal_map.getptr(p_signal); if (!s) { bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal) || (!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal)); - ERR_FAIL_COND_V_MSG(signal_is_valid, false, "Attempt to disconnect a nonexistent connection from '" + to_string() + "'. Signal: '" + p_signal + "', callable: '" + p_callable + "'."); + ERR_FAIL_COND_V_MSG(signal_is_valid, false, vformat("Attempt to disconnect a nonexistent connection from '%s'. Signal: '%s', callable: '%s'.", to_string(), p_signal, p_callable)); } - ERR_FAIL_NULL_V_MSG(s, false, vformat("Disconnecting nonexistent signal '%s' in %s.", p_signal, to_string())); + ERR_FAIL_NULL_V_MSG(s, false, vformat("Disconnecting nonexistent signal '%s' in '%s'.", p_signal, to_string())); - ERR_FAIL_COND_V_MSG(!s->slot_map.has(*p_callable.get_base_comparator()), false, "Attempt to disconnect a nonexistent connection from '" + to_string() + "'. Signal: '" + p_signal + "', callable: '" + p_callable + "'."); + ERR_FAIL_COND_V_MSG(!s->slot_map.has(*p_callable.get_base_comparator()), false, vformat("Attempt to disconnect a nonexistent connection from '%s'. Signal: '%s', callable: '%s'.", to_string(), p_signal, p_callable)); SignalData::Slot *slot = &s->slot_map[*p_callable.get_base_comparator()]; @@ -2128,7 +2128,7 @@ Object::~Object() { if (_emitting) { //@todo this may need to actually reach the debugger prioritarily somehow because it may crash before - ERR_PRINT("Object " + to_string() + " was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes."); + ERR_PRINT(vformat("Object '%s' was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes.", to_string())); } // Drop all connections to the signals of this object. diff --git a/core/object/object.h b/core/object/object.h index 763e2974b9..8f93b75bd8 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -87,6 +87,7 @@ enum PropertyHint { PROPERTY_HINT_PASSWORD, PROPERTY_HINT_LAYERS_AVOIDANCE, PROPERTY_HINT_DICTIONARY_TYPE, + PROPERTY_HINT_TOOL_BUTTON, PROPERTY_HINT_MAX, }; @@ -215,6 +216,7 @@ enum MethodFlags { METHOD_FLAG_VARARG = 16, METHOD_FLAG_STATIC = 32, METHOD_FLAG_OBJECT_CORE = 64, + METHOD_FLAG_VIRTUAL_REQUIRED = 128, METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL, }; @@ -368,11 +370,8 @@ struct ObjectGDExtension { #endif }; -#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call<false>(__VA_ARGS__) -#define GDVIRTUAL_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call<false>(__VA_ARGS__) - -#define GDVIRTUAL_REQUIRED_CALL(m_name, ...) _gdvirtual_##m_name##_call<true>(__VA_ARGS__) -#define GDVIRTUAL_REQUIRED_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call<true>(__VA_ARGS__) +#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call(__VA_ARGS__) +#define GDVIRTUAL_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call(__VA_ARGS__) #ifdef DEBUG_METHODS_ENABLED #define GDVIRTUAL_BIND(m_name, ...) ::ClassDB::add_virtual_method(get_class_static(), _gdvirtual_##m_name##_get_method_info(), true, sarray(__VA_ARGS__)); @@ -686,22 +685,22 @@ protected: _ALWAYS_INLINE_ const ObjectGDExtension *_get_extension() const { return _extension; } _ALWAYS_INLINE_ GDExtensionClassInstancePtr _get_extension_instance() const { return _extension_instance; } virtual void _initialize_classv() { initialize_class(); } - virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; }; - virtual bool _getv(const StringName &p_name, Variant &r_property) const { return false; }; - virtual void _get_property_listv(List<PropertyInfo> *p_list, bool p_reversed) const {}; - virtual void _validate_propertyv(PropertyInfo &p_property) const {}; - virtual bool _property_can_revertv(const StringName &p_name) const { return false; }; - virtual bool _property_get_revertv(const StringName &p_name, Variant &r_property) const { return false; }; + virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; } + virtual bool _getv(const StringName &p_name, Variant &r_property) const { return false; } + virtual void _get_property_listv(List<PropertyInfo> *p_list, bool p_reversed) const {} + virtual void _validate_propertyv(PropertyInfo &p_property) const {} + virtual bool _property_can_revertv(const StringName &p_name) const { return false; } + virtual bool _property_get_revertv(const StringName &p_name, Variant &r_property) const { return false; } virtual void _notificationv(int p_notification, bool p_reversed) {} static void _bind_methods(); static void _bind_compatibility_methods() {} - bool _set(const StringName &p_name, const Variant &p_property) { return false; }; - bool _get(const StringName &p_name, Variant &r_property) const { return false; }; - void _get_property_list(List<PropertyInfo> *p_list) const {}; - void _validate_property(PropertyInfo &p_property) const {}; - bool _property_can_revert(const StringName &p_name) const { return false; }; - bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return false; }; + bool _set(const StringName &p_name, const Variant &p_property) { return false; } + bool _get(const StringName &p_name, Variant &r_property) const { return false; } + void _get_property_list(List<PropertyInfo> *p_list) const {} + void _validate_property(PropertyInfo &p_property) const {} + bool _property_can_revert(const StringName &p_name) const { return false; } + bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return false; } void _notification(int p_notification) {} _FORCE_INLINE_ static void (*_get_bind_methods())() { diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h index f0706b4d08..22eb5a7a3f 100644 --- a/core/object/ref_counted.h +++ b/core/object/ref_counted.h @@ -57,24 +57,30 @@ template <typename T> class Ref { T *reference = nullptr; - void ref(const Ref &p_from) { - if (p_from.reference == reference) { + _FORCE_INLINE_ void ref(const Ref &p_from) { + ref_pointer<false>(p_from.reference); + } + + template <bool Init> + _FORCE_INLINE_ void ref_pointer(T *p_refcounted) { + if (p_refcounted == reference) { return; } - unref(); - - reference = p_from.reference; + // This will go out of scope and get unref'd. + Ref cleanup_ref; + cleanup_ref.reference = reference; + reference = p_refcounted; if (reference) { - reference->reference(); - } - } - - void ref_pointer(T *p_ref) { - ERR_FAIL_NULL(p_ref); - - if (p_ref->init_ref()) { - reference = p_ref; + if constexpr (Init) { + if (!reference->init_ref()) { + reference = nullptr; + } + } else { + if (!reference->reference()) { + reference = nullptr; + } + } } } @@ -124,15 +130,11 @@ public: template <typename T_Other> void operator=(const Ref<T_Other> &p_from) { - RefCounted *refb = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_from.ptr())); - if (!refb) { - unref(); - return; - } - Ref r; - r.reference = Object::cast_to<T>(refb); - ref(r); - r.reference = nullptr; + ref_pointer<false>(Object::cast_to<T>(p_from.ptr())); + } + + void operator=(T *p_from) { + ref_pointer<true>(p_from); } void operator=(const Variant &p_variant) { @@ -142,16 +144,7 @@ public: return; } - unref(); - - if (!object) { - return; - } - - T *r = Object::cast_to<T>(object); - if (r && r->reference()) { - reference = r; - } + ref_pointer<false>(Object::cast_to<T>(object)); } template <typename T_Other> @@ -159,48 +152,25 @@ public: if (reference == p_ptr) { return; } - unref(); - T *r = Object::cast_to<T>(p_ptr); - if (r) { - ref_pointer(r); - } + ref_pointer<true>(Object::cast_to<T>(p_ptr)); } Ref(const Ref &p_from) { - ref(p_from); + this->operator=(p_from); } template <typename T_Other> Ref(const Ref<T_Other> &p_from) { - RefCounted *refb = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_from.ptr())); - if (!refb) { - unref(); - return; - } - Ref r; - r.reference = Object::cast_to<T>(refb); - ref(r); - r.reference = nullptr; + this->operator=(p_from); } - Ref(T *p_reference) { - if (p_reference) { - ref_pointer(p_reference); - } + Ref(T *p_from) { + this->operator=(p_from); } - Ref(const Variant &p_variant) { - Object *object = p_variant.get_validated_object(); - - if (!object) { - return; - } - - T *r = Object::cast_to<T>(object); - if (r && r->reference()) { - reference = r; - } + Ref(const Variant &p_from) { + this->operator=(p_from); } inline bool is_valid() const { return reference != nullptr; } @@ -222,7 +192,7 @@ public: ref(memnew(T(p_params...))); } - Ref() {} + Ref() = default; ~Ref() { unref(); @@ -299,13 +269,13 @@ struct GetTypeInfo<const Ref<T> &> { template <typename T> struct VariantInternalAccessor<Ref<T>> { static _FORCE_INLINE_ Ref<T> get(const Variant *v) { return Ref<T>(*VariantInternal::get_object(v)); } - static _FORCE_INLINE_ void set(Variant *v, const Ref<T> &p_ref) { VariantInternal::refcounted_object_assign(v, p_ref.ptr()); } + static _FORCE_INLINE_ void set(Variant *v, const Ref<T> &p_ref) { VariantInternal::object_assign(v, p_ref); } }; template <typename T> struct VariantInternalAccessor<const Ref<T> &> { static _FORCE_INLINE_ Ref<T> get(const Variant *v) { return Ref<T>(*VariantInternal::get_object(v)); } - static _FORCE_INLINE_ void set(Variant *v, const Ref<T> &p_ref) { VariantInternal::refcounted_object_assign(v, p_ref.ptr()); } + static _FORCE_INLINE_ void set(Variant *v, const Ref<T> &p_ref) { VariantInternal::object_assign(v, p_ref); } }; #endif // REF_COUNTED_H diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index c5856a8a81..542cb2b24d 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -239,9 +239,9 @@ Error ScriptServer::register_language(ScriptLanguage *p_language) { ERR_FAIL_COND_V_MSG(_language_count >= MAX_LANGUAGES, ERR_UNAVAILABLE, "Script languages limit has been reach, cannot register more."); for (int i = 0; i < _language_count; i++) { const ScriptLanguage *other_language = _languages[i]; - ERR_FAIL_COND_V_MSG(other_language->get_extension() == p_language->get_extension(), ERR_ALREADY_EXISTS, "A script language with extension '" + p_language->get_extension() + "' is already registered."); - ERR_FAIL_COND_V_MSG(other_language->get_name() == p_language->get_name(), ERR_ALREADY_EXISTS, "A script language with name '" + p_language->get_name() + "' is already registered."); - ERR_FAIL_COND_V_MSG(other_language->get_type() == p_language->get_type(), ERR_ALREADY_EXISTS, "A script language with type '" + p_language->get_type() + "' is already registered."); + ERR_FAIL_COND_V_MSG(other_language->get_extension() == p_language->get_extension(), ERR_ALREADY_EXISTS, vformat("A script language with extension '%s' is already registered.", p_language->get_extension())); + ERR_FAIL_COND_V_MSG(other_language->get_name() == p_language->get_name(), ERR_ALREADY_EXISTS, vformat("A script language with name '%s' is already registered.", p_language->get_name())); + ERR_FAIL_COND_V_MSG(other_language->get_type() == p_language->get_type(), ERR_ALREADY_EXISTS, vformat("A script language with type '%s' is already registered.", p_language->get_type())); } _languages[_language_count++] = p_language; return OK; diff --git a/core/object/script_language.h b/core/object/script_language.h index 3ddfbb3e7d..31d6638e58 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -446,8 +446,8 @@ public: virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const override; virtual void validate_property(PropertyInfo &p_property) const override {} - virtual bool property_can_revert(const StringName &p_name) const override { return false; }; - virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const override { return false; }; + virtual bool property_can_revert(const StringName &p_name) const override { return false; } + virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const override { return false; } virtual void get_method_list(List<MethodInfo> *p_list) const override; virtual bool has_method(const StringName &p_method) const override; diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h index bc773c5ad3..d2dce34d4f 100644 --- a/core/object/script_language_extension.h +++ b/core/object/script_language_extension.h @@ -57,16 +57,16 @@ public: EXBIND1RC(bool, inherits_script, const Ref<Script> &) EXBIND0RC(StringName, get_instance_base_type) - GDVIRTUAL1RC(GDExtensionPtr<void>, _instance_create, Object *) + GDVIRTUAL1RC_REQUIRED(GDExtensionPtr<void>, _instance_create, Object *) virtual ScriptInstance *instance_create(Object *p_this) override { GDExtensionPtr<void> ret = nullptr; - GDVIRTUAL_REQUIRED_CALL(_instance_create, p_this, ret); + GDVIRTUAL_CALL(_instance_create, p_this, ret); return reinterpret_cast<ScriptInstance *>(ret.operator void *()); } - GDVIRTUAL1RC(GDExtensionPtr<void>, _placeholder_instance_create, Object *) + GDVIRTUAL1RC_REQUIRED(GDExtensionPtr<void>, _placeholder_instance_create, Object *) PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this) override { GDExtensionPtr<void> ret = nullptr; - GDVIRTUAL_REQUIRED_CALL(_placeholder_instance_create, p_this, ret); + GDVIRTUAL_CALL(_placeholder_instance_create, p_this, ret); return reinterpret_cast<PlaceHolderScriptInstance *>(ret.operator void *()); } @@ -76,12 +76,12 @@ public: EXBIND1(set_source_code, const String &) EXBIND1R(Error, reload, bool) - GDVIRTUAL0RC(TypedArray<Dictionary>, _get_documentation) + GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_documentation) GDVIRTUAL0RC(String, _get_class_icon_path) #ifdef TOOLS_ENABLED virtual Vector<DocData::ClassDoc> get_documentation() const override { TypedArray<Dictionary> doc; - GDVIRTUAL_REQUIRED_CALL(_get_documentation, doc); + GDVIRTUAL_CALL(_get_documentation, doc); Vector<DocData::ClassDoc> class_doc; for (int i = 0; i < doc.size(); i++) { @@ -114,10 +114,10 @@ public: return Script::get_script_method_argument_count(p_method, r_is_valid); } - GDVIRTUAL1RC(Dictionary, _get_method_info, const StringName &) + GDVIRTUAL1RC_REQUIRED(Dictionary, _get_method_info, const StringName &) virtual MethodInfo get_method_info(const StringName &p_method) const override { Dictionary mi; - GDVIRTUAL_REQUIRED_CALL(_get_method_info, p_method, mi); + GDVIRTUAL_CALL(_get_method_info, p_method, mi); return MethodInfo::from_dict(mi); } @@ -133,47 +133,47 @@ public: EXBIND0RC(ScriptLanguage *, get_language) EXBIND1RC(bool, has_script_signal, const StringName &) - GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_signal_list) + GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_script_signal_list) virtual void get_script_signal_list(List<MethodInfo> *r_signals) const override { TypedArray<Dictionary> sl; - GDVIRTUAL_REQUIRED_CALL(_get_script_signal_list, sl); + GDVIRTUAL_CALL(_get_script_signal_list, sl); for (int i = 0; i < sl.size(); i++) { r_signals->push_back(MethodInfo::from_dict(sl[i])); } } - GDVIRTUAL1RC(bool, _has_property_default_value, const StringName &) - GDVIRTUAL1RC(Variant, _get_property_default_value, const StringName &) + GDVIRTUAL1RC_REQUIRED(bool, _has_property_default_value, const StringName &) + GDVIRTUAL1RC_REQUIRED(Variant, _get_property_default_value, const StringName &) virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const override { bool has_dv = false; - if (!GDVIRTUAL_REQUIRED_CALL(_has_property_default_value, p_property, has_dv) || !has_dv) { + if (!GDVIRTUAL_CALL(_has_property_default_value, p_property, has_dv) || !has_dv) { return false; } Variant ret; - GDVIRTUAL_REQUIRED_CALL(_get_property_default_value, p_property, ret); + GDVIRTUAL_CALL(_get_property_default_value, p_property, ret); r_value = ret; return true; } EXBIND0(update_exports) - GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_method_list) + GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_script_method_list) virtual void get_script_method_list(List<MethodInfo> *r_methods) const override { TypedArray<Dictionary> sl; - GDVIRTUAL_REQUIRED_CALL(_get_script_method_list, sl); + GDVIRTUAL_CALL(_get_script_method_list, sl); for (int i = 0; i < sl.size(); i++) { r_methods->push_back(MethodInfo::from_dict(sl[i])); } } - GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_property_list) + GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_script_property_list) virtual void get_script_property_list(List<PropertyInfo> *r_propertys) const override { TypedArray<Dictionary> sl; - GDVIRTUAL_REQUIRED_CALL(_get_script_property_list, sl); + GDVIRTUAL_CALL(_get_script_property_list, sl); for (int i = 0; i < sl.size(); i++) { r_propertys->push_back(PropertyInfo::from_dict(sl[i])); } @@ -181,21 +181,21 @@ public: EXBIND1RC(int, get_member_line, const StringName &) - GDVIRTUAL0RC(Dictionary, _get_constants) + GDVIRTUAL0RC_REQUIRED(Dictionary, _get_constants) virtual void get_constants(HashMap<StringName, Variant> *p_constants) override { Dictionary constants; - GDVIRTUAL_REQUIRED_CALL(_get_constants, constants); + GDVIRTUAL_CALL(_get_constants, constants); List<Variant> keys; constants.get_key_list(&keys); for (const Variant &K : keys) { p_constants->insert(K, constants[K]); } } - GDVIRTUAL0RC(TypedArray<StringName>, _get_members) + GDVIRTUAL0RC_REQUIRED(TypedArray<StringName>, _get_members) virtual void get_members(HashSet<StringName> *p_members) override { TypedArray<StringName> members; - GDVIRTUAL_REQUIRED_CALL(_get_members, members); + GDVIRTUAL_CALL(_get_members, members); for (int i = 0; i < members.size(); i++) { p_members->insert(members[i]); } @@ -203,11 +203,11 @@ public: EXBIND0RC(bool, is_placeholder_fallback_enabled) - GDVIRTUAL0RC(Variant, _get_rpc_config) + GDVIRTUAL0RC_REQUIRED(Variant, _get_rpc_config) virtual Variant get_rpc_config() const override { Variant ret; - GDVIRTUAL_REQUIRED_CALL(_get_rpc_config, ret); + GDVIRTUAL_CALL(_get_rpc_config, ret); return ret; } @@ -233,22 +233,22 @@ public: /* EDITOR FUNCTIONS */ - GDVIRTUAL0RC(Vector<String>, _get_reserved_words) + GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_reserved_words) virtual void get_reserved_words(List<String> *p_words) const override { Vector<String> ret; - GDVIRTUAL_REQUIRED_CALL(_get_reserved_words, ret); + GDVIRTUAL_CALL(_get_reserved_words, ret); for (int i = 0; i < ret.size(); i++) { p_words->push_back(ret[i]); } } EXBIND1RC(bool, is_control_flow_keyword, const String &) - GDVIRTUAL0RC(Vector<String>, _get_comment_delimiters) + GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_comment_delimiters) virtual void get_comment_delimiters(List<String> *p_words) const override { Vector<String> ret; - GDVIRTUAL_REQUIRED_CALL(_get_comment_delimiters, ret); + GDVIRTUAL_CALL(_get_comment_delimiters, ret); for (int i = 0; i < ret.size(); i++) { p_words->push_back(ret[i]); } @@ -264,11 +264,11 @@ public: } } - GDVIRTUAL0RC(Vector<String>, _get_string_delimiters) + GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_string_delimiters) virtual void get_string_delimiters(List<String> *p_words) const override { Vector<String> ret; - GDVIRTUAL_REQUIRED_CALL(_get_string_delimiters, ret); + GDVIRTUAL_CALL(_get_string_delimiters, ret); for (int i = 0; i < ret.size(); i++) { p_words->push_back(ret[i]); } @@ -276,11 +276,11 @@ public: EXBIND3RC(Ref<Script>, make_template, const String &, const String &, const String &) - GDVIRTUAL1RC(TypedArray<Dictionary>, _get_built_in_templates, StringName) + GDVIRTUAL1RC_REQUIRED(TypedArray<Dictionary>, _get_built_in_templates, StringName) virtual Vector<ScriptTemplate> get_built_in_templates(const StringName &p_object) override { TypedArray<Dictionary> ret; - GDVIRTUAL_REQUIRED_CALL(_get_built_in_templates, p_object, ret); + GDVIRTUAL_CALL(_get_built_in_templates, p_object, ret); Vector<ScriptTemplate> stret; for (int i = 0; i < ret.size(); i++) { Dictionary d = ret[i]; @@ -304,10 +304,10 @@ public: EXBIND0R(bool, is_using_templates) - GDVIRTUAL6RC(Dictionary, _validate, const String &, const String &, bool, bool, bool, bool) + GDVIRTUAL6RC_REQUIRED(Dictionary, _validate, const String &, const String &, bool, bool, bool, bool) virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override { Dictionary ret; - GDVIRTUAL_REQUIRED_CALL(_validate, p_script, p_path, r_functions != nullptr, r_errors != nullptr, r_warnings != nullptr, r_safe_lines != nullptr, ret); + GDVIRTUAL_CALL(_validate, p_script, p_path, r_functions != nullptr, r_errors != nullptr, r_warnings != nullptr, r_safe_lines != nullptr, ret); if (!ret.has("valid")) { return false; } @@ -371,10 +371,10 @@ public: } EXBIND1RC(String, validate_path, const String &) - GDVIRTUAL0RC(Object *, _create_script) + GDVIRTUAL0RC_REQUIRED(Object *, _create_script) Script *create_script() const override { Object *ret = nullptr; - GDVIRTUAL_REQUIRED_CALL(_create_script, ret); + GDVIRTUAL_CALL(_create_script, ret); return Object::cast_to<Script>(ret); } #ifndef DISABLE_DEPRECATED @@ -400,11 +400,11 @@ public: return ScriptNameCasing::SCRIPT_NAME_CASING_SNAKE_CASE; } - GDVIRTUAL3RC(Dictionary, _complete_code, const String &, const String &, Object *) + GDVIRTUAL3RC_REQUIRED(Dictionary, _complete_code, const String &, const String &, Object *) virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<CodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) override { Dictionary ret; - GDVIRTUAL_REQUIRED_CALL(_complete_code, p_code, p_path, p_owner, ret); + GDVIRTUAL_CALL(_complete_code, p_code, p_path, p_owner, ret); if (!ret.has("result")) { return ERR_UNAVAILABLE; } @@ -449,11 +449,11 @@ public: return result; } - GDVIRTUAL4RC(Dictionary, _lookup_code, const String &, const String &, const String &, Object *) + GDVIRTUAL4RC_REQUIRED(Dictionary, _lookup_code, const String &, const String &, const String &, Object *) virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) override { Dictionary ret; - GDVIRTUAL_REQUIRED_CALL(_lookup_code, p_code, p_symbol, p_path, p_owner, ret); + GDVIRTUAL_CALL(_lookup_code, p_code, p_symbol, p_path, p_owner, ret); if (!ret.has("result")) { return ERR_UNAVAILABLE; } @@ -474,10 +474,10 @@ public: return result; } - GDVIRTUAL3RC(String, _auto_indent_code, const String &, int, int) + GDVIRTUAL3RC_REQUIRED(String, _auto_indent_code, const String &, int, int) virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override { String ret; - GDVIRTUAL_REQUIRED_CALL(_auto_indent_code, p_code, p_from_line, p_to_line, ret); + GDVIRTUAL_CALL(_auto_indent_code, p_code, p_from_line, p_to_line, ret); p_code = ret; } EXBIND2(add_global_constant, const StringName &, const Variant &) @@ -496,10 +496,10 @@ public: EXBIND1RC(String, debug_get_stack_level_function, int) EXBIND1RC(String, debug_get_stack_level_source, int) - GDVIRTUAL3R(Dictionary, _debug_get_stack_level_locals, int, int, int) + GDVIRTUAL3R_REQUIRED(Dictionary, _debug_get_stack_level_locals, int, int, int) virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override { Dictionary ret; - GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_locals, p_level, p_max_subitems, p_max_depth, ret); + GDVIRTUAL_CALL(_debug_get_stack_level_locals, p_level, p_max_subitems, p_max_depth, ret); if (ret.size() == 0) { return; } @@ -516,10 +516,10 @@ public: } } } - GDVIRTUAL3R(Dictionary, _debug_get_stack_level_members, int, int, int) + GDVIRTUAL3R_REQUIRED(Dictionary, _debug_get_stack_level_members, int, int, int) virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override { Dictionary ret; - GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_members, p_level, p_max_subitems, p_max_depth, ret); + GDVIRTUAL_CALL(_debug_get_stack_level_members, p_level, p_max_subitems, p_max_depth, ret); if (ret.size() == 0) { return; } @@ -536,17 +536,17 @@ public: } } } - GDVIRTUAL1R(GDExtensionPtr<void>, _debug_get_stack_level_instance, int) + GDVIRTUAL1R_REQUIRED(GDExtensionPtr<void>, _debug_get_stack_level_instance, int) virtual ScriptInstance *debug_get_stack_level_instance(int p_level) override { GDExtensionPtr<void> ret = nullptr; - GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_instance, p_level, ret); + GDVIRTUAL_CALL(_debug_get_stack_level_instance, p_level, ret); return reinterpret_cast<ScriptInstance *>(ret.operator void *()); } - GDVIRTUAL2R(Dictionary, _debug_get_globals, int, int) + GDVIRTUAL2R_REQUIRED(Dictionary, _debug_get_globals, int, int) virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override { Dictionary ret; - GDVIRTUAL_REQUIRED_CALL(_debug_get_globals, p_max_subitems, p_max_depth, ret); + GDVIRTUAL_CALL(_debug_get_globals, p_max_subitems, p_max_depth, ret); if (ret.size() == 0) { return; } @@ -566,10 +566,10 @@ public: EXBIND4R(String, debug_parse_stack_level_expression, int, const String &, int, int) - GDVIRTUAL0R(TypedArray<Dictionary>, _debug_get_current_stack_info) + GDVIRTUAL0R_REQUIRED(TypedArray<Dictionary>, _debug_get_current_stack_info) virtual Vector<StackInfo> debug_get_current_stack_info() override { TypedArray<Dictionary> ret; - GDVIRTUAL_REQUIRED_CALL(_debug_get_current_stack_info, ret); + GDVIRTUAL_CALL(_debug_get_current_stack_info, ret); Vector<StackInfo> sret; for (const Variant &var : ret) { StackInfo si; @@ -590,29 +590,29 @@ public: EXBIND2(reload_tool_script, const Ref<Script> &, bool) /* LOADER FUNCTIONS */ - GDVIRTUAL0RC(PackedStringArray, _get_recognized_extensions) + GDVIRTUAL0RC_REQUIRED(PackedStringArray, _get_recognized_extensions) virtual void get_recognized_extensions(List<String> *p_extensions) const override { PackedStringArray ret; - GDVIRTUAL_REQUIRED_CALL(_get_recognized_extensions, ret); + GDVIRTUAL_CALL(_get_recognized_extensions, ret); for (int i = 0; i < ret.size(); i++) { p_extensions->push_back(ret[i]); } } - GDVIRTUAL0RC(TypedArray<Dictionary>, _get_public_functions) + GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_public_functions) virtual void get_public_functions(List<MethodInfo> *p_functions) const override { TypedArray<Dictionary> ret; - GDVIRTUAL_REQUIRED_CALL(_get_public_functions, ret); + GDVIRTUAL_CALL(_get_public_functions, ret); for (const Variant &var : ret) { MethodInfo mi = MethodInfo::from_dict(var); p_functions->push_back(mi); } } - GDVIRTUAL0RC(Dictionary, _get_public_constants) + GDVIRTUAL0RC_REQUIRED(Dictionary, _get_public_constants) virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override { Dictionary ret; - GDVIRTUAL_REQUIRED_CALL(_get_public_constants, ret); + GDVIRTUAL_CALL(_get_public_constants, ret); for (int i = 0; i < ret.size(); i++) { Dictionary d = ret[i]; ERR_CONTINUE(!d.has("name")); @@ -620,10 +620,10 @@ public: p_constants->push_back(Pair<String, Variant>(d["name"], d["value"])); } } - GDVIRTUAL0RC(TypedArray<Dictionary>, _get_public_annotations) + GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_public_annotations) virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override { TypedArray<Dictionary> ret; - GDVIRTUAL_REQUIRED_CALL(_get_public_annotations, ret); + GDVIRTUAL_CALL(_get_public_annotations, ret); for (const Variant &var : ret) { MethodInfo mi = MethodInfo::from_dict(var); p_annotations->push_back(mi); @@ -634,19 +634,19 @@ public: EXBIND0(profiling_stop) EXBIND1(profiling_set_save_native_calls, bool) - GDVIRTUAL2R(int, _profiling_get_accumulated_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int) + GDVIRTUAL2R_REQUIRED(int, _profiling_get_accumulated_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int) virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override { int ret = 0; - GDVIRTUAL_REQUIRED_CALL(_profiling_get_accumulated_data, p_info_arr, p_info_max, ret); + GDVIRTUAL_CALL(_profiling_get_accumulated_data, p_info_arr, p_info_max, ret); return ret; } - GDVIRTUAL2R(int, _profiling_get_frame_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int) + GDVIRTUAL2R_REQUIRED(int, _profiling_get_frame_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int) virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override { int ret = 0; - GDVIRTUAL_REQUIRED_CALL(_profiling_get_frame_data, p_info_arr, p_info_max, ret); + GDVIRTUAL_CALL(_profiling_get_frame_data, p_info_arr, p_info_max, ret); return ret; } @@ -654,11 +654,11 @@ public: EXBIND1RC(bool, handles_global_class_type, const String &) - GDVIRTUAL1RC(Dictionary, _get_global_class_name, const String &) + GDVIRTUAL1RC_REQUIRED(Dictionary, _get_global_class_name, const String &) virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const override { Dictionary ret; - GDVIRTUAL_REQUIRED_CALL(_get_global_class_name, p_path, ret); + GDVIRTUAL_CALL(_get_global_class_name, p_path, ret); if (!ret.has("name")) { return String(); } diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index 4d67cd930e..7126852a5a 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -48,7 +48,7 @@ void UndoRedo::Operation::delete_reference() { } } -void UndoRedo::_discard_redo() { +void UndoRedo::discard_redo() { if (current_action == actions.size() - 1) { return; } @@ -89,7 +89,7 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode, bool p_back uint64_t ticks = OS::get_singleton()->get_ticks_msec(); if (action_level == 0) { - _discard_redo(); + discard_redo(); // Check if the merge operation is valid if (p_mode != MERGE_DISABLE && actions.size() && actions[actions.size() - 1].name == p_name && actions[actions.size() - 1].backward_undo_ops == p_backward_undo_ops && actions[actions.size() - 1].last_tick + 800 > ticks) { @@ -288,7 +288,7 @@ void UndoRedo::end_force_keep_in_merge_ends() { } void UndoRedo::_pop_history_tail() { - _discard_redo(); + discard_redo(); if (!actions.size()) { return; @@ -364,7 +364,7 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E, bool p_execu Variant ret; op.callable.callp(nullptr, 0, ret, ce); if (ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, nullptr, 0, ce)); + ERR_PRINT(vformat("Error calling UndoRedo method operation '%s': %s.", String(op.name), Variant::get_call_error_text(obj, op.name, nullptr, 0, ce))); } #ifdef TOOLS_ENABLED Resource *res = Object::cast_to<Resource>(obj); @@ -455,7 +455,7 @@ String UndoRedo::get_action_name(int p_id) { void UndoRedo::clear_history(bool p_increase_version) { ERR_FAIL_COND(action_level > 0); - _discard_redo(); + discard_redo(); while (actions.size()) { _pop_history_tail(); diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h index 19d178635c..ded962670c 100644 --- a/core/object/undo_redo.h +++ b/core/object/undo_redo.h @@ -129,6 +129,7 @@ public: int get_current_action(); String get_action_name(int p_id); void clear_history(bool p_increase_version = true); + void discard_redo(); bool has_undo() const; bool has_redo() const; diff --git a/core/os/SCsub b/core/os/SCsub index 19a6549225..ab81175894 100644 --- a/core/os/SCsub +++ b/core/os/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/os/os.cpp b/core/os/os.cpp index 642de11a9f..aed14d48c0 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -352,7 +352,7 @@ void OS::ensure_user_data_dir() { Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); Error err = da->make_dir_recursive(dd); - ERR_FAIL_COND_MSG(err != OK, "Error attempting to create data dir: " + dd + "."); + ERR_FAIL_COND_MSG(err != OK, vformat("Error attempting to create data dir: %s.", dd)); } String OS::get_model_name() const { diff --git a/core/os/os.h b/core/os/os.h index 30d2a4266f..4bb177eb77 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -32,7 +32,6 @@ #define OS_H #include "core/config/engine.h" -#include "core/io/image.h" #include "core/io/logger.h" #include "core/io/remote_filesystem_client.h" #include "core/os/time_enums.h" @@ -177,14 +176,14 @@ public: void set_delta_smoothing(bool p_enabled); bool is_delta_smoothing_enabled() const; - virtual Vector<String> get_system_fonts() const { return Vector<String>(); }; - virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const { return String(); }; - virtual Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const { return Vector<String>(); }; + virtual Vector<String> get_system_fonts() const { return Vector<String>(); } + virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const { return String(); } + virtual Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const { return Vector<String>(); } virtual String get_executable_path() const; virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) = 0; virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments, bool p_blocking = true) { return Dictionary(); } virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) = 0; - virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) { return create_process(get_executable_path(), p_arguments, r_child_id); }; + virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) { return create_process(get_executable_path(), p_arguments, r_child_id); } virtual Error kill(const ProcessID &p_pid) = 0; virtual int get_process_id() const; virtual bool is_process_running(const ProcessID &p_pid) const = 0; diff --git a/core/os/spin_lock.h b/core/os/spin_lock.h index d386cd5890..8c2d5667ff 100644 --- a/core/os/spin_lock.h +++ b/core/os/spin_lock.h @@ -33,6 +33,10 @@ #include "core/typedefs.h" +#ifdef _MSC_VER +#include <intrin.h> +#endif + #if defined(__APPLE__) #include <os/lock.h> @@ -52,19 +56,52 @@ public: #else +#include "core/os/thread.h" + #include <atomic> -class SpinLock { - mutable std::atomic_flag locked = ATOMIC_FLAG_INIT; +_ALWAYS_INLINE_ static void _cpu_pause() { +#if defined(_MSC_VER) +// ----- MSVC. +#if defined(_M_ARM) || defined(_M_ARM64) // ARM. + __yield(); +#elif defined(_M_IX86) || defined(_M_X64) // x86. + _mm_pause(); +#endif +#elif defined(__GNUC__) || defined(__clang__) +// ----- GCC/Clang. +#if defined(__i386__) || defined(__x86_64__) // x86. + __builtin_ia32_pause(); +#elif defined(__arm__) || defined(__aarch64__) // ARM. + asm volatile("yield"); +#elif defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) // PowerPC. + asm volatile("or 27,27,27"); +#elif defined(__riscv) // RISC-V. + asm volatile(".insn i 0x0F, 0, x0, x0, 0x010"); +#endif +#endif +} + +static_assert(std::atomic_bool::is_always_lock_free); + +class alignas(Thread::CACHE_LINE_BYTES) SpinLock { + mutable std::atomic<bool> locked = ATOMIC_VAR_INIT(false); public: _ALWAYS_INLINE_ void lock() const { - while (locked.test_and_set(std::memory_order_acquire)) { - // Continue. + while (true) { + bool expected = false; + if (locked.compare_exchange_weak(expected, true, std::memory_order_acquire, std::memory_order_relaxed)) { + break; + } + do { + _cpu_pause(); + } while (locked.load(std::memory_order_relaxed)); } } + _ALWAYS_INLINE_ void unlock() const { - locked.clear(std::memory_order_release); + locked.store(false, std::memory_order_release); } }; diff --git a/core/os/thread.h b/core/os/thread.h index a0ecc24c91..1c442b41f6 100644 --- a/core/os/thread.h +++ b/core/os/thread.h @@ -42,6 +42,8 @@ #include "core/templates/safe_refcount.h" #include "core/typedefs.h" +#include <new> + #ifdef MINGW_ENABLED #define MINGW_STDTHREAD_REDUNDANCY_WARNING #include "thirdparty/mingw-std-threads/mingw.thread.h" @@ -85,6 +87,20 @@ public: void (*term)() = nullptr; }; +#if defined(__cpp_lib_hardware_interference_size) && !defined(ANDROID_ENABLED) // This would be OK with NDK >= 26. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winterference-size" +#endif + static constexpr size_t CACHE_LINE_BYTES = std::hardware_destructive_interference_size; +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif +#else + // At a negligible memory cost, we use a conservatively high value. + static constexpr size_t CACHE_LINE_BYTES = 128; +#endif + private: friend class Main; @@ -135,6 +151,8 @@ public: typedef uint64_t ID; + static constexpr size_t CACHE_LINE_BYTES = sizeof(void *); + enum : ID { UNASSIGNED_ID = 0, MAIN_ID = 1 diff --git a/core/string/SCsub b/core/string/SCsub index 3217166f18..b06e32eb88 100644 --- a/core/string/SCsub +++ b/core/string/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/string/char_range.inc b/core/string/char_range.inc index 2b081b96de..efae757802 100644 --- a/core/string/char_range.inc +++ b/core/string/char_range.inc @@ -33,14 +33,17 @@ #include "core/typedefs.h" +// Unicode Derived Core Properties +// Source: https://www.unicode.org/Public/16.0.0/ucd/DerivedCoreProperties.txt + struct CharRange { char32_t start; char32_t end; }; -inline constexpr CharRange xid_start[] = { +constexpr inline CharRange xid_start[] = { { 0x41, 0x5a }, - { 0x5f, 0x5f }, + { 0x5f, 0x5f }, // Underscore technically isn't in XID_Start, but for our purposes it's included. { 0x61, 0x7a }, { 0xaa, 0xaa }, { 0xb5, 0xb5 }, @@ -54,7 +57,7 @@ inline constexpr CharRange xid_start[] = { { 0x2ee, 0x2ee }, { 0x370, 0x374 }, { 0x376, 0x377 }, - { 0x37a, 0x37d }, + { 0x37b, 0x37d }, { 0x37f, 0x37f }, { 0x386, 0x386 }, { 0x388, 0x38a }, @@ -182,7 +185,7 @@ inline constexpr CharRange xid_start[] = { { 0xdbd, 0xdbd }, { 0xdc0, 0xdc6 }, { 0xe01, 0xe30 }, - { 0xe32, 0xe33 }, + { 0xe32, 0xe32 }, { 0xe40, 0xe46 }, { 0xe81, 0xe82 }, { 0xe84, 0xe84 }, @@ -190,7 +193,7 @@ inline constexpr CharRange xid_start[] = { { 0xe8c, 0xea3 }, { 0xea5, 0xea5 }, { 0xea7, 0xeb0 }, - { 0xeb2, 0xeb3 }, + { 0xeb2, 0xeb2 }, { 0xebd, 0xebd }, { 0xec0, 0xec4 }, { 0xec6, 0xec6 }, @@ -245,8 +248,7 @@ inline constexpr CharRange xid_start[] = { { 0x17d7, 0x17d7 }, { 0x17dc, 0x17dc }, { 0x1820, 0x1878 }, - { 0x1880, 0x1884 }, - { 0x1887, 0x18a8 }, + { 0x1880, 0x18a8 }, { 0x18aa, 0x18aa }, { 0x18b0, 0x18f5 }, { 0x1900, 0x191e }, @@ -265,7 +267,7 @@ inline constexpr CharRange xid_start[] = { { 0x1c00, 0x1c23 }, { 0x1c4d, 0x1c4f }, { 0x1c5a, 0x1c7d }, - { 0x1c80, 0x1c88 }, + { 0x1c80, 0x1c8a }, { 0x1c90, 0x1cba }, { 0x1cbd, 0x1cbf }, { 0x1ce9, 0x1cec }, @@ -330,7 +332,7 @@ inline constexpr CharRange xid_start[] = { { 0x3031, 0x3035 }, { 0x3038, 0x303c }, { 0x3041, 0x3096 }, - { 0x309b, 0x309f }, + { 0x309d, 0x309f }, { 0x30a1, 0x30fa }, { 0x30fc, 0x30ff }, { 0x3105, 0x312f }, @@ -348,10 +350,10 @@ inline constexpr CharRange xid_start[] = { { 0xa6a0, 0xa6ef }, { 0xa717, 0xa71f }, { 0xa722, 0xa788 }, - { 0xa78b, 0xa7ca }, + { 0xa78b, 0xa7cd }, { 0xa7d0, 0xa7d1 }, { 0xa7d3, 0xa7d3 }, - { 0xa7d5, 0xa7d9 }, + { 0xa7d5, 0xa7dc }, { 0xa7f2, 0xa801 }, { 0xa803, 0xa805 }, { 0xa807, 0xa80a }, @@ -406,15 +408,22 @@ inline constexpr CharRange xid_start[] = { { 0xfb40, 0xfb41 }, { 0xfb43, 0xfb44 }, { 0xfb46, 0xfbb1 }, - { 0xfbd3, 0xfd3d }, + { 0xfbd3, 0xfc5d }, + { 0xfc64, 0xfd3d }, { 0xfd50, 0xfd8f }, { 0xfd92, 0xfdc7 }, - { 0xfdf0, 0xfdfb }, - { 0xfe70, 0xfe74 }, - { 0xfe76, 0xfefc }, + { 0xfdf0, 0xfdf9 }, + { 0xfe71, 0xfe71 }, + { 0xfe73, 0xfe73 }, + { 0xfe77, 0xfe77 }, + { 0xfe79, 0xfe79 }, + { 0xfe7b, 0xfe7b }, + { 0xfe7d, 0xfe7d }, + { 0xfe7f, 0xfefc }, { 0xff21, 0xff3a }, { 0xff41, 0xff5a }, - { 0xff66, 0xffbe }, + { 0xff66, 0xff9d }, + { 0xffa0, 0xffbe }, { 0xffc2, 0xffc7 }, { 0xffca, 0xffcf }, { 0xffd2, 0xffd7 }, @@ -449,6 +458,7 @@ inline constexpr CharRange xid_start[] = { { 0x105a3, 0x105b1 }, { 0x105b3, 0x105b9 }, { 0x105bb, 0x105bc }, + { 0x105c0, 0x105f3 }, { 0x10600, 0x10736 }, { 0x10740, 0x10755 }, { 0x10760, 0x10767 }, @@ -485,8 +495,11 @@ inline constexpr CharRange xid_start[] = { { 0x10c80, 0x10cb2 }, { 0x10cc0, 0x10cf2 }, { 0x10d00, 0x10d23 }, + { 0x10d4a, 0x10d65 }, + { 0x10d6f, 0x10d85 }, { 0x10e80, 0x10ea9 }, { 0x10eb0, 0x10eb1 }, + { 0x10ec2, 0x10ec4 }, { 0x10f00, 0x10f1c }, { 0x10f27, 0x10f27 }, { 0x10f30, 0x10f45 }, @@ -509,6 +522,7 @@ inline constexpr CharRange xid_start[] = { { 0x111dc, 0x111dc }, { 0x11200, 0x11211 }, { 0x11213, 0x1122b }, + { 0x1123f, 0x11240 }, { 0x11280, 0x11286 }, { 0x11288, 0x11288 }, { 0x1128a, 0x1128d }, @@ -524,6 +538,13 @@ inline constexpr CharRange xid_start[] = { { 0x1133d, 0x1133d }, { 0x11350, 0x11350 }, { 0x1135d, 0x11361 }, + { 0x11380, 0x11389 }, + { 0x1138b, 0x1138b }, + { 0x1138e, 0x1138e }, + { 0x11390, 0x113b5 }, + { 0x113b7, 0x113b7 }, + { 0x113d1, 0x113d1 }, + { 0x113d3, 0x113d3 }, { 0x11400, 0x11434 }, { 0x11447, 0x1144a }, { 0x1145f, 0x11461 }, @@ -558,6 +579,7 @@ inline constexpr CharRange xid_start[] = { { 0x11a5c, 0x11a89 }, { 0x11a9d, 0x11a9d }, { 0x11ab0, 0x11af8 }, + { 0x11bc0, 0x11be0 }, { 0x11c00, 0x11c08 }, { 0x11c0a, 0x11c2e }, { 0x11c40, 0x11c40 }, @@ -571,13 +593,19 @@ inline constexpr CharRange xid_start[] = { { 0x11d6a, 0x11d89 }, { 0x11d98, 0x11d98 }, { 0x11ee0, 0x11ef2 }, + { 0x11f02, 0x11f02 }, + { 0x11f04, 0x11f10 }, + { 0x11f12, 0x11f33 }, { 0x11fb0, 0x11fb0 }, { 0x12000, 0x12399 }, { 0x12400, 0x1246e }, { 0x12480, 0x12543 }, { 0x12f90, 0x12ff0 }, - { 0x13000, 0x1342e }, + { 0x13000, 0x1342f }, + { 0x13441, 0x13446 }, + { 0x13460, 0x143fa }, { 0x14400, 0x14646 }, + { 0x16100, 0x1611d }, { 0x16800, 0x16a38 }, { 0x16a40, 0x16a5e }, { 0x16a70, 0x16abe }, @@ -586,6 +614,7 @@ inline constexpr CharRange xid_start[] = { { 0x16b40, 0x16b43 }, { 0x16b63, 0x16b77 }, { 0x16b7d, 0x16b8f }, + { 0x16d40, 0x16d6c }, { 0x16e40, 0x16e7f }, { 0x16f00, 0x16f4a }, { 0x16f50, 0x16f50 }, @@ -594,12 +623,14 @@ inline constexpr CharRange xid_start[] = { { 0x16fe3, 0x16fe3 }, { 0x17000, 0x187f7 }, { 0x18800, 0x18cd5 }, - { 0x18d00, 0x18d08 }, + { 0x18cff, 0x18d08 }, { 0x1aff0, 0x1aff3 }, { 0x1aff5, 0x1affb }, { 0x1affd, 0x1affe }, { 0x1b000, 0x1b122 }, + { 0x1b132, 0x1b132 }, { 0x1b150, 0x1b152 }, + { 0x1b155, 0x1b155 }, { 0x1b164, 0x1b167 }, { 0x1b170, 0x1b2fb }, { 0x1bc00, 0x1bc6a }, @@ -637,11 +668,16 @@ inline constexpr CharRange xid_start[] = { { 0x1d7aa, 0x1d7c2 }, { 0x1d7c4, 0x1d7cb }, { 0x1df00, 0x1df1e }, + { 0x1df25, 0x1df2a }, + { 0x1e030, 0x1e06d }, { 0x1e100, 0x1e12c }, { 0x1e137, 0x1e13d }, { 0x1e14e, 0x1e14e }, { 0x1e290, 0x1e2ad }, { 0x1e2c0, 0x1e2eb }, + { 0x1e4d0, 0x1e4eb }, + { 0x1e5d0, 0x1e5ed }, + { 0x1e5f0, 0x1e5f0 }, { 0x1e7e0, 0x1e7e6 }, { 0x1e7e8, 0x1e7eb }, { 0x1e7ed, 0x1e7ee }, @@ -683,15 +719,17 @@ inline constexpr CharRange xid_start[] = { { 0x1eea5, 0x1eea9 }, { 0x1eeab, 0x1eebb }, { 0x20000, 0x2a6df }, - { 0x2a700, 0x2b738 }, + { 0x2a700, 0x2b739 }, { 0x2b740, 0x2b81d }, { 0x2b820, 0x2cea1 }, { 0x2ceb0, 0x2ebe0 }, + { 0x2ebf0, 0x2ee5d }, { 0x2f800, 0x2fa1d }, { 0x30000, 0x3134a }, + { 0x31350, 0x323af }, }; -inline constexpr CharRange xid_continue[] = { +constexpr inline CharRange xid_continue[] = { { 0x30, 0x39 }, { 0x41, 0x5a }, { 0x5f, 0x5f }, @@ -709,7 +747,7 @@ inline constexpr CharRange xid_continue[] = { { 0x2ee, 0x2ee }, { 0x300, 0x374 }, { 0x376, 0x377 }, - { 0x37a, 0x37d }, + { 0x37b, 0x37d }, { 0x37f, 0x37f }, { 0x386, 0x38a }, { 0x38c, 0x38c }, @@ -745,7 +783,7 @@ inline constexpr CharRange xid_continue[] = { { 0x860, 0x86a }, { 0x870, 0x887 }, { 0x889, 0x88e }, - { 0x898, 0x8e1 }, + { 0x897, 0x8e1 }, { 0x8e3, 0x963 }, { 0x966, 0x96f }, { 0x971, 0x983 }, @@ -850,7 +888,7 @@ inline constexpr CharRange xid_continue[] = { { 0xcdd, 0xcde }, { 0xce0, 0xce3 }, { 0xce6, 0xcef }, - { 0xcf1, 0xcf2 }, + { 0xcf1, 0xcf3 }, { 0xd00, 0xd0c }, { 0xd0e, 0xd10 }, { 0xd12, 0xd44 }, @@ -883,7 +921,7 @@ inline constexpr CharRange xid_continue[] = { { 0xea7, 0xebd }, { 0xec0, 0xec4 }, { 0xec6, 0xec6 }, - { 0xec8, 0xecd }, + { 0xec8, 0xece }, { 0xed0, 0xed9 }, { 0xedc, 0xedf }, { 0xf00, 0xf00 }, @@ -921,8 +959,7 @@ inline constexpr CharRange xid_continue[] = { { 0x1312, 0x1315 }, { 0x1318, 0x135a }, { 0x135d, 0x135f }, - { 0x1369, 0x1369 }, - { 0x1371, 0x1371 }, + { 0x1369, 0x1371 }, { 0x1380, 0x138f }, { 0x13a0, 0x13f5 }, { 0x13f8, 0x13fd }, @@ -969,7 +1006,7 @@ inline constexpr CharRange xid_continue[] = { { 0x1c00, 0x1c37 }, { 0x1c40, 0x1c49 }, { 0x1c4d, 0x1c7d }, - { 0x1c80, 0x1c88 }, + { 0x1c80, 0x1c8a }, { 0x1c90, 0x1cba }, { 0x1cbd, 0x1cbf }, { 0x1cd0, 0x1cd2 }, @@ -993,6 +1030,7 @@ inline constexpr CharRange xid_continue[] = { { 0x1fe0, 0x1fec }, { 0x1ff2, 0x1ff4 }, { 0x1ff6, 0x1ffc }, + { 0x200c, 0x200d }, { 0x203f, 0x2040 }, { 0x2054, 0x2054 }, { 0x2071, 0x2071 }, @@ -1036,9 +1074,9 @@ inline constexpr CharRange xid_continue[] = { { 0x3031, 0x3035 }, { 0x3038, 0x303c }, { 0x3041, 0x3096 }, - { 0x3099, 0x309f }, - { 0x30a1, 0x30fa }, - { 0x30fc, 0x30ff }, + { 0x3099, 0x309a }, + { 0x309d, 0x309f }, + { 0x30a1, 0x30ff }, { 0x3105, 0x312f }, { 0x3131, 0x318e }, { 0x31a0, 0x31bf }, @@ -1053,10 +1091,10 @@ inline constexpr CharRange xid_continue[] = { { 0xa67f, 0xa6f1 }, { 0xa717, 0xa71f }, { 0xa722, 0xa788 }, - { 0xa78b, 0xa7ca }, + { 0xa78b, 0xa7cd }, { 0xa7d0, 0xa7d1 }, { 0xa7d3, 0xa7d3 }, - { 0xa7d5, 0xa7d9 }, + { 0xa7d5, 0xa7dc }, { 0xa7f2, 0xa827 }, { 0xa82c, 0xa82c }, { 0xa840, 0xa873 }, @@ -1102,21 +1140,27 @@ inline constexpr CharRange xid_continue[] = { { 0xfb40, 0xfb41 }, { 0xfb43, 0xfb44 }, { 0xfb46, 0xfbb1 }, - { 0xfbd3, 0xfd3d }, + { 0xfbd3, 0xfc5d }, + { 0xfc64, 0xfd3d }, { 0xfd50, 0xfd8f }, { 0xfd92, 0xfdc7 }, - { 0xfdf0, 0xfdfb }, + { 0xfdf0, 0xfdf9 }, { 0xfe00, 0xfe0f }, { 0xfe20, 0xfe2f }, { 0xfe33, 0xfe34 }, { 0xfe4d, 0xfe4f }, - { 0xfe70, 0xfe74 }, - { 0xfe76, 0xfefc }, + { 0xfe71, 0xfe71 }, + { 0xfe73, 0xfe73 }, + { 0xfe77, 0xfe77 }, + { 0xfe79, 0xfe79 }, + { 0xfe7b, 0xfe7b }, + { 0xfe7d, 0xfe7d }, + { 0xfe7f, 0xfefc }, { 0xff10, 0xff19 }, { 0xff21, 0xff3a }, { 0xff3f, 0xff3f }, { 0xff41, 0xff5a }, - { 0xff66, 0xffbe }, + { 0xff65, 0xffbe }, { 0xffc2, 0xffc7 }, { 0xffca, 0xffcf }, { 0xffd2, 0xffd7 }, @@ -1154,6 +1198,7 @@ inline constexpr CharRange xid_continue[] = { { 0x105a3, 0x105b1 }, { 0x105b3, 0x105b9 }, { 0x105bb, 0x105bc }, + { 0x105c0, 0x105f3 }, { 0x10600, 0x10736 }, { 0x10740, 0x10755 }, { 0x10760, 0x10767 }, @@ -1194,10 +1239,14 @@ inline constexpr CharRange xid_continue[] = { { 0x10cc0, 0x10cf2 }, { 0x10d00, 0x10d27 }, { 0x10d30, 0x10d39 }, + { 0x10d40, 0x10d65 }, + { 0x10d69, 0x10d6d }, + { 0x10d6f, 0x10d85 }, { 0x10e80, 0x10ea9 }, { 0x10eab, 0x10eac }, { 0x10eb0, 0x10eb1 }, - { 0x10f00, 0x10f1c }, + { 0x10ec2, 0x10ec4 }, + { 0x10efc, 0x10f1c }, { 0x10f27, 0x10f27 }, { 0x10f30, 0x10f50 }, { 0x10f70, 0x10f85 }, @@ -1220,7 +1269,7 @@ inline constexpr CharRange xid_continue[] = { { 0x111dc, 0x111dc }, { 0x11200, 0x11211 }, { 0x11213, 0x11237 }, - { 0x1123e, 0x1123e }, + { 0x1123e, 0x11241 }, { 0x11280, 0x11286 }, { 0x11288, 0x11288 }, { 0x1128a, 0x1128d }, @@ -1243,6 +1292,16 @@ inline constexpr CharRange xid_continue[] = { { 0x1135d, 0x11363 }, { 0x11366, 0x1136c }, { 0x11370, 0x11374 }, + { 0x11380, 0x11389 }, + { 0x1138b, 0x1138b }, + { 0x1138e, 0x1138e }, + { 0x11390, 0x113b5 }, + { 0x113b7, 0x113c0 }, + { 0x113c2, 0x113c2 }, + { 0x113c5, 0x113c5 }, + { 0x113c7, 0x113ca }, + { 0x113cc, 0x113d3 }, + { 0x113e1, 0x113e2 }, { 0x11400, 0x1144a }, { 0x11450, 0x11459 }, { 0x1145e, 0x11461 }, @@ -1257,6 +1316,7 @@ inline constexpr CharRange xid_continue[] = { { 0x11650, 0x11659 }, { 0x11680, 0x116b8 }, { 0x116c0, 0x116c9 }, + { 0x116d0, 0x116e3 }, { 0x11700, 0x1171a }, { 0x1171d, 0x1172b }, { 0x11730, 0x11739 }, @@ -1280,6 +1340,8 @@ inline constexpr CharRange xid_continue[] = { { 0x11a50, 0x11a99 }, { 0x11a9d, 0x11a9d }, { 0x11ab0, 0x11af8 }, + { 0x11bc0, 0x11be0 }, + { 0x11bf0, 0x11bf9 }, { 0x11c00, 0x11c08 }, { 0x11c0a, 0x11c36 }, { 0x11c38, 0x11c40 }, @@ -1301,13 +1363,20 @@ inline constexpr CharRange xid_continue[] = { { 0x11d93, 0x11d98 }, { 0x11da0, 0x11da9 }, { 0x11ee0, 0x11ef6 }, + { 0x11f00, 0x11f10 }, + { 0x11f12, 0x11f3a }, + { 0x11f3e, 0x11f42 }, + { 0x11f50, 0x11f5a }, { 0x11fb0, 0x11fb0 }, { 0x12000, 0x12399 }, { 0x12400, 0x1246e }, { 0x12480, 0x12543 }, { 0x12f90, 0x12ff0 }, - { 0x13000, 0x1342e }, + { 0x13000, 0x1342f }, + { 0x13440, 0x13455 }, + { 0x13460, 0x143fa }, { 0x14400, 0x14646 }, + { 0x16100, 0x16139 }, { 0x16800, 0x16a38 }, { 0x16a40, 0x16a5e }, { 0x16a60, 0x16a69 }, @@ -1320,6 +1389,8 @@ inline constexpr CharRange xid_continue[] = { { 0x16b50, 0x16b59 }, { 0x16b63, 0x16b77 }, { 0x16b7d, 0x16b8f }, + { 0x16d40, 0x16d6c }, + { 0x16d70, 0x16d79 }, { 0x16e40, 0x16e7f }, { 0x16f00, 0x16f4a }, { 0x16f4f, 0x16f87 }, @@ -1329,12 +1400,14 @@ inline constexpr CharRange xid_continue[] = { { 0x16ff0, 0x16ff1 }, { 0x17000, 0x187f7 }, { 0x18800, 0x18cd5 }, - { 0x18d00, 0x18d08 }, + { 0x18cff, 0x18d08 }, { 0x1aff0, 0x1aff3 }, { 0x1aff5, 0x1affb }, { 0x1affd, 0x1affe }, { 0x1b000, 0x1b122 }, + { 0x1b132, 0x1b132 }, { 0x1b150, 0x1b152 }, + { 0x1b155, 0x1b155 }, { 0x1b164, 0x1b167 }, { 0x1b170, 0x1b2fb }, { 0x1bc00, 0x1bc6a }, @@ -1342,6 +1415,7 @@ inline constexpr CharRange xid_continue[] = { { 0x1bc80, 0x1bc88 }, { 0x1bc90, 0x1bc99 }, { 0x1bc9d, 0x1bc9e }, + { 0x1ccf0, 0x1ccf9 }, { 0x1cf00, 0x1cf2d }, { 0x1cf30, 0x1cf46 }, { 0x1d165, 0x1d169 }, @@ -1388,17 +1462,22 @@ inline constexpr CharRange xid_continue[] = { { 0x1da9b, 0x1da9f }, { 0x1daa1, 0x1daaf }, { 0x1df00, 0x1df1e }, + { 0x1df25, 0x1df2a }, { 0x1e000, 0x1e006 }, { 0x1e008, 0x1e018 }, { 0x1e01b, 0x1e021 }, { 0x1e023, 0x1e024 }, { 0x1e026, 0x1e02a }, + { 0x1e030, 0x1e06d }, + { 0x1e08f, 0x1e08f }, { 0x1e100, 0x1e12c }, { 0x1e130, 0x1e13d }, { 0x1e140, 0x1e149 }, { 0x1e14e, 0x1e14e }, { 0x1e290, 0x1e2ae }, { 0x1e2c0, 0x1e2f9 }, + { 0x1e4d0, 0x1e4f9 }, + { 0x1e5d0, 0x1e5fa }, { 0x1e7e0, 0x1e7e6 }, { 0x1e7e8, 0x1e7eb }, { 0x1e7ed, 0x1e7ee }, @@ -1442,16 +1521,18 @@ inline constexpr CharRange xid_continue[] = { { 0x1eeab, 0x1eebb }, { 0x1fbf0, 0x1fbf9 }, { 0x20000, 0x2a6df }, - { 0x2a700, 0x2b738 }, + { 0x2a700, 0x2b739 }, { 0x2b740, 0x2b81d }, { 0x2b820, 0x2cea1 }, { 0x2ceb0, 0x2ebe0 }, + { 0x2ebf0, 0x2ee5d }, { 0x2f800, 0x2fa1d }, { 0x30000, 0x3134a }, + { 0x31350, 0x323af }, { 0xe0100, 0xe01ef }, }; -inline constexpr CharRange uppercase_letter[] = { +constexpr inline CharRange uppercase_letter[] = { { 0x41, 0x5a }, { 0xc0, 0xd6 }, { 0xd8, 0xde }, @@ -1728,6 +1809,7 @@ inline constexpr CharRange uppercase_letter[] = { { 0x10c7, 0x10c7 }, { 0x10cd, 0x10cd }, { 0x13a0, 0x13f5 }, + { 0x1c89, 0x1c89 }, { 0x1c90, 0x1cba }, { 0x1cbd, 0x1cbf }, { 0x1e00, 0x1e00 }, @@ -1882,7 +1964,9 @@ inline constexpr CharRange uppercase_letter[] = { { 0x2130, 0x2133 }, { 0x213e, 0x213f }, { 0x2145, 0x2145 }, + { 0x2160, 0x216f }, { 0x2183, 0x2183 }, + { 0x24b6, 0x24cf }, { 0x2c00, 0x2c2f }, { 0x2c60, 0x2c60 }, { 0x2c62, 0x2c64 }, @@ -2052,9 +2136,12 @@ inline constexpr CharRange uppercase_letter[] = { { 0xa7c2, 0xa7c2 }, { 0xa7c4, 0xa7c7 }, { 0xa7c9, 0xa7c9 }, + { 0xa7cb, 0xa7cc }, { 0xa7d0, 0xa7d0 }, { 0xa7d6, 0xa7d6 }, { 0xa7d8, 0xa7d8 }, + { 0xa7da, 0xa7da }, + { 0xa7dc, 0xa7dc }, { 0xa7f5, 0xa7f5 }, { 0xff21, 0xff3a }, { 0x10400, 0x10427 }, @@ -2064,6 +2151,7 @@ inline constexpr CharRange uppercase_letter[] = { { 0x1058c, 0x10592 }, { 0x10594, 0x10595 }, { 0x10c80, 0x10cb2 }, + { 0x10d50, 0x10d65 }, { 0x118a0, 0x118bf }, { 0x16e40, 0x16e5f }, { 0x1d400, 0x1d419 }, @@ -2098,11 +2186,16 @@ inline constexpr CharRange uppercase_letter[] = { { 0x1d790, 0x1d7a8 }, { 0x1d7ca, 0x1d7ca }, { 0x1e900, 0x1e921 }, + { 0x1f130, 0x1f149 }, + { 0x1f150, 0x1f169 }, + { 0x1f170, 0x1f189 }, }; -inline constexpr CharRange lowercase_letter[] = { +constexpr inline CharRange lowercase_letter[] = { { 0x61, 0x7a }, + { 0xaa, 0xaa }, { 0xb5, 0xb5 }, + { 0xba, 0xba }, { 0xdf, 0xf6 }, { 0xf8, 0xff }, { 0x101, 0x101 }, @@ -2246,11 +2339,14 @@ inline constexpr CharRange lowercase_letter[] = { { 0x24b, 0x24b }, { 0x24d, 0x24d }, { 0x24f, 0x293 }, - { 0x295, 0x2af }, + { 0x295, 0x2b8 }, + { 0x2c0, 0x2c1 }, + { 0x2e0, 0x2e4 }, + { 0x345, 0x345 }, { 0x371, 0x371 }, { 0x373, 0x373 }, { 0x377, 0x377 }, - { 0x37b, 0x37d }, + { 0x37a, 0x37d }, { 0x390, 0x390 }, { 0x3ac, 0x3ce }, { 0x3d0, 0x3d1 }, @@ -2372,12 +2468,11 @@ inline constexpr CharRange lowercase_letter[] = { { 0x52f, 0x52f }, { 0x560, 0x588 }, { 0x10d0, 0x10fa }, - { 0x10fd, 0x10ff }, + { 0x10fc, 0x10ff }, { 0x13f8, 0x13fd }, { 0x1c80, 0x1c88 }, - { 0x1d00, 0x1d2b }, - { 0x1d6b, 0x1d77 }, - { 0x1d79, 0x1d9a }, + { 0x1c8a, 0x1c8a }, + { 0x1d00, 0x1dbf }, { 0x1e01, 0x1e01 }, { 0x1e03, 0x1e03 }, { 0x1e05, 0x1e05 }, @@ -2522,6 +2617,9 @@ inline constexpr CharRange lowercase_letter[] = { { 0x1fe0, 0x1fe7 }, { 0x1ff2, 0x1ff4 }, { 0x1ff6, 0x1ff7 }, + { 0x2071, 0x2071 }, + { 0x207f, 0x207f }, + { 0x2090, 0x209c }, { 0x210a, 0x210a }, { 0x210e, 0x210f }, { 0x2113, 0x2113 }, @@ -2531,7 +2629,9 @@ inline constexpr CharRange lowercase_letter[] = { { 0x213c, 0x213d }, { 0x2146, 0x2149 }, { 0x214e, 0x214e }, + { 0x2170, 0x217f }, { 0x2184, 0x2184 }, + { 0x24d0, 0x24e9 }, { 0x2c30, 0x2c5f }, { 0x2c61, 0x2c61 }, { 0x2c65, 0x2c66 }, @@ -2540,7 +2640,7 @@ inline constexpr CharRange lowercase_letter[] = { { 0x2c6c, 0x2c6c }, { 0x2c71, 0x2c71 }, { 0x2c73, 0x2c74 }, - { 0x2c76, 0x2c7b }, + { 0x2c76, 0x2c7d }, { 0x2c81, 0x2c81 }, { 0x2c83, 0x2c83 }, { 0x2c85, 0x2c85 }, @@ -2633,7 +2733,7 @@ inline constexpr CharRange lowercase_letter[] = { { 0xa695, 0xa695 }, { 0xa697, 0xa697 }, { 0xa699, 0xa699 }, - { 0xa69b, 0xa69b }, + { 0xa69b, 0xa69d }, { 0xa723, 0xa723 }, { 0xa725, 0xa725 }, { 0xa727, 0xa727 }, @@ -2671,8 +2771,7 @@ inline constexpr CharRange lowercase_letter[] = { { 0xa769, 0xa769 }, { 0xa76b, 0xa76b }, { 0xa76d, 0xa76d }, - { 0xa76f, 0xa76f }, - { 0xa771, 0xa778 }, + { 0xa76f, 0xa778 }, { 0xa77a, 0xa77a }, { 0xa77c, 0xa77c }, { 0xa77f, 0xa77f }, @@ -2705,15 +2804,18 @@ inline constexpr CharRange lowercase_letter[] = { { 0xa7c3, 0xa7c3 }, { 0xa7c8, 0xa7c8 }, { 0xa7ca, 0xa7ca }, + { 0xa7cd, 0xa7cd }, { 0xa7d1, 0xa7d1 }, { 0xa7d3, 0xa7d3 }, { 0xa7d5, 0xa7d5 }, { 0xa7d7, 0xa7d7 }, { 0xa7d9, 0xa7d9 }, + { 0xa7db, 0xa7db }, + { 0xa7f2, 0xa7f4 }, { 0xa7f6, 0xa7f6 }, - { 0xa7fa, 0xa7fa }, + { 0xa7f8, 0xa7fa }, { 0xab30, 0xab5a }, - { 0xab60, 0xab68 }, + { 0xab5c, 0xab69 }, { 0xab70, 0xabbf }, { 0xfb00, 0xfb06 }, { 0xfb13, 0xfb17 }, @@ -2724,7 +2826,12 @@ inline constexpr CharRange lowercase_letter[] = { { 0x105a3, 0x105b1 }, { 0x105b3, 0x105b9 }, { 0x105bb, 0x105bc }, + { 0x10780, 0x10780 }, + { 0x10783, 0x10785 }, + { 0x10787, 0x107b0 }, + { 0x107b2, 0x107ba }, { 0x10cc0, 0x10cf2 }, + { 0x10d70, 0x10d85 }, { 0x118c0, 0x118df }, { 0x16e60, 0x16e7f }, { 0x1d41a, 0x1d433 }, @@ -2758,10 +2865,11 @@ inline constexpr CharRange lowercase_letter[] = { { 0x1df00, 0x1df09 }, { 0x1df0b, 0x1df1e }, { 0x1df25, 0x1df2a }, + { 0x1e030, 0x1e06d }, { 0x1e922, 0x1e943 }, }; -inline constexpr CharRange unicode_letter[] = { +constexpr inline CharRange unicode_letter[] = { { 0x41, 0x5a }, { 0x61, 0x7a }, { 0xaa, 0xaa }, @@ -2774,7 +2882,8 @@ inline constexpr CharRange unicode_letter[] = { { 0x2e0, 0x2e4 }, { 0x2ec, 0x2ec }, { 0x2ee, 0x2ee }, - { 0x370, 0x374 }, + { 0x345, 0x345 }, + { 0x363, 0x374 }, { 0x376, 0x377 }, { 0x37a, 0x37d }, { 0x37f, 0x37f }, @@ -2788,49 +2897,58 @@ inline constexpr CharRange unicode_letter[] = { { 0x531, 0x556 }, { 0x559, 0x559 }, { 0x560, 0x588 }, + { 0x5b0, 0x5bd }, + { 0x5bf, 0x5bf }, + { 0x5c1, 0x5c2 }, + { 0x5c4, 0x5c5 }, + { 0x5c7, 0x5c7 }, { 0x5d0, 0x5ea }, { 0x5ef, 0x5f2 }, - { 0x620, 0x64a }, - { 0x66e, 0x66f }, - { 0x671, 0x6d3 }, - { 0x6d5, 0x6d5 }, - { 0x6e5, 0x6e6 }, - { 0x6ee, 0x6ef }, + { 0x610, 0x61a }, + { 0x620, 0x657 }, + { 0x659, 0x65f }, + { 0x66e, 0x6d3 }, + { 0x6d5, 0x6dc }, + { 0x6e1, 0x6e8 }, + { 0x6ed, 0x6ef }, { 0x6fa, 0x6fc }, { 0x6ff, 0x6ff }, - { 0x710, 0x710 }, - { 0x712, 0x72f }, - { 0x74d, 0x7a5 }, - { 0x7b1, 0x7b1 }, + { 0x710, 0x73f }, + { 0x74d, 0x7b1 }, { 0x7ca, 0x7ea }, { 0x7f4, 0x7f5 }, { 0x7fa, 0x7fa }, - { 0x800, 0x815 }, - { 0x81a, 0x81a }, - { 0x824, 0x824 }, - { 0x828, 0x828 }, + { 0x800, 0x817 }, + { 0x81a, 0x82c }, { 0x840, 0x858 }, { 0x860, 0x86a }, { 0x870, 0x887 }, { 0x889, 0x88e }, + { 0x897, 0x897 }, { 0x8a0, 0x8c9 }, - { 0x904, 0x939 }, - { 0x93d, 0x93d }, - { 0x950, 0x950 }, - { 0x958, 0x961 }, - { 0x971, 0x980 }, + { 0x8d4, 0x8df }, + { 0x8e3, 0x8e9 }, + { 0x8f0, 0x93b }, + { 0x93d, 0x94c }, + { 0x94e, 0x950 }, + { 0x955, 0x963 }, + { 0x971, 0x983 }, { 0x985, 0x98c }, { 0x98f, 0x990 }, { 0x993, 0x9a8 }, { 0x9aa, 0x9b0 }, { 0x9b2, 0x9b2 }, { 0x9b6, 0x9b9 }, - { 0x9bd, 0x9bd }, + { 0x9bd, 0x9c4 }, + { 0x9c7, 0x9c8 }, + { 0x9cb, 0x9cc }, { 0x9ce, 0x9ce }, + { 0x9d7, 0x9d7 }, { 0x9dc, 0x9dd }, - { 0x9df, 0x9e1 }, + { 0x9df, 0x9e3 }, { 0x9f0, 0x9f1 }, { 0x9fc, 0x9fc }, + { 0xa01, 0xa03 }, { 0xa05, 0xa0a }, { 0xa0f, 0xa10 }, { 0xa13, 0xa28 }, @@ -2838,30 +2956,41 @@ inline constexpr CharRange unicode_letter[] = { { 0xa32, 0xa33 }, { 0xa35, 0xa36 }, { 0xa38, 0xa39 }, + { 0xa3e, 0xa42 }, + { 0xa47, 0xa48 }, + { 0xa4b, 0xa4c }, + { 0xa51, 0xa51 }, { 0xa59, 0xa5c }, { 0xa5e, 0xa5e }, - { 0xa72, 0xa74 }, + { 0xa70, 0xa75 }, + { 0xa81, 0xa83 }, { 0xa85, 0xa8d }, { 0xa8f, 0xa91 }, { 0xa93, 0xaa8 }, { 0xaaa, 0xab0 }, { 0xab2, 0xab3 }, { 0xab5, 0xab9 }, - { 0xabd, 0xabd }, + { 0xabd, 0xac5 }, + { 0xac7, 0xac9 }, + { 0xacb, 0xacc }, { 0xad0, 0xad0 }, - { 0xae0, 0xae1 }, - { 0xaf9, 0xaf9 }, + { 0xae0, 0xae3 }, + { 0xaf9, 0xafc }, + { 0xb01, 0xb03 }, { 0xb05, 0xb0c }, { 0xb0f, 0xb10 }, { 0xb13, 0xb28 }, { 0xb2a, 0xb30 }, { 0xb32, 0xb33 }, { 0xb35, 0xb39 }, - { 0xb3d, 0xb3d }, + { 0xb3d, 0xb44 }, + { 0xb47, 0xb48 }, + { 0xb4b, 0xb4c }, + { 0xb56, 0xb57 }, { 0xb5c, 0xb5d }, - { 0xb5f, 0xb61 }, + { 0xb5f, 0xb63 }, { 0xb71, 0xb71 }, - { 0xb83, 0xb83 }, + { 0xb82, 0xb83 }, { 0xb85, 0xb8a }, { 0xb8e, 0xb90 }, { 0xb92, 0xb95 }, @@ -2871,65 +3000,80 @@ inline constexpr CharRange unicode_letter[] = { { 0xba3, 0xba4 }, { 0xba8, 0xbaa }, { 0xbae, 0xbb9 }, + { 0xbbe, 0xbc2 }, + { 0xbc6, 0xbc8 }, + { 0xbca, 0xbcc }, { 0xbd0, 0xbd0 }, - { 0xc05, 0xc0c }, + { 0xbd7, 0xbd7 }, + { 0xc00, 0xc0c }, { 0xc0e, 0xc10 }, { 0xc12, 0xc28 }, { 0xc2a, 0xc39 }, - { 0xc3d, 0xc3d }, + { 0xc3d, 0xc44 }, + { 0xc46, 0xc48 }, + { 0xc4a, 0xc4c }, + { 0xc55, 0xc56 }, { 0xc58, 0xc5a }, { 0xc5d, 0xc5d }, - { 0xc60, 0xc61 }, - { 0xc80, 0xc80 }, + { 0xc60, 0xc63 }, + { 0xc80, 0xc83 }, { 0xc85, 0xc8c }, { 0xc8e, 0xc90 }, { 0xc92, 0xca8 }, { 0xcaa, 0xcb3 }, { 0xcb5, 0xcb9 }, - { 0xcbd, 0xcbd }, + { 0xcbd, 0xcc4 }, + { 0xcc6, 0xcc8 }, + { 0xcca, 0xccc }, + { 0xcd5, 0xcd6 }, { 0xcdd, 0xcde }, - { 0xce0, 0xce1 }, - { 0xcf1, 0xcf2 }, - { 0xd04, 0xd0c }, + { 0xce0, 0xce3 }, + { 0xcf1, 0xcf3 }, + { 0xd00, 0xd0c }, { 0xd0e, 0xd10 }, { 0xd12, 0xd3a }, - { 0xd3d, 0xd3d }, + { 0xd3d, 0xd44 }, + { 0xd46, 0xd48 }, + { 0xd4a, 0xd4c }, { 0xd4e, 0xd4e }, - { 0xd54, 0xd56 }, - { 0xd5f, 0xd61 }, + { 0xd54, 0xd57 }, + { 0xd5f, 0xd63 }, { 0xd7a, 0xd7f }, + { 0xd81, 0xd83 }, { 0xd85, 0xd96 }, { 0xd9a, 0xdb1 }, { 0xdb3, 0xdbb }, { 0xdbd, 0xdbd }, { 0xdc0, 0xdc6 }, - { 0xe01, 0xe30 }, - { 0xe32, 0xe33 }, + { 0xdcf, 0xdd4 }, + { 0xdd6, 0xdd6 }, + { 0xdd8, 0xddf }, + { 0xdf2, 0xdf3 }, + { 0xe01, 0xe3a }, { 0xe40, 0xe46 }, + { 0xe4d, 0xe4d }, { 0xe81, 0xe82 }, { 0xe84, 0xe84 }, { 0xe86, 0xe8a }, { 0xe8c, 0xea3 }, { 0xea5, 0xea5 }, - { 0xea7, 0xeb0 }, - { 0xeb2, 0xeb3 }, - { 0xebd, 0xebd }, + { 0xea7, 0xeb9 }, + { 0xebb, 0xebd }, { 0xec0, 0xec4 }, { 0xec6, 0xec6 }, + { 0xecd, 0xecd }, { 0xedc, 0xedf }, { 0xf00, 0xf00 }, { 0xf40, 0xf47 }, { 0xf49, 0xf6c }, - { 0xf88, 0xf8c }, - { 0x1000, 0x102a }, - { 0x103f, 0x103f }, - { 0x1050, 0x1055 }, - { 0x105a, 0x105d }, - { 0x1061, 0x1061 }, - { 0x1065, 0x1066 }, - { 0x106e, 0x1070 }, - { 0x1075, 0x1081 }, - { 0x108e, 0x108e }, + { 0xf71, 0xf83 }, + { 0xf88, 0xf97 }, + { 0xf99, 0xfbc }, + { 0x1000, 0x1036 }, + { 0x1038, 0x1038 }, + { 0x103b, 0x103f }, + { 0x1050, 0x108f }, + { 0x109a, 0x109d }, { 0x10a0, 0x10c5 }, { 0x10c7, 0x10c7 }, { 0x10cd, 0x10cd }, @@ -2957,37 +3101,44 @@ inline constexpr CharRange unicode_letter[] = { { 0x166f, 0x167f }, { 0x1681, 0x169a }, { 0x16a0, 0x16ea }, - { 0x16f1, 0x16f8 }, - { 0x1700, 0x1711 }, - { 0x171f, 0x1731 }, - { 0x1740, 0x1751 }, + { 0x16ee, 0x16f8 }, + { 0x1700, 0x1713 }, + { 0x171f, 0x1733 }, + { 0x1740, 0x1753 }, { 0x1760, 0x176c }, { 0x176e, 0x1770 }, + { 0x1772, 0x1773 }, { 0x1780, 0x17b3 }, + { 0x17b6, 0x17c8 }, { 0x17d7, 0x17d7 }, { 0x17dc, 0x17dc }, { 0x1820, 0x1878 }, - { 0x1880, 0x1884 }, - { 0x1887, 0x18a8 }, - { 0x18aa, 0x18aa }, + { 0x1880, 0x18aa }, { 0x18b0, 0x18f5 }, { 0x1900, 0x191e }, + { 0x1920, 0x192b }, + { 0x1930, 0x1938 }, { 0x1950, 0x196d }, { 0x1970, 0x1974 }, { 0x1980, 0x19ab }, { 0x19b0, 0x19c9 }, - { 0x1a00, 0x1a16 }, - { 0x1a20, 0x1a54 }, + { 0x1a00, 0x1a1b }, + { 0x1a20, 0x1a5e }, + { 0x1a61, 0x1a74 }, { 0x1aa7, 0x1aa7 }, - { 0x1b05, 0x1b33 }, + { 0x1abf, 0x1ac0 }, + { 0x1acc, 0x1ace }, + { 0x1b00, 0x1b33 }, + { 0x1b35, 0x1b43 }, { 0x1b45, 0x1b4c }, - { 0x1b83, 0x1ba0 }, - { 0x1bae, 0x1baf }, + { 0x1b80, 0x1ba9 }, + { 0x1bac, 0x1baf }, { 0x1bba, 0x1be5 }, - { 0x1c00, 0x1c23 }, + { 0x1be7, 0x1bf1 }, + { 0x1c00, 0x1c36 }, { 0x1c4d, 0x1c4f }, { 0x1c5a, 0x1c7d }, - { 0x1c80, 0x1c88 }, + { 0x1c80, 0x1c8a }, { 0x1c90, 0x1cba }, { 0x1cbd, 0x1cbf }, { 0x1ce9, 0x1cec }, @@ -2995,6 +3146,7 @@ inline constexpr CharRange unicode_letter[] = { { 0x1cf5, 0x1cf6 }, { 0x1cfa, 0x1cfa }, { 0x1d00, 0x1dbf }, + { 0x1dd3, 0x1df4 }, { 0x1e00, 0x1f15 }, { 0x1f18, 0x1f1d }, { 0x1f20, 0x1f45 }, @@ -3030,7 +3182,8 @@ inline constexpr CharRange unicode_letter[] = { { 0x213c, 0x213f }, { 0x2145, 0x2149 }, { 0x214e, 0x214e }, - { 0x2183, 0x2184 }, + { 0x2160, 0x2188 }, + { 0x24b6, 0x24e9 }, { 0x2c00, 0x2ce4 }, { 0x2ceb, 0x2cee }, { 0x2cf2, 0x2cf3 }, @@ -3048,10 +3201,12 @@ inline constexpr CharRange unicode_letter[] = { { 0x2dc8, 0x2dce }, { 0x2dd0, 0x2dd6 }, { 0x2dd8, 0x2dde }, + { 0x2de0, 0x2dff }, { 0x2e2f, 0x2e2f }, - { 0x3005, 0x3006 }, + { 0x3005, 0x3007 }, + { 0x3021, 0x3029 }, { 0x3031, 0x3035 }, - { 0x303b, 0x303c }, + { 0x3038, 0x303c }, { 0x3041, 0x3096 }, { 0x309d, 0x309f }, { 0x30a1, 0x30fa }, @@ -3067,45 +3222,39 @@ inline constexpr CharRange unicode_letter[] = { { 0xa610, 0xa61f }, { 0xa62a, 0xa62b }, { 0xa640, 0xa66e }, - { 0xa67f, 0xa69d }, - { 0xa6a0, 0xa6e5 }, + { 0xa674, 0xa67b }, + { 0xa67f, 0xa6ef }, { 0xa717, 0xa71f }, { 0xa722, 0xa788 }, - { 0xa78b, 0xa7ca }, + { 0xa78b, 0xa7cd }, { 0xa7d0, 0xa7d1 }, { 0xa7d3, 0xa7d3 }, - { 0xa7d5, 0xa7d9 }, - { 0xa7f2, 0xa801 }, - { 0xa803, 0xa805 }, - { 0xa807, 0xa80a }, - { 0xa80c, 0xa822 }, + { 0xa7d5, 0xa7dc }, + { 0xa7f2, 0xa805 }, + { 0xa807, 0xa827 }, { 0xa840, 0xa873 }, - { 0xa882, 0xa8b3 }, + { 0xa880, 0xa8c3 }, + { 0xa8c5, 0xa8c5 }, { 0xa8f2, 0xa8f7 }, { 0xa8fb, 0xa8fb }, - { 0xa8fd, 0xa8fe }, - { 0xa90a, 0xa925 }, - { 0xa930, 0xa946 }, + { 0xa8fd, 0xa8ff }, + { 0xa90a, 0xa92a }, + { 0xa930, 0xa952 }, { 0xa960, 0xa97c }, - { 0xa984, 0xa9b2 }, + { 0xa980, 0xa9b2 }, + { 0xa9b4, 0xa9bf }, { 0xa9cf, 0xa9cf }, - { 0xa9e0, 0xa9e4 }, - { 0xa9e6, 0xa9ef }, + { 0xa9e0, 0xa9ef }, { 0xa9fa, 0xa9fe }, - { 0xaa00, 0xaa28 }, - { 0xaa40, 0xaa42 }, - { 0xaa44, 0xaa4b }, + { 0xaa00, 0xaa36 }, + { 0xaa40, 0xaa4d }, { 0xaa60, 0xaa76 }, - { 0xaa7a, 0xaa7a }, - { 0xaa7e, 0xaaaf }, - { 0xaab1, 0xaab1 }, - { 0xaab5, 0xaab6 }, - { 0xaab9, 0xaabd }, + { 0xaa7a, 0xaabe }, { 0xaac0, 0xaac0 }, { 0xaac2, 0xaac2 }, { 0xaadb, 0xaadd }, - { 0xaae0, 0xaaea }, - { 0xaaf2, 0xaaf4 }, + { 0xaae0, 0xaaef }, + { 0xaaf2, 0xaaf5 }, { 0xab01, 0xab06 }, { 0xab09, 0xab0e }, { 0xab11, 0xab16 }, @@ -3113,7 +3262,7 @@ inline constexpr CharRange unicode_letter[] = { { 0xab28, 0xab2e }, { 0xab30, 0xab5a }, { 0xab5c, 0xab69 }, - { 0xab70, 0xabe2 }, + { 0xab70, 0xabea }, { 0xac00, 0xd7a3 }, { 0xd7b0, 0xd7c6 }, { 0xd7cb, 0xd7fb }, @@ -3121,8 +3270,7 @@ inline constexpr CharRange unicode_letter[] = { { 0xfa70, 0xfad9 }, { 0xfb00, 0xfb06 }, { 0xfb13, 0xfb17 }, - { 0xfb1d, 0xfb1d }, - { 0xfb1f, 0xfb28 }, + { 0xfb1d, 0xfb28 }, { 0xfb2a, 0xfb36 }, { 0xfb38, 0xfb3c }, { 0xfb3e, 0xfb3e }, @@ -3149,15 +3297,16 @@ inline constexpr CharRange unicode_letter[] = { { 0x1003f, 0x1004d }, { 0x10050, 0x1005d }, { 0x10080, 0x100fa }, + { 0x10140, 0x10174 }, { 0x10280, 0x1029c }, { 0x102a0, 0x102d0 }, { 0x10300, 0x1031f }, - { 0x1032d, 0x10340 }, - { 0x10342, 0x10349 }, - { 0x10350, 0x10375 }, + { 0x1032d, 0x1034a }, + { 0x10350, 0x1037a }, { 0x10380, 0x1039d }, { 0x103a0, 0x103c3 }, { 0x103c8, 0x103cf }, + { 0x103d1, 0x103d5 }, { 0x10400, 0x1049d }, { 0x104b0, 0x104d3 }, { 0x104d8, 0x104fb }, @@ -3171,6 +3320,7 @@ inline constexpr CharRange unicode_letter[] = { { 0x105a3, 0x105b1 }, { 0x105b3, 0x105b9 }, { 0x105bb, 0x105bc }, + { 0x105c0, 0x105f3 }, { 0x10600, 0x10736 }, { 0x10740, 0x10755 }, { 0x10760, 0x10767 }, @@ -3191,8 +3341,9 @@ inline constexpr CharRange unicode_letter[] = { { 0x10920, 0x10939 }, { 0x10980, 0x109b7 }, { 0x109be, 0x109bf }, - { 0x10a00, 0x10a00 }, - { 0x10a10, 0x10a13 }, + { 0x10a00, 0x10a03 }, + { 0x10a05, 0x10a06 }, + { 0x10a0c, 0x10a13 }, { 0x10a15, 0x10a17 }, { 0x10a19, 0x10a35 }, { 0x10a60, 0x10a7c }, @@ -3206,104 +3357,143 @@ inline constexpr CharRange unicode_letter[] = { { 0x10c00, 0x10c48 }, { 0x10c80, 0x10cb2 }, { 0x10cc0, 0x10cf2 }, - { 0x10d00, 0x10d23 }, + { 0x10d00, 0x10d27 }, + { 0x10d4a, 0x10d65 }, + { 0x10d69, 0x10d69 }, + { 0x10d6f, 0x10d85 }, { 0x10e80, 0x10ea9 }, + { 0x10eab, 0x10eac }, { 0x10eb0, 0x10eb1 }, + { 0x10ec2, 0x10ec4 }, + { 0x10efc, 0x10efc }, { 0x10f00, 0x10f1c }, { 0x10f27, 0x10f27 }, { 0x10f30, 0x10f45 }, { 0x10f70, 0x10f81 }, { 0x10fb0, 0x10fc4 }, { 0x10fe0, 0x10ff6 }, - { 0x11003, 0x11037 }, - { 0x11071, 0x11072 }, - { 0x11075, 0x11075 }, - { 0x11083, 0x110af }, + { 0x11000, 0x11045 }, + { 0x11071, 0x11075 }, + { 0x11080, 0x110b8 }, + { 0x110c2, 0x110c2 }, { 0x110d0, 0x110e8 }, - { 0x11103, 0x11126 }, - { 0x11144, 0x11144 }, - { 0x11147, 0x11147 }, + { 0x11100, 0x11132 }, + { 0x11144, 0x11147 }, { 0x11150, 0x11172 }, { 0x11176, 0x11176 }, - { 0x11183, 0x111b2 }, + { 0x11180, 0x111bf }, { 0x111c1, 0x111c4 }, + { 0x111ce, 0x111cf }, { 0x111da, 0x111da }, { 0x111dc, 0x111dc }, { 0x11200, 0x11211 }, - { 0x11213, 0x1122b }, - { 0x1123f, 0x11240 }, + { 0x11213, 0x11234 }, + { 0x11237, 0x11237 }, + { 0x1123e, 0x11241 }, { 0x11280, 0x11286 }, { 0x11288, 0x11288 }, { 0x1128a, 0x1128d }, { 0x1128f, 0x1129d }, { 0x1129f, 0x112a8 }, - { 0x112b0, 0x112de }, + { 0x112b0, 0x112e8 }, + { 0x11300, 0x11303 }, { 0x11305, 0x1130c }, { 0x1130f, 0x11310 }, { 0x11313, 0x11328 }, { 0x1132a, 0x11330 }, { 0x11332, 0x11333 }, { 0x11335, 0x11339 }, - { 0x1133d, 0x1133d }, + { 0x1133d, 0x11344 }, + { 0x11347, 0x11348 }, + { 0x1134b, 0x1134c }, { 0x11350, 0x11350 }, - { 0x1135d, 0x11361 }, - { 0x11400, 0x11434 }, + { 0x11357, 0x11357 }, + { 0x1135d, 0x11363 }, + { 0x11380, 0x11389 }, + { 0x1138b, 0x1138b }, + { 0x1138e, 0x1138e }, + { 0x11390, 0x113b5 }, + { 0x113b7, 0x113c0 }, + { 0x113c2, 0x113c2 }, + { 0x113c5, 0x113c5 }, + { 0x113c7, 0x113ca }, + { 0x113cc, 0x113cd }, + { 0x113d1, 0x113d1 }, + { 0x113d3, 0x113d3 }, + { 0x11400, 0x11441 }, + { 0x11443, 0x11445 }, { 0x11447, 0x1144a }, { 0x1145f, 0x11461 }, - { 0x11480, 0x114af }, + { 0x11480, 0x114c1 }, { 0x114c4, 0x114c5 }, { 0x114c7, 0x114c7 }, - { 0x11580, 0x115ae }, - { 0x115d8, 0x115db }, - { 0x11600, 0x1162f }, + { 0x11580, 0x115b5 }, + { 0x115b8, 0x115be }, + { 0x115d8, 0x115dd }, + { 0x11600, 0x1163e }, + { 0x11640, 0x11640 }, { 0x11644, 0x11644 }, - { 0x11680, 0x116aa }, + { 0x11680, 0x116b5 }, { 0x116b8, 0x116b8 }, { 0x11700, 0x1171a }, + { 0x1171d, 0x1172a }, { 0x11740, 0x11746 }, - { 0x11800, 0x1182b }, + { 0x11800, 0x11838 }, { 0x118a0, 0x118df }, { 0x118ff, 0x11906 }, { 0x11909, 0x11909 }, { 0x1190c, 0x11913 }, { 0x11915, 0x11916 }, - { 0x11918, 0x1192f }, - { 0x1193f, 0x1193f }, - { 0x11941, 0x11941 }, + { 0x11918, 0x11935 }, + { 0x11937, 0x11938 }, + { 0x1193b, 0x1193c }, + { 0x1193f, 0x11942 }, { 0x119a0, 0x119a7 }, - { 0x119aa, 0x119d0 }, + { 0x119aa, 0x119d7 }, + { 0x119da, 0x119df }, { 0x119e1, 0x119e1 }, - { 0x119e3, 0x119e3 }, - { 0x11a00, 0x11a00 }, - { 0x11a0b, 0x11a32 }, - { 0x11a3a, 0x11a3a }, - { 0x11a50, 0x11a50 }, - { 0x11a5c, 0x11a89 }, + { 0x119e3, 0x119e4 }, + { 0x11a00, 0x11a32 }, + { 0x11a35, 0x11a3e }, + { 0x11a50, 0x11a97 }, { 0x11a9d, 0x11a9d }, { 0x11ab0, 0x11af8 }, + { 0x11bc0, 0x11be0 }, { 0x11c00, 0x11c08 }, - { 0x11c0a, 0x11c2e }, + { 0x11c0a, 0x11c36 }, + { 0x11c38, 0x11c3e }, { 0x11c40, 0x11c40 }, { 0x11c72, 0x11c8f }, + { 0x11c92, 0x11ca7 }, + { 0x11ca9, 0x11cb6 }, { 0x11d00, 0x11d06 }, { 0x11d08, 0x11d09 }, - { 0x11d0b, 0x11d30 }, - { 0x11d46, 0x11d46 }, + { 0x11d0b, 0x11d36 }, + { 0x11d3a, 0x11d3a }, + { 0x11d3c, 0x11d3d }, + { 0x11d3f, 0x11d41 }, + { 0x11d43, 0x11d43 }, + { 0x11d46, 0x11d47 }, { 0x11d60, 0x11d65 }, { 0x11d67, 0x11d68 }, - { 0x11d6a, 0x11d89 }, + { 0x11d6a, 0x11d8e }, + { 0x11d90, 0x11d91 }, + { 0x11d93, 0x11d96 }, { 0x11d98, 0x11d98 }, - { 0x11ee0, 0x11ef2 }, - { 0x11f02, 0x11f02 }, - { 0x11f04, 0x11f10 }, - { 0x11f12, 0x11f33 }, + { 0x11ee0, 0x11ef6 }, + { 0x11f00, 0x11f10 }, + { 0x11f12, 0x11f3a }, + { 0x11f3e, 0x11f40 }, { 0x11fb0, 0x11fb0 }, { 0x12000, 0x12399 }, + { 0x12400, 0x1246e }, { 0x12480, 0x12543 }, { 0x12f90, 0x12ff0 }, { 0x13000, 0x1342f }, { 0x13441, 0x13446 }, + { 0x13460, 0x143fa }, { 0x14400, 0x14646 }, + { 0x16100, 0x1612e }, { 0x16800, 0x16a38 }, { 0x16a40, 0x16a5e }, { 0x16a70, 0x16abe }, @@ -3312,15 +3502,17 @@ inline constexpr CharRange unicode_letter[] = { { 0x16b40, 0x16b43 }, { 0x16b63, 0x16b77 }, { 0x16b7d, 0x16b8f }, + { 0x16d40, 0x16d6c }, { 0x16e40, 0x16e7f }, { 0x16f00, 0x16f4a }, - { 0x16f50, 0x16f50 }, - { 0x16f93, 0x16f9f }, + { 0x16f4f, 0x16f87 }, + { 0x16f8f, 0x16f9f }, { 0x16fe0, 0x16fe1 }, { 0x16fe3, 0x16fe3 }, + { 0x16ff0, 0x16ff1 }, { 0x17000, 0x187f7 }, { 0x18800, 0x18cd5 }, - { 0x18d00, 0x18d08 }, + { 0x18cff, 0x18d08 }, { 0x1aff0, 0x1aff3 }, { 0x1aff5, 0x1affb }, { 0x1affd, 0x1affe }, @@ -3334,6 +3526,7 @@ inline constexpr CharRange unicode_letter[] = { { 0x1bc70, 0x1bc7c }, { 0x1bc80, 0x1bc88 }, { 0x1bc90, 0x1bc99 }, + { 0x1bc9e, 0x1bc9e }, { 0x1d400, 0x1d454 }, { 0x1d456, 0x1d49c }, { 0x1d49e, 0x1d49f }, @@ -3366,19 +3559,28 @@ inline constexpr CharRange unicode_letter[] = { { 0x1d7c4, 0x1d7cb }, { 0x1df00, 0x1df1e }, { 0x1df25, 0x1df2a }, + { 0x1e000, 0x1e006 }, + { 0x1e008, 0x1e018 }, + { 0x1e01b, 0x1e021 }, + { 0x1e023, 0x1e024 }, + { 0x1e026, 0x1e02a }, { 0x1e030, 0x1e06d }, + { 0x1e08f, 0x1e08f }, { 0x1e100, 0x1e12c }, { 0x1e137, 0x1e13d }, { 0x1e14e, 0x1e14e }, { 0x1e290, 0x1e2ad }, { 0x1e2c0, 0x1e2eb }, { 0x1e4d0, 0x1e4eb }, + { 0x1e5d0, 0x1e5ed }, + { 0x1e5f0, 0x1e5f0 }, { 0x1e7e0, 0x1e7e6 }, { 0x1e7e8, 0x1e7eb }, { 0x1e7ed, 0x1e7ee }, { 0x1e7f0, 0x1e7fe }, { 0x1e800, 0x1e8c4 }, { 0x1e900, 0x1e943 }, + { 0x1e947, 0x1e947 }, { 0x1e94b, 0x1e94b }, { 0x1ee00, 0x1ee03 }, { 0x1ee05, 0x1ee1f }, @@ -3413,6 +3615,9 @@ inline constexpr CharRange unicode_letter[] = { { 0x1eea1, 0x1eea3 }, { 0x1eea5, 0x1eea9 }, { 0x1eeab, 0x1eebb }, + { 0x1f130, 0x1f149 }, + { 0x1f150, 0x1f169 }, + { 0x1f170, 0x1f189 }, { 0x20000, 0x2a6df }, { 0x2a700, 0x2b739 }, { 0x2b740, 0x2b81d }, diff --git a/core/string/char_utils.h b/core/string/char_utils.h index 4acb81253f..62ab4e9584 100644 --- a/core/string/char_utils.h +++ b/core/string/char_utils.h @@ -38,97 +38,97 @@ #define BSEARCH_CHAR_RANGE(m_array) \ int low = 0; \ int high = sizeof(m_array) / sizeof(m_array[0]) - 1; \ - int middle; \ + int middle = (low + high) / 2; \ \ while (low <= high) { \ - middle = (low + high) / 2; \ - \ - if (c < m_array[middle].start) { \ + if (p_char < m_array[middle].start) { \ high = middle - 1; \ - } else if (c > m_array[middle].end) { \ + } else if (p_char > m_array[middle].end) { \ low = middle + 1; \ } else { \ return true; \ } \ + \ + middle = (low + high) / 2; \ } \ \ return false -static _FORCE_INLINE_ bool is_unicode_identifier_start(char32_t c) { +constexpr bool is_unicode_identifier_start(char32_t p_char) { BSEARCH_CHAR_RANGE(xid_start); } -static _FORCE_INLINE_ bool is_unicode_identifier_continue(char32_t c) { +constexpr bool is_unicode_identifier_continue(char32_t p_char) { BSEARCH_CHAR_RANGE(xid_continue); } -static _FORCE_INLINE_ bool is_unicode_upper_case(char32_t c) { +constexpr bool is_unicode_upper_case(char32_t p_char) { BSEARCH_CHAR_RANGE(uppercase_letter); } -static _FORCE_INLINE_ bool is_unicode_lower_case(char32_t c) { +constexpr bool is_unicode_lower_case(char32_t p_char) { BSEARCH_CHAR_RANGE(lowercase_letter); } -static _FORCE_INLINE_ bool is_unicode_letter(char32_t c) { +constexpr bool is_unicode_letter(char32_t p_char) { BSEARCH_CHAR_RANGE(unicode_letter); } #undef BSEARCH_CHAR_RANGE -static _FORCE_INLINE_ bool is_ascii_upper_case(char32_t c) { - return (c >= 'A' && c <= 'Z'); +constexpr bool is_ascii_upper_case(char32_t p_char) { + return (p_char >= 'A' && p_char <= 'Z'); } -static _FORCE_INLINE_ bool is_ascii_lower_case(char32_t c) { - return (c >= 'a' && c <= 'z'); +constexpr bool is_ascii_lower_case(char32_t p_char) { + return (p_char >= 'a' && p_char <= 'z'); } -static _FORCE_INLINE_ bool is_digit(char32_t c) { - return (c >= '0' && c <= '9'); +constexpr bool is_digit(char32_t p_char) { + return (p_char >= '0' && p_char <= '9'); } -static _FORCE_INLINE_ bool is_hex_digit(char32_t c) { - return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +constexpr bool is_hex_digit(char32_t p_char) { + return (is_digit(p_char) || (p_char >= 'a' && p_char <= 'f') || (p_char >= 'A' && p_char <= 'F')); } -static _FORCE_INLINE_ bool is_binary_digit(char32_t c) { - return (c == '0' || c == '1'); +constexpr bool is_binary_digit(char32_t p_char) { + return (p_char == '0' || p_char == '1'); } -static _FORCE_INLINE_ bool is_ascii_alphabet_char(char32_t c) { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +constexpr bool is_ascii_alphabet_char(char32_t p_char) { + return (p_char >= 'a' && p_char <= 'z') || (p_char >= 'A' && p_char <= 'Z'); } -static _FORCE_INLINE_ bool is_ascii_alphanumeric_char(char32_t c) { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); +constexpr bool is_ascii_alphanumeric_char(char32_t p_char) { + return (p_char >= 'a' && p_char <= 'z') || (p_char >= 'A' && p_char <= 'Z') || (p_char >= '0' && p_char <= '9'); } -static _FORCE_INLINE_ bool is_ascii_identifier_char(char32_t c) { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; +constexpr bool is_ascii_identifier_char(char32_t p_char) { + return (p_char >= 'a' && p_char <= 'z') || (p_char >= 'A' && p_char <= 'Z') || (p_char >= '0' && p_char <= '9') || p_char == '_'; } -static _FORCE_INLINE_ bool is_symbol(char32_t c) { - return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' '); +constexpr bool is_symbol(char32_t p_char) { + return p_char != '_' && ((p_char >= '!' && p_char <= '/') || (p_char >= ':' && p_char <= '@') || (p_char >= '[' && p_char <= '`') || (p_char >= '{' && p_char <= '~') || p_char == '\t' || p_char == ' '); } -static _FORCE_INLINE_ bool is_control(char32_t p_char) { +constexpr bool is_control(char32_t p_char) { return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009f); } -static _FORCE_INLINE_ bool is_whitespace(char32_t p_char) { +constexpr bool is_whitespace(char32_t p_char) { return (p_char == ' ') || (p_char == 0x00a0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085); } -static _FORCE_INLINE_ bool is_linebreak(char32_t p_char) { +constexpr bool is_linebreak(char32_t p_char) { return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029); } -static _FORCE_INLINE_ bool is_punct(char32_t p_char) { +constexpr bool is_punct(char32_t p_char) { return (p_char >= ' ' && p_char <= '/') || (p_char >= ':' && p_char <= '@') || (p_char >= '[' && p_char <= '^') || (p_char == '`') || (p_char >= '{' && p_char <= '~') || (p_char >= 0x2000 && p_char <= 0x206f) || (p_char >= 0x3000 && p_char <= 0x303f); } -static _FORCE_INLINE_ bool is_underscore(char32_t p_char) { +constexpr bool is_underscore(char32_t p_char) { return (p_char == '_'); } diff --git a/core/string/node_path.cpp b/core/string/node_path.cpp index fdc72bc8dc..3faf3bb0c5 100644 --- a/core/string/node_path.cpp +++ b/core/string/node_path.cpp @@ -420,7 +420,7 @@ NodePath::NodePath(const String &p_path) { continue; // Allow end-of-path : } - ERR_FAIL_MSG("Invalid NodePath '" + p_path + "'."); + ERR_FAIL_MSG(vformat("Invalid NodePath '%s'.", p_path)); } subpath.push_back(str); diff --git a/core/string/translation_domain.cpp b/core/string/translation_domain.cpp index b44eb40366..cf6689efff 100644 --- a/core/string/translation_domain.cpp +++ b/core/string/translation_domain.cpp @@ -33,6 +33,170 @@ #include "core/string/translation.h" #include "core/string/translation_server.h" +struct _character_accent_pair { + const char32_t character; + const char32_t *accented_character; +}; + +static _character_accent_pair _character_to_accented[] = { + { 'A', U"Å" }, + { 'B', U"ß" }, + { 'C', U"Ç" }, + { 'D', U"Ð" }, + { 'E', U"É" }, + { 'F', U"F́" }, + { 'G', U"Ĝ" }, + { 'H', U"Ĥ" }, + { 'I', U"Ĩ" }, + { 'J', U"Ĵ" }, + { 'K', U"ĸ" }, + { 'L', U"Ł" }, + { 'M', U"Ḿ" }, + { 'N', U"й" }, + { 'O', U"Ö" }, + { 'P', U"Ṕ" }, + { 'Q', U"Q́" }, + { 'R', U"Ř" }, + { 'S', U"Ŝ" }, + { 'T', U"Ŧ" }, + { 'U', U"Ũ" }, + { 'V', U"Ṽ" }, + { 'W', U"Ŵ" }, + { 'X', U"X́" }, + { 'Y', U"Ÿ" }, + { 'Z', U"Ž" }, + { 'a', U"á" }, + { 'b', U"ḅ" }, + { 'c', U"ć" }, + { 'd', U"d́" }, + { 'e', U"é" }, + { 'f', U"f́" }, + { 'g', U"ǵ" }, + { 'h', U"h̀" }, + { 'i', U"í" }, + { 'j', U"ǰ" }, + { 'k', U"ḱ" }, + { 'l', U"ł" }, + { 'm', U"m̀" }, + { 'n', U"ή" }, + { 'o', U"ô" }, + { 'p', U"ṕ" }, + { 'q', U"q́" }, + { 'r', U"ŕ" }, + { 's', U"š" }, + { 't', U"ŧ" }, + { 'u', U"ü" }, + { 'v', U"ṽ" }, + { 'w', U"ŵ" }, + { 'x', U"x́" }, + { 'y', U"ý" }, + { 'z', U"ź" }, +}; + +String TranslationDomain::_get_override_string(const String &p_message) const { + String res; + for (int i = 0; i < p_message.length(); i++) { + if (pseudolocalization.skip_placeholders_enabled && _is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + res += '*'; + } + return res; +} + +String TranslationDomain::_double_vowels(const String &p_message) const { + String res; + for (int i = 0; i < p_message.length(); i++) { + if (pseudolocalization.skip_placeholders_enabled && _is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + res += p_message[i]; + if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' || + p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') { + res += p_message[i]; + } + } + return res; +} + +String TranslationDomain::_replace_with_accented_string(const String &p_message) const { + String res; + for (int i = 0; i < p_message.length(); i++) { + if (pseudolocalization.skip_placeholders_enabled && _is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + const char32_t *accented = _get_accented_version(p_message[i]); + if (accented) { + res += accented; + } else { + res += p_message[i]; + } + } + return res; +} + +String TranslationDomain::_wrap_with_fakebidi_characters(const String &p_message) const { + String res; + char32_t fakebidiprefix = U'\u202e'; + char32_t fakebidisuffix = U'\u202c'; + res += fakebidiprefix; + // The fake bidi unicode gets popped at every newline so pushing it back at every newline. + for (int i = 0; i < p_message.length(); i++) { + if (p_message[i] == '\n') { + res += fakebidisuffix; + res += p_message[i]; + res += fakebidiprefix; + } else if (pseudolocalization.skip_placeholders_enabled && _is_placeholder(p_message, i)) { + res += fakebidisuffix; + res += p_message[i]; + res += p_message[i + 1]; + res += fakebidiprefix; + i++; + } else { + res += p_message[i]; + } + } + res += fakebidisuffix; + return res; +} + +String TranslationDomain::_add_padding(const String &p_message, int p_length) const { + String underscores = String("_").repeat(p_length * pseudolocalization.expansion_ratio / 2); + String prefix = pseudolocalization.prefix + underscores; + String suffix = underscores + pseudolocalization.suffix; + + return prefix + p_message + suffix; +} + +const char32_t *TranslationDomain::_get_accented_version(char32_t p_character) const { + if (!is_ascii_alphabet_char(p_character)) { + return nullptr; + } + + for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) { + if (_character_to_accented[i].character == p_character) { + return _character_to_accented[i].accented_character; + } + } + + return nullptr; +} + +bool TranslationDomain::_is_placeholder(const String &p_message, int p_index) const { + return p_index < p_message.length() - 1 && p_message[p_index] == '%' && + (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' || + p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f'); +} + StringName TranslationDomain::get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_context) const { StringName res; int best_score = 0; @@ -129,9 +293,9 @@ StringName TranslationDomain::translate(const StringName &p_message, const Strin } if (!res) { - return p_message; + return pseudolocalization.enabled ? pseudolocalize(p_message) : p_message; } - return res; + return pseudolocalization.enabled ? pseudolocalize(res) : res; } StringName TranslationDomain::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { @@ -152,6 +316,104 @@ StringName TranslationDomain::translate_plural(const StringName &p_message, cons return res; } +bool TranslationDomain::is_pseudolocalization_enabled() const { + return pseudolocalization.enabled; +} + +void TranslationDomain::set_pseudolocalization_enabled(bool p_enabled) { + pseudolocalization.enabled = p_enabled; +} + +bool TranslationDomain::is_pseudolocalization_accents_enabled() const { + return pseudolocalization.accents_enabled; +} + +void TranslationDomain::set_pseudolocalization_accents_enabled(bool p_enabled) { + pseudolocalization.accents_enabled = p_enabled; +} + +bool TranslationDomain::is_pseudolocalization_double_vowels_enabled() const { + return pseudolocalization.double_vowels_enabled; +} + +void TranslationDomain::set_pseudolocalization_double_vowels_enabled(bool p_enabled) { + pseudolocalization.double_vowels_enabled = p_enabled; +} + +bool TranslationDomain::is_pseudolocalization_fake_bidi_enabled() const { + return pseudolocalization.fake_bidi_enabled; +} + +void TranslationDomain::set_pseudolocalization_fake_bidi_enabled(bool p_enabled) { + pseudolocalization.fake_bidi_enabled = p_enabled; +} + +bool TranslationDomain::is_pseudolocalization_override_enabled() const { + return pseudolocalization.override_enabled; +} + +void TranslationDomain::set_pseudolocalization_override_enabled(bool p_enabled) { + pseudolocalization.override_enabled = p_enabled; +} + +bool TranslationDomain::is_pseudolocalization_skip_placeholders_enabled() const { + return pseudolocalization.skip_placeholders_enabled; +} + +void TranslationDomain::set_pseudolocalization_skip_placeholders_enabled(bool p_enabled) { + pseudolocalization.skip_placeholders_enabled = p_enabled; +} + +float TranslationDomain::get_pseudolocalization_expansion_ratio() const { + return pseudolocalization.expansion_ratio; +} + +void TranslationDomain::set_pseudolocalization_expansion_ratio(float p_ratio) { + pseudolocalization.expansion_ratio = p_ratio; +} + +String TranslationDomain::get_pseudolocalization_prefix() const { + return pseudolocalization.prefix; +} + +void TranslationDomain::set_pseudolocalization_prefix(const String &p_prefix) { + pseudolocalization.prefix = p_prefix; +} + +String TranslationDomain::get_pseudolocalization_suffix() const { + return pseudolocalization.suffix; +} + +void TranslationDomain::set_pseudolocalization_suffix(const String &p_suffix) { + pseudolocalization.suffix = p_suffix; +} + +StringName TranslationDomain::pseudolocalize(const StringName &p_message) const { + if (p_message.is_empty()) { + return p_message; + } + + String message = p_message; + int length = message.length(); + if (pseudolocalization.override_enabled) { + message = _get_override_string(message); + } + + if (pseudolocalization.double_vowels_enabled) { + message = _double_vowels(message); + } + + if (pseudolocalization.accents_enabled) { + message = _replace_with_accented_string(message); + } + + if (pseudolocalization.fake_bidi_enabled) { + message = _wrap_with_fakebidi_characters(message); + } + + return _add_padding(message, length); +} + void TranslationDomain::_bind_methods() { ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationDomain::get_translation_object); ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationDomain::add_translation); @@ -159,6 +421,36 @@ void TranslationDomain::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &TranslationDomain::clear); ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationDomain::translate, DEFVAL(StringName())); ClassDB::bind_method(D_METHOD("translate_plural", "message", "message_plural", "n", "context"), &TranslationDomain::translate_plural, DEFVAL(StringName())); + + ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationDomain::is_pseudolocalization_enabled); + ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_enabled); + ClassDB::bind_method(D_METHOD("is_pseudolocalization_accents_enabled"), &TranslationDomain::is_pseudolocalization_accents_enabled); + ClassDB::bind_method(D_METHOD("set_pseudolocalization_accents_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_accents_enabled); + ClassDB::bind_method(D_METHOD("is_pseudolocalization_double_vowels_enabled"), &TranslationDomain::is_pseudolocalization_double_vowels_enabled); + ClassDB::bind_method(D_METHOD("set_pseudolocalization_double_vowels_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_double_vowels_enabled); + ClassDB::bind_method(D_METHOD("is_pseudolocalization_fake_bidi_enabled"), &TranslationDomain::is_pseudolocalization_fake_bidi_enabled); + ClassDB::bind_method(D_METHOD("set_pseudolocalization_fake_bidi_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_fake_bidi_enabled); + ClassDB::bind_method(D_METHOD("is_pseudolocalization_override_enabled"), &TranslationDomain::is_pseudolocalization_override_enabled); + ClassDB::bind_method(D_METHOD("set_pseudolocalization_override_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_override_enabled); + ClassDB::bind_method(D_METHOD("is_pseudolocalization_skip_placeholders_enabled"), &TranslationDomain::is_pseudolocalization_skip_placeholders_enabled); + ClassDB::bind_method(D_METHOD("set_pseudolocalization_skip_placeholders_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_skip_placeholders_enabled); + ClassDB::bind_method(D_METHOD("get_pseudolocalization_expansion_ratio"), &TranslationDomain::get_pseudolocalization_expansion_ratio); + ClassDB::bind_method(D_METHOD("set_pseudolocalization_expansion_ratio", "ratio"), &TranslationDomain::set_pseudolocalization_expansion_ratio); + ClassDB::bind_method(D_METHOD("get_pseudolocalization_prefix"), &TranslationDomain::get_pseudolocalization_prefix); + ClassDB::bind_method(D_METHOD("set_pseudolocalization_prefix", "prefix"), &TranslationDomain::set_pseudolocalization_prefix); + ClassDB::bind_method(D_METHOD("get_pseudolocalization_suffix"), &TranslationDomain::get_pseudolocalization_suffix); + ClassDB::bind_method(D_METHOD("set_pseudolocalization_suffix", "suffix"), &TranslationDomain::set_pseudolocalization_suffix); + ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationDomain::pseudolocalize); + + ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_accents_enabled"), "set_pseudolocalization_accents_enabled", "is_pseudolocalization_accents_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_double_vowels_enabled"), "set_pseudolocalization_double_vowels_enabled", "is_pseudolocalization_double_vowels_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_fake_bidi_enabled"), "set_pseudolocalization_fake_bidi_enabled", "is_pseudolocalization_fake_bidi_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_override_enabled"), "set_pseudolocalization_override_enabled", "is_pseudolocalization_override_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_skip_placeholders_enabled"), "set_pseudolocalization_skip_placeholders_enabled", "is_pseudolocalization_skip_placeholders_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::Type::FLOAT, "pseudolocalization_expansion_ratio"), "set_pseudolocalization_expansion_ratio", "get_pseudolocalization_expansion_ratio"); + ADD_PROPERTY(PropertyInfo(Variant::Type::STRING, "pseudolocalization_prefix"), "set_pseudolocalization_prefix", "get_pseudolocalization_prefix"); + ADD_PROPERTY(PropertyInfo(Variant::Type::STRING, "pseudolocalization_suffix"), "set_pseudolocalization_suffix", "get_pseudolocalization_suffix"); } TranslationDomain::TranslationDomain() { diff --git a/core/string/translation_domain.h b/core/string/translation_domain.h index 6139967217..55592d3b35 100644 --- a/core/string/translation_domain.h +++ b/core/string/translation_domain.h @@ -38,7 +38,28 @@ class Translation; class TranslationDomain : public RefCounted { GDCLASS(TranslationDomain, RefCounted); + struct PseudolocalizationConfig { + bool enabled = false; + bool accents_enabled = true; + bool double_vowels_enabled = false; + bool fake_bidi_enabled = false; + bool override_enabled = false; + bool skip_placeholders_enabled = true; + float expansion_ratio = 0.0; + String prefix = "["; + String suffix = "]"; + }; + HashSet<Ref<Translation>> translations; + PseudolocalizationConfig pseudolocalization; + + String _get_override_string(const String &p_message) const; + String _double_vowels(const String &p_message) const; + String _replace_with_accented_string(const String &p_message) const; + String _wrap_with_fakebidi_characters(const String &p_message) const; + String _add_padding(const String &p_message, int p_length) const; + const char32_t *_get_accented_version(char32_t p_character) const; + bool _is_placeholder(const String &p_message, int p_index) const; protected: static void _bind_methods(); @@ -59,6 +80,27 @@ public: StringName translate(const StringName &p_message, const StringName &p_context) const; StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const; + bool is_pseudolocalization_enabled() const; + void set_pseudolocalization_enabled(bool p_enabled); + bool is_pseudolocalization_accents_enabled() const; + void set_pseudolocalization_accents_enabled(bool p_enabled); + bool is_pseudolocalization_double_vowels_enabled() const; + void set_pseudolocalization_double_vowels_enabled(bool p_enabled); + bool is_pseudolocalization_fake_bidi_enabled() const; + void set_pseudolocalization_fake_bidi_enabled(bool p_enabled); + bool is_pseudolocalization_override_enabled() const; + void set_pseudolocalization_override_enabled(bool p_enabled); + bool is_pseudolocalization_skip_placeholders_enabled() const; + void set_pseudolocalization_skip_placeholders_enabled(bool p_enabled); + float get_pseudolocalization_expansion_ratio() const; + void set_pseudolocalization_expansion_ratio(float p_ratio); + String get_pseudolocalization_prefix() const; + void set_pseudolocalization_prefix(const String &p_prefix); + String get_pseudolocalization_suffix() const; + void set_pseudolocalization_suffix(const String &p_suffix); + + StringName pseudolocalize(const StringName &p_message) const; + TranslationDomain(); }; diff --git a/core/string/translation_po.cpp b/core/string/translation_po.cpp index 8e275505b0..da79e472e7 100644 --- a/core/string/translation_po.cpp +++ b/core/string/translation_po.cpp @@ -246,7 +246,7 @@ void TranslationPO::add_message(const StringName &p_src_text, const StringName & HashMap<StringName, Vector<StringName>> &map_id_str = translation_map[p_context]; if (map_id_str.has(p_src_text)) { - WARN_PRINT("Double translations for \"" + String(p_src_text) + "\" under the same context \"" + String(p_context) + "\" for locale \"" + get_locale() + "\".\nThere should only be one unique translation for a given string under the same context."); + WARN_PRINT(vformat("Double translations for \"%s\" under the same context \"%s\" for locale \"%s\".\nThere should only be one unique translation for a given string under the same context.", String(p_src_text), String(p_context), get_locale())); map_id_str[p_src_text].set(0, p_xlated_text); } else { map_id_str[p_src_text].push_back(p_xlated_text); @@ -254,12 +254,12 @@ void TranslationPO::add_message(const StringName &p_src_text, const StringName & } void TranslationPO::add_plural_message(const StringName &p_src_text, const Vector<String> &p_plural_xlated_texts, const StringName &p_context) { - ERR_FAIL_COND_MSG(p_plural_xlated_texts.size() != plural_forms, "Trying to add plural texts that don't match the required number of plural forms for locale \"" + get_locale() + "\""); + ERR_FAIL_COND_MSG(p_plural_xlated_texts.size() != plural_forms, vformat("Trying to add plural texts that don't match the required number of plural forms for locale \"%s\".", get_locale())); HashMap<StringName, Vector<StringName>> &map_id_str = translation_map[p_context]; if (map_id_str.has(p_src_text)) { - WARN_PRINT("Double translations for \"" + p_src_text + "\" under the same context \"" + p_context + "\" for locale " + get_locale() + ".\nThere should only be one unique translation for a given string under the same context."); + WARN_PRINT(vformat("Double translations for \"%s\" under the same context \"%s\" for locale %s.\nThere should only be one unique translation for a given string under the same context.", p_src_text, p_context, get_locale())); map_id_str[p_src_text].clear(); } @@ -280,7 +280,7 @@ StringName TranslationPO::get_message(const StringName &p_src_text, const String if (!translation_map.has(p_context) || !translation_map[p_context].has(p_src_text)) { return StringName(); } - ERR_FAIL_COND_V_MSG(translation_map[p_context][p_src_text].is_empty(), StringName(), "Source text \"" + String(p_src_text) + "\" is registered but doesn't have a translation. Please report this bug."); + ERR_FAIL_COND_V_MSG(translation_map[p_context][p_src_text].is_empty(), StringName(), vformat("Source text \"%s\" is registered but doesn't have a translation. Please report this bug.", String(p_src_text))); return translation_map[p_context][p_src_text][0]; } @@ -296,7 +296,7 @@ StringName TranslationPO::get_plural_message(const StringName &p_src_text, const if (!translation_map.has(p_context) || !translation_map[p_context].has(p_src_text)) { return StringName(); } - ERR_FAIL_COND_V_MSG(translation_map[p_context][p_src_text].is_empty(), StringName(), "Source text \"" + String(p_src_text) + "\" is registered but doesn't have a translation. Please report this bug."); + ERR_FAIL_COND_V_MSG(translation_map[p_context][p_src_text].is_empty(), StringName(), vformat("Source text \"%s\" is registered but doesn't have a translation. Please report this bug.", String(p_src_text))); int plural_index = _get_plural_index(p_n); ERR_FAIL_COND_V_MSG(plural_index < 0 || translation_map[p_context][p_src_text].size() < plural_index + 1, StringName(), "Plural index returned or number of plural translations is not valid. Please report this bug."); diff --git a/core/string/translation_server.cpp b/core/string/translation_server.cpp index c6b818a49b..92b473b61f 100644 --- a/core/string/translation_server.cpp +++ b/core/string/translation_server.cpp @@ -39,66 +39,6 @@ #include "main/main.h" #endif -struct _character_accent_pair { - const char32_t character; - const char32_t *accented_character; -}; - -static _character_accent_pair _character_to_accented[] = { - { 'A', U"Å" }, - { 'B', U"ß" }, - { 'C', U"Ç" }, - { 'D', U"Ð" }, - { 'E', U"É" }, - { 'F', U"F́" }, - { 'G', U"Ĝ" }, - { 'H', U"Ĥ" }, - { 'I', U"Ĩ" }, - { 'J', U"Ĵ" }, - { 'K', U"ĸ" }, - { 'L', U"Ł" }, - { 'M', U"Ḿ" }, - { 'N', U"й" }, - { 'O', U"Ö" }, - { 'P', U"Ṕ" }, - { 'Q', U"Q́" }, - { 'R', U"Ř" }, - { 'S', U"Ŝ" }, - { 'T', U"Ŧ" }, - { 'U', U"Ũ" }, - { 'V', U"Ṽ" }, - { 'W', U"Ŵ" }, - { 'X', U"X́" }, - { 'Y', U"Ÿ" }, - { 'Z', U"Ž" }, - { 'a', U"á" }, - { 'b', U"ḅ" }, - { 'c', U"ć" }, - { 'd', U"d́" }, - { 'e', U"é" }, - { 'f', U"f́" }, - { 'g', U"ǵ" }, - { 'h', U"h̀" }, - { 'i', U"í" }, - { 'j', U"ǰ" }, - { 'k', U"ḱ" }, - { 'l', U"ł" }, - { 'm', U"m̀" }, - { 'n', U"ή" }, - { 'o', U"ô" }, - { 'p', U"ṕ" }, - { 'q', U"q́" }, - { 'r', U"ŕ" }, - { 's', U"š" }, - { 't', U"ŧ" }, - { 'u', U"ü" }, - { 'v', U"ṽ" }, - { 'w', U"ŵ" }, - { 'x', U"x́" }, - { 'y', U"ý" }, - { 'z', U"ź" }, -}; - Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info; HashMap<String, String> TranslationServer::language_map; @@ -288,32 +228,41 @@ int TranslationServer::compare_locales(const String &p_locale_a, const String &p return 10; } + const String cache_key = p_locale_a + "|" + p_locale_b; + const int *cached_result = locale_compare_cache.getptr(cache_key); + if (cached_result) { + return *cached_result; + } + String locale_a = _standardize_locale(p_locale_a, true); String locale_b = _standardize_locale(p_locale_b, true); if (locale_a == locale_b) { // Exact match. + locale_compare_cache.insert(cache_key, 10); return 10; } Vector<String> locale_a_elements = locale_a.split("_"); Vector<String> locale_b_elements = locale_b.split("_"); - if (locale_a_elements[0] == locale_b_elements[0]) { - // Matching language, both locales have extra parts. - // Return number of matching elements. - int matching_elements = 1; - for (int i = 1; i < locale_a_elements.size(); i++) { - for (int j = 1; j < locale_b_elements.size(); j++) { - if (locale_a_elements[i] == locale_b_elements[j]) { - matching_elements++; - } - } - } - return matching_elements; - } else { + if (locale_a_elements[0] != locale_b_elements[0]) { // No match. + locale_compare_cache.insert(cache_key, 0); return 0; } + + // Matching language, both locales have extra parts. + // Return number of matching elements. + int matching_elements = 1; + for (int i = 1; i < locale_a_elements.size(); i++) { + for (int j = 1; j < locale_b_elements.size(); j++) { + if (locale_a_elements[i] == locale_b_elements[j]) { + matching_elements++; + } + } + } + locale_compare_cache.insert(cache_key, matching_elements); + return matching_elements; } String TranslationServer::get_locale_name(const String &p_locale) const { @@ -433,8 +382,7 @@ StringName TranslationServer::translate(const StringName &p_message, const Strin return p_message; } - const StringName res = main_domain->translate(p_message, p_context); - return pseudolocalization_enabled ? pseudolocalize(res) : res; + return main_domain->translate(p_message, p_context); } StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { @@ -510,15 +458,15 @@ void TranslationServer::setup() { } fallback = GLOBAL_DEF("internationalization/locale/fallback", "en"); - pseudolocalization_enabled = GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false); - pseudolocalization_accents_enabled = GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true); - pseudolocalization_double_vowels_enabled = GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false); - pseudolocalization_fake_bidi_enabled = GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false); - pseudolocalization_override_enabled = GLOBAL_DEF("internationalization/pseudolocalization/override", false); - expansion_ratio = GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0); - pseudolocalization_prefix = GLOBAL_DEF("internationalization/pseudolocalization/prefix", "["); - pseudolocalization_suffix = GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]"); - pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true); + main_domain->set_pseudolocalization_enabled(GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false)); + main_domain->set_pseudolocalization_accents_enabled(GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true)); + main_domain->set_pseudolocalization_double_vowels_enabled(GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false)); + main_domain->set_pseudolocalization_fake_bidi_enabled(GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false)); + main_domain->set_pseudolocalization_override_enabled(GLOBAL_DEF("internationalization/pseudolocalization/override", false)); + main_domain->set_pseudolocalization_expansion_ratio(GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0)); + main_domain->set_pseudolocalization_prefix(GLOBAL_DEF("internationalization/pseudolocalization/prefix", "[")); + main_domain->set_pseudolocalization_suffix(GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]")); + main_domain->set_pseudolocalization_skip_placeholders_enabled(GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true)); #ifdef TOOLS_ENABLED ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_LOCALE_ID, "")); @@ -567,11 +515,11 @@ StringName TranslationServer::doc_translate_plural(const StringName &p_message, } bool TranslationServer::is_pseudolocalization_enabled() const { - return pseudolocalization_enabled; + return main_domain->is_pseudolocalization_enabled(); } void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) { - pseudolocalization_enabled = p_enabled; + main_domain->set_pseudolocalization_enabled(p_enabled); ResourceLoader::reload_translation_remaps(); @@ -581,14 +529,14 @@ void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) { } void TranslationServer::reload_pseudolocalization() { - pseudolocalization_accents_enabled = GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents"); - pseudolocalization_double_vowels_enabled = GLOBAL_GET("internationalization/pseudolocalization/double_vowels"); - pseudolocalization_fake_bidi_enabled = GLOBAL_GET("internationalization/pseudolocalization/fake_bidi"); - pseudolocalization_override_enabled = GLOBAL_GET("internationalization/pseudolocalization/override"); - expansion_ratio = GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio"); - pseudolocalization_prefix = GLOBAL_GET("internationalization/pseudolocalization/prefix"); - pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix"); - pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders"); + main_domain->set_pseudolocalization_accents_enabled(GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents")); + main_domain->set_pseudolocalization_double_vowels_enabled(GLOBAL_GET("internationalization/pseudolocalization/double_vowels")); + main_domain->set_pseudolocalization_fake_bidi_enabled(GLOBAL_GET("internationalization/pseudolocalization/fake_bidi")); + main_domain->set_pseudolocalization_override_enabled(GLOBAL_GET("internationalization/pseudolocalization/override")); + main_domain->set_pseudolocalization_expansion_ratio(GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio")); + main_domain->set_pseudolocalization_prefix(GLOBAL_GET("internationalization/pseudolocalization/prefix")); + main_domain->set_pseudolocalization_suffix(GLOBAL_GET("internationalization/pseudolocalization/suffix")); + main_domain->set_pseudolocalization_skip_placeholders_enabled(GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders")); ResourceLoader::reload_translation_remaps(); @@ -598,138 +546,7 @@ void TranslationServer::reload_pseudolocalization() { } StringName TranslationServer::pseudolocalize(const StringName &p_message) const { - String message = p_message; - int length = message.length(); - if (pseudolocalization_override_enabled) { - message = get_override_string(message); - } - - if (pseudolocalization_double_vowels_enabled) { - message = double_vowels(message); - } - - if (pseudolocalization_accents_enabled) { - message = replace_with_accented_string(message); - } - - if (pseudolocalization_fake_bidi_enabled) { - message = wrap_with_fakebidi_characters(message); - } - - StringName res = add_padding(message, length); - return res; -} - -StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) const { - String message = p_message; - message = double_vowels(message); - message = replace_with_accented_string(message); - StringName res = "[!!! " + message + " !!!]"; - return res; -} - -String TranslationServer::get_override_string(String &p_message) const { - String res; - for (int i = 0; i < p_message.length(); i++) { - if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { - res += p_message[i]; - res += p_message[i + 1]; - i++; - continue; - } - res += '*'; - } - return res; -} - -String TranslationServer::double_vowels(String &p_message) const { - String res; - for (int i = 0; i < p_message.length(); i++) { - if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { - res += p_message[i]; - res += p_message[i + 1]; - i++; - continue; - } - res += p_message[i]; - if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' || - p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') { - res += p_message[i]; - } - } - return res; -}; - -String TranslationServer::replace_with_accented_string(String &p_message) const { - String res; - for (int i = 0; i < p_message.length(); i++) { - if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { - res += p_message[i]; - res += p_message[i + 1]; - i++; - continue; - } - const char32_t *accented = get_accented_version(p_message[i]); - if (accented) { - res += accented; - } else { - res += p_message[i]; - } - } - return res; -} - -String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const { - String res; - char32_t fakebidiprefix = U'\u202e'; - char32_t fakebidisuffix = U'\u202c'; - res += fakebidiprefix; - // The fake bidi unicode gets popped at every newline so pushing it back at every newline. - for (int i = 0; i < p_message.length(); i++) { - if (p_message[i] == '\n') { - res += fakebidisuffix; - res += p_message[i]; - res += fakebidiprefix; - } else if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { - res += fakebidisuffix; - res += p_message[i]; - res += p_message[i + 1]; - res += fakebidiprefix; - i++; - } else { - res += p_message[i]; - } - } - res += fakebidisuffix; - return res; -} - -String TranslationServer::add_padding(const String &p_message, int p_length) const { - String underscores = String("_").repeat(p_length * expansion_ratio / 2); - String prefix = pseudolocalization_prefix + underscores; - String suffix = underscores + pseudolocalization_suffix; - - return prefix + p_message + suffix; -} - -const char32_t *TranslationServer::get_accented_version(char32_t p_character) const { - if (!is_ascii_alphabet_char(p_character)) { - return nullptr; - } - - for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) { - if (_character_to_accented[i].character == p_character) { - return _character_to_accented[i].accented_character; - } - } - - return nullptr; -} - -bool TranslationServer::is_placeholder(String &p_message, int p_index) const { - return p_index < p_message.length() - 1 && p_message[p_index] == '%' && - (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' || - p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f'); + return main_domain->pseudolocalize(p_message); } #ifdef TOOLS_ENABLED diff --git a/core/string/translation_server.h b/core/string/translation_server.h index 272fa1f11c..2438349a69 100644 --- a/core/string/translation_server.h +++ b/core/string/translation_server.h @@ -46,26 +46,9 @@ class TranslationServer : public Object { Ref<TranslationDomain> doc_domain; HashMap<StringName, Ref<TranslationDomain>> custom_domains; - bool enabled = true; + mutable HashMap<String, int> locale_compare_cache; - bool pseudolocalization_enabled = false; - bool pseudolocalization_accents_enabled = false; - bool pseudolocalization_double_vowels_enabled = false; - bool pseudolocalization_fake_bidi_enabled = false; - bool pseudolocalization_override_enabled = false; - bool pseudolocalization_skip_placeholders_enabled = false; - float expansion_ratio = 0.0; - String pseudolocalization_prefix; - String pseudolocalization_suffix; - - StringName tool_pseudolocalize(const StringName &p_message) const; - String get_override_string(String &p_message) const; - String double_vowels(String &p_message) const; - String replace_with_accented_string(String &p_message) const; - String wrap_with_fakebidi_characters(String &p_message) const; - String add_padding(const String &p_message, int p_length) const; - const char32_t *get_accented_version(char32_t p_character) const; - bool is_placeholder(String &p_message, int p_index) const; + bool enabled = true; static TranslationServer *singleton; bool _load_translations(const String &p_from); @@ -93,6 +76,8 @@ class TranslationServer : public Object { public: _FORCE_INLINE_ static TranslationServer *get_singleton() { return singleton; } + Ref<TranslationDomain> get_editor_domain() const { return editor_domain; } + void set_enabled(bool p_enabled) { enabled = p_enabled; } _FORCE_INLINE_ bool is_enabled() const { return enabled; } diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 391a203d5b..a78f0ff5ff 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -33,6 +33,7 @@ #include "core/crypto/crypto_core.h" #include "core/math/color.h" #include "core/math/math_funcs.h" +#include "core/object/object.h" #include "core/os/memory.h" #include "core/string/print_string.h" #include "core/string/string_name.h" @@ -221,18 +222,35 @@ void CharString::copy_from(const char *p_cstr) { /* String */ /*************************************************************************/ -Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const { - // Splits the URL into scheme, host, port, path. Strip credentials when present. +Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path, String &r_fragment) const { + // Splits the URL into scheme, host, port, path, fragment. Strip credentials when present. String base = *this; r_scheme = ""; r_host = ""; r_port = 0; r_path = ""; + r_fragment = ""; + int pos = base.find("://"); // Scheme if (pos != -1) { - r_scheme = base.substr(0, pos + 3).to_lower(); - base = base.substr(pos + 3, base.length() - pos - 3); + bool is_scheme_valid = true; + for (int i = 0; i < pos; i++) { + if (!is_ascii_alphanumeric_char(base[i]) && base[i] != '+' && base[i] != '-' && base[i] != '.') { + is_scheme_valid = false; + break; + } + } + if (is_scheme_valid) { + r_scheme = base.substr(0, pos + 3).to_lower(); + base = base.substr(pos + 3, base.length() - pos - 3); + } + } + pos = base.find("#"); + // Fragment + if (pos != -1) { + r_fragment = base.substr(pos + 1); + base = base.substr(0, pos); } pos = base.find("/"); // Path @@ -1801,7 +1819,7 @@ String String::num(double p_num, int p_decimals) { #endif buf[324] = 0; - //destroy trailing zeroes + // Destroy trailing zeroes, except one after period. { bool period = false; int z = 0; @@ -1818,7 +1836,7 @@ String String::num(double p_num, int p_decimals) { if (buf[z] == '0') { buf[z] = 0; } else if (buf[z] == '.') { - buf[z] = 0; + buf[z + 1] = '0'; break; } else { break; @@ -1833,6 +1851,8 @@ String String::num(double p_num, int p_decimals) { } String String::num_int64(int64_t p_num, int base, bool capitalize_hex) { + ERR_FAIL_COND_V_MSG(base < 2 || base > 36, "", "Cannot convert to base " + itos(base) + ", since the value is " + (base < 2 ? "less than 2." : "greater than 36.")); + bool sign = p_num < 0; int64_t n = p_num; @@ -1871,6 +1891,8 @@ String String::num_int64(int64_t p_num, int base, bool capitalize_hex) { } String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) { + ERR_FAIL_COND_V_MSG(base < 2 || base > 36, "", "Cannot convert to base " + itos(base) + ", since the value is " + (base < 2 ? "less than 2." : "greater than 36.")); + uint64_t n = p_num; int chars = 0; @@ -1907,14 +1929,28 @@ String String::num_real(double p_num, bool p_trailing) { return num_int64((int64_t)p_num); } } -#ifdef REAL_T_IS_DOUBLE int decimals = 14; -#else + // We want to align the digits to the above sane default, so we only need + // to subtract log10 for numbers with a positive power of ten magnitude. + const double abs_num = Math::abs(p_num); + if (abs_num > 10) { + decimals -= (int)floor(log10(abs_num)); + } + return num(p_num, decimals); +} + +String String::num_real(float p_num, bool p_trailing) { + if (p_num == (float)(int64_t)p_num) { + if (p_trailing) { + return num_int64((int64_t)p_num) + ".0"; + } else { + return num_int64((int64_t)p_num); + } + } int decimals = 6; -#endif // We want to align the digits to the above sane default, so we only need // to subtract log10 for numbers with a positive power of ten magnitude. - double abs_num = Math::abs(p_num); + const float abs_num = Math::abs(p_num); if (abs_num > 10) { decimals -= (int)floor(log10(abs_num)); } @@ -4043,8 +4079,18 @@ String String::format(const Variant &values, const String &placeholder) const { for (const Variant &key : keys) { new_string = new_string.replace(placeholder.replace("_", key), d[key]); } + } else if (values.get_type() == Variant::OBJECT) { + Object *obj = values.get_validated_object(); + ERR_FAIL_NULL_V(obj, new_string); + + List<PropertyInfo> props; + obj->get_property_list(&props); + + for (const PropertyInfo &E : props) { + new_string = new_string.replace(placeholder.replace("_", E.name), obj->get(E.name)); + } } else { - ERR_PRINT(String("Invalid type: use Array or Dictionary.").ascii().get_data()); + ERR_PRINT(String("Invalid type: use Array, Dictionary or Object.").ascii().get_data()); } return new_string; @@ -4584,7 +4630,7 @@ String String::humanize_size(uint64_t p_size) { } if (magnitude == 0) { - return String::num(p_size) + " " + RTR("B"); + return String::num_uint64(p_size) + " " + RTR("B"); } else { String suffix; switch (magnitude) { diff --git a/core/string/ustring.h b/core/string/ustring.h index 5d4b209c25..11d187beb4 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -118,7 +118,7 @@ public: Char16String &operator+=(char16_t p_char); int length() const { return size() ? size() - 1 : 0; } const char16_t *get_data() const; - operator const char16_t *() const { return get_data(); }; + operator const char16_t *() const { return get_data(); } protected: void copy_from(const char16_t *p_cstr); @@ -160,7 +160,7 @@ public: CharString &operator+=(char p_char); int length() const { return size() ? size() - 1 : 0; } const char *get_data() const; - operator const char *() const { return get_data(); }; + operator const char *() const { return get_data(); } protected: void copy_from(const char *p_cstr); @@ -332,6 +332,7 @@ public: static String num(double p_num, int p_decimals = -1); static String num_scientific(double p_num); static String num_real(double p_num, bool p_trailing = true); + static String num_real(float p_num, bool p_trailing = true); static String num_int64(int64_t p_num, int base = 10, bool capitalize_hex = false); static String num_uint64(uint64_t p_num, int base = 10, bool capitalize_hex = false); static String chr(char32_t p_char); @@ -452,7 +453,7 @@ public: String c_escape_multiline() const; String c_unescape() const; String json_escape() const; - Error parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const; + Error parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path, String &r_fragment) const; String property_name_encode() const; diff --git a/core/templates/SCsub b/core/templates/SCsub index 8c4c843a33..7f806d5609 100644 --- a/core/templates/SCsub +++ b/core/templates/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/templates/a_hash_map.cpp b/core/templates/a_hash_map.cpp new file mode 100644 index 0000000000..04a14c261a --- /dev/null +++ b/core/templates/a_hash_map.cpp @@ -0,0 +1,39 @@ +/**************************************************************************/ +/* a_hash_map.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "a_hash_map.h" +#include "core/variant/variant.h" + +// Explicit instantiation. +template class AHashMap<int, int>; +template class AHashMap<String, int>; +template class AHashMap<StringName, StringName>; +template class AHashMap<StringName, Variant>; +template class AHashMap<StringName, int>; diff --git a/core/templates/a_hash_map.h b/core/templates/a_hash_map.h new file mode 100644 index 0000000000..29983ea268 --- /dev/null +++ b/core/templates/a_hash_map.h @@ -0,0 +1,732 @@ +/**************************************************************************/ +/* a_hash_map.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef A_HASH_MAP_H +#define A_HASH_MAP_H + +#include "core/templates/hash_map.h" + +struct HashMapData { + union { + struct + { + uint32_t hash; + uint32_t hash_to_key; + }; + uint64_t data; + }; +}; + +static_assert(sizeof(HashMapData) == 8); + +/** + * An array-based implementation of a hash map. It is very efficient in terms of performance and + * memory usage. Works like a dynamic array, adding elements to the end of the array, and + * allows you to access array elements by their index by using `get_by_index` method. + * Example: + * ``` + * AHashMap<int, Object *> map; + * + * int get_object_id_by_number(int p_number) { + * int id = map.get_index(p_number); + * return id; + * } + * + * Object *get_object_by_id(int p_id) { + * map.get_by_index(p_id).value; + * } + * ``` + * Still, don`t erase the elements because ID can break. + * + * When an element erase, its place is taken by the element from the end. + * + * <------------- + * | | + * 6 8 X 9 32 -1 5 -10 7 X X X + * 6 8 7 9 32 -1 5 -10 X X X X + * + * + * Use RBMap if you need to iterate over sorted elements. + * + * Use HashMap if: + * - You need to keep an iterator or const pointer to Key and you intend to add/remove elements in the meantime. + * - You need to preserve the insertion order when using erase. + * + * It is recommended to use `HashMap` if `KeyValue` size is very large. + */ +template <typename TKey, typename TValue, + typename Hasher = HashMapHasherDefault, + typename Comparator = HashMapComparatorDefault<TKey>> +class AHashMap { +public: + // Must be a power of two. + static constexpr uint32_t INITIAL_CAPACITY = 16; + static constexpr uint32_t EMPTY_HASH = 0; + static_assert(EMPTY_HASH == 0, "EMPTY_HASH must always be 0 for the memcpy() optimization."); + +private: + typedef KeyValue<TKey, TValue> MapKeyValue; + MapKeyValue *elements = nullptr; + HashMapData *map_data = nullptr; + + // Due to optimization, this is `capacity - 1`. Use + 1 to get normal capacity. + uint32_t capacity = 0; + uint32_t num_elements = 0; + + uint32_t _hash(const TKey &p_key) const { + uint32_t hash = Hasher::hash(p_key); + + if (unlikely(hash == EMPTY_HASH)) { + hash = EMPTY_HASH + 1; + } + + return hash; + } + + static _FORCE_INLINE_ uint32_t _get_resize_count(uint32_t p_capacity) { + return p_capacity ^ (p_capacity + 1) >> 2; // = get_capacity() * 0.75 - 1; Works only if p_capacity = 2^n - 1. + } + + static _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash, uint32_t p_local_capacity) { + const uint32_t original_pos = p_hash & p_local_capacity; + return (p_pos - original_pos + p_local_capacity + 1) & p_local_capacity; + } + + bool _lookup_pos(const TKey &p_key, uint32_t &r_pos, uint32_t &r_hash_pos) const { + if (unlikely(elements == nullptr)) { + return false; // Failed lookups, no elements. + } + return _lookup_pos_with_hash(p_key, r_pos, r_hash_pos, _hash(p_key)); + } + + bool _lookup_pos_with_hash(const TKey &p_key, uint32_t &r_pos, uint32_t &r_hash_pos, uint32_t p_hash) const { + if (unlikely(elements == nullptr)) { + return false; // Failed lookups, no elements. + } + + uint32_t pos = p_hash & capacity; + HashMapData data = map_data[pos]; + if (data.hash == p_hash && Comparator::compare(elements[data.hash_to_key].key, p_key)) { + r_pos = data.hash_to_key; + r_hash_pos = pos; + return true; + } + + if (data.data == EMPTY_HASH) { + return false; + } + + // A collision occurred. + pos = (pos + 1) & capacity; + uint32_t distance = 1; + while (true) { + data = map_data[pos]; + if (data.hash == p_hash && Comparator::compare(elements[data.hash_to_key].key, p_key)) { + r_pos = data.hash_to_key; + r_hash_pos = pos; + return true; + } + + if (data.data == EMPTY_HASH) { + return false; + } + + if (distance > _get_probe_length(pos, data.hash, capacity)) { + return false; + } + + pos = (pos + 1) & capacity; + distance++; + } + } + + uint32_t _insert_with_hash(uint32_t p_hash, uint32_t p_index) { + uint32_t pos = p_hash & capacity; + + if (map_data[pos].data == EMPTY_HASH) { + uint64_t data = ((uint64_t)p_index << 32) | p_hash; + map_data[pos].data = data; + return pos; + } + + uint32_t distance = 1; + pos = (pos + 1) & capacity; + HashMapData c_data; + c_data.hash = p_hash; + c_data.hash_to_key = p_index; + + while (true) { + if (map_data[pos].data == EMPTY_HASH) { +#ifdef DEV_ENABLED + if (unlikely(distance > 12)) { + WARN_PRINT("Excessive collision count (" + + itos(distance) + "), is the right hash function being used?"); + } +#endif + map_data[pos] = c_data; + return pos; + } + + // Not an empty slot, let's check the probing length of the existing one. + uint32_t existing_probe_len = _get_probe_length(pos, map_data[pos].hash, capacity); + if (existing_probe_len < distance) { + SWAP(c_data, map_data[pos]); + distance = existing_probe_len; + } + + pos = (pos + 1) & capacity; + distance++; + } + } + + void _resize_and_rehash(uint32_t p_new_capacity) { + uint32_t real_old_capacity = capacity + 1; + // Capacity can't be 0 and must be 2^n - 1. + capacity = MAX(4u, p_new_capacity); + uint32_t real_capacity = next_power_of_2(capacity); + capacity = real_capacity - 1; + + HashMapData *old_map_data = map_data; + + map_data = reinterpret_cast<HashMapData *>(Memory::alloc_static(sizeof(HashMapData) * real_capacity)); + elements = reinterpret_cast<MapKeyValue *>(Memory::realloc_static(elements, sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1))); + + memset(map_data, EMPTY_HASH, real_capacity * sizeof(HashMapData)); + + if (num_elements != 0) { + for (uint32_t i = 0; i < real_old_capacity; i++) { + HashMapData data = old_map_data[i]; + if (data.data != EMPTY_HASH) { + _insert_with_hash(data.hash, data.hash_to_key); + } + } + } + + Memory::free_static(old_map_data); + } + + int32_t _insert_element(const TKey &p_key, const TValue &p_value, uint32_t p_hash) { + if (unlikely(elements == nullptr)) { + // Allocate on demand to save memory. + + uint32_t real_capacity = capacity + 1; + map_data = reinterpret_cast<HashMapData *>(Memory::alloc_static(sizeof(HashMapData) * real_capacity)); + elements = reinterpret_cast<MapKeyValue *>(Memory::alloc_static(sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1))); + + memset(map_data, EMPTY_HASH, real_capacity * sizeof(HashMapData)); + } + + if (unlikely(num_elements > _get_resize_count(capacity))) { + _resize_and_rehash(capacity * 2); + } + + memnew_placement(&elements[num_elements], MapKeyValue(p_key, p_value)); + + _insert_with_hash(p_hash, num_elements); + num_elements++; + return num_elements - 1; + } + + void _init_from(const AHashMap &p_other) { + capacity = p_other.capacity; + uint32_t real_capacity = capacity + 1; + num_elements = p_other.num_elements; + + if (p_other.num_elements == 0) { + return; + } + + map_data = reinterpret_cast<HashMapData *>(Memory::alloc_static(sizeof(HashMapData) * real_capacity)); + elements = reinterpret_cast<MapKeyValue *>(Memory::alloc_static(sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1))); + + if constexpr (std::is_trivially_copyable_v<TKey> && std::is_trivially_copyable_v<TValue>) { + void *destination = elements; + const void *source = p_other.elements; + memcpy(destination, source, sizeof(MapKeyValue) * num_elements); + } else { + for (uint32_t i = 0; i < num_elements; i++) { + memnew_placement(&elements[i], MapKeyValue(p_other.elements[i])); + } + } + + memcpy(map_data, p_other.map_data, sizeof(HashMapData) * real_capacity); + } + +public: + /* Standard Godot Container API */ + + _FORCE_INLINE_ uint32_t get_capacity() const { return capacity + 1; } + _FORCE_INLINE_ uint32_t size() const { return num_elements; } + + _FORCE_INLINE_ bool is_empty() const { + return num_elements == 0; + } + + void clear() { + if (elements == nullptr || num_elements == 0) { + return; + } + + memset(map_data, EMPTY_HASH, (capacity + 1) * sizeof(HashMapData)); + if constexpr (!(std::is_trivially_destructible_v<TKey> && std::is_trivially_destructible_v<TValue>)) { + for (uint32_t i = 0; i < num_elements; i++) { + elements[i].key.~TKey(); + elements[i].value.~TValue(); + } + } + + num_elements = 0; + } + + TValue &get(const TKey &p_key) { + uint32_t pos = 0; + uint32_t hash_pos = 0; + bool exists = _lookup_pos(p_key, pos, hash_pos); + CRASH_COND_MSG(!exists, "AHashMap key not found."); + return elements[pos].value; + } + + const TValue &get(const TKey &p_key) const { + uint32_t pos = 0; + uint32_t hash_pos = 0; + bool exists = _lookup_pos(p_key, pos, hash_pos); + CRASH_COND_MSG(!exists, "AHashMap key not found."); + return elements[pos].value; + } + + const TValue *getptr(const TKey &p_key) const { + uint32_t pos = 0; + uint32_t hash_pos = 0; + bool exists = _lookup_pos(p_key, pos, hash_pos); + + if (exists) { + return &elements[pos].value; + } + return nullptr; + } + + TValue *getptr(const TKey &p_key) { + uint32_t pos = 0; + uint32_t hash_pos = 0; + bool exists = _lookup_pos(p_key, pos, hash_pos); + + if (exists) { + return &elements[pos].value; + } + return nullptr; + } + + bool has(const TKey &p_key) const { + uint32_t _pos = 0; + uint32_t h_pos = 0; + return _lookup_pos(p_key, _pos, h_pos); + } + + bool erase(const TKey &p_key) { + uint32_t pos = 0; + uint32_t element_pos = 0; + bool exists = _lookup_pos(p_key, element_pos, pos); + + if (!exists) { + return false; + } + + uint32_t next_pos = (pos + 1) & capacity; + while (map_data[next_pos].hash != EMPTY_HASH && _get_probe_length(next_pos, map_data[next_pos].hash, capacity) != 0) { + SWAP(map_data[next_pos], map_data[pos]); + + pos = next_pos; + next_pos = (next_pos + 1) & capacity; + } + + map_data[pos].data = EMPTY_HASH; + elements[element_pos].key.~TKey(); + elements[element_pos].value.~TValue(); + num_elements--; + + if (element_pos < num_elements) { + void *destination = &elements[element_pos]; + const void *source = &elements[num_elements]; + memcpy(destination, source, sizeof(MapKeyValue)); + uint32_t h_pos = 0; + _lookup_pos(elements[num_elements].key, pos, h_pos); + map_data[h_pos].hash_to_key = element_pos; + } + + return true; + } + + // Replace the key of an entry in-place, without invalidating iterators or changing the entries position during iteration. + // p_old_key must exist in the map and p_new_key must not, unless it is equal to p_old_key. + bool replace_key(const TKey &p_old_key, const TKey &p_new_key) { + if (p_old_key == p_new_key) { + return true; + } + uint32_t pos = 0; + uint32_t element_pos = 0; + ERR_FAIL_COND_V(_lookup_pos(p_new_key, element_pos, pos), false); + ERR_FAIL_COND_V(!_lookup_pos(p_old_key, element_pos, pos), false); + MapKeyValue &element = elements[element_pos]; + const_cast<TKey &>(element.key) = p_new_key; + + uint32_t next_pos = (pos + 1) & capacity; + while (map_data[next_pos].hash != EMPTY_HASH && _get_probe_length(next_pos, map_data[next_pos].hash, capacity) != 0) { + SWAP(map_data[next_pos], map_data[pos]); + + pos = next_pos; + next_pos = (next_pos + 1) & capacity; + } + + map_data[pos].data = EMPTY_HASH; + + uint32_t hash = _hash(p_new_key); + _insert_with_hash(hash, element_pos); + + return true; + } + + // Reserves space for a number of elements, useful to avoid many resizes and rehashes. + // If adding a known (possibly large) number of elements at once, must be larger than old capacity. + void reserve(uint32_t p_new_capacity) { + ERR_FAIL_COND_MSG(p_new_capacity < get_capacity(), "It is impossible to reserve less capacity than is currently available."); + if (elements == nullptr) { + capacity = MAX(4u, p_new_capacity); + capacity = next_power_of_2(capacity) - 1; + return; // Unallocated yet. + } + _resize_and_rehash(p_new_capacity); + } + + /** Iterator API **/ + + struct ConstIterator { + _FORCE_INLINE_ const MapKeyValue &operator*() const { + return *pair; + } + _FORCE_INLINE_ const MapKeyValue *operator->() const { + return pair; + } + _FORCE_INLINE_ ConstIterator &operator++() { + pair++; + return *this; + } + + _FORCE_INLINE_ ConstIterator &operator--() { + pair--; + if (pair < begin) { + pair = end; + } + return *this; + } + + _FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return pair == b.pair; } + _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return pair != b.pair; } + + _FORCE_INLINE_ explicit operator bool() const { + return pair != end; + } + + _FORCE_INLINE_ ConstIterator(MapKeyValue *p_key, MapKeyValue *p_begin, MapKeyValue *p_end) { + pair = p_key; + begin = p_begin; + end = p_end; + } + _FORCE_INLINE_ ConstIterator() {} + _FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) { + pair = p_it.pair; + begin = p_it.begin; + end = p_it.end; + } + _FORCE_INLINE_ void operator=(const ConstIterator &p_it) { + pair = p_it.pair; + begin = p_it.begin; + end = p_it.end; + } + + private: + MapKeyValue *pair = nullptr; + MapKeyValue *begin = nullptr; + MapKeyValue *end = nullptr; + }; + + struct Iterator { + _FORCE_INLINE_ MapKeyValue &operator*() const { + return *pair; + } + _FORCE_INLINE_ MapKeyValue *operator->() const { + return pair; + } + _FORCE_INLINE_ Iterator &operator++() { + pair++; + return *this; + } + _FORCE_INLINE_ Iterator &operator--() { + pair--; + if (pair < begin) { + pair = end; + } + return *this; + } + + _FORCE_INLINE_ bool operator==(const Iterator &b) const { return pair == b.pair; } + _FORCE_INLINE_ bool operator!=(const Iterator &b) const { return pair != b.pair; } + + _FORCE_INLINE_ explicit operator bool() const { + return pair != end; + } + + _FORCE_INLINE_ Iterator(MapKeyValue *p_key, MapKeyValue *p_begin, MapKeyValue *p_end) { + pair = p_key; + begin = p_begin; + end = p_end; + } + _FORCE_INLINE_ Iterator() {} + _FORCE_INLINE_ Iterator(const Iterator &p_it) { + pair = p_it.pair; + begin = p_it.begin; + end = p_it.end; + } + _FORCE_INLINE_ void operator=(const Iterator &p_it) { + pair = p_it.pair; + begin = p_it.begin; + end = p_it.end; + } + + operator ConstIterator() const { + return ConstIterator(pair, begin, end); + } + + private: + MapKeyValue *pair = nullptr; + MapKeyValue *begin = nullptr; + MapKeyValue *end = nullptr; + }; + + _FORCE_INLINE_ Iterator begin() { + return Iterator(elements, elements, elements + num_elements); + } + _FORCE_INLINE_ Iterator end() { + return Iterator(elements + num_elements, elements, elements + num_elements); + } + _FORCE_INLINE_ Iterator last() { + if (unlikely(num_elements == 0)) { + return Iterator(nullptr, nullptr, nullptr); + } + return Iterator(elements + num_elements - 1, elements, elements + num_elements); + } + + Iterator find(const TKey &p_key) { + uint32_t pos = 0; + uint32_t h_pos = 0; + bool exists = _lookup_pos(p_key, pos, h_pos); + if (!exists) { + return end(); + } + return Iterator(elements + pos, elements, elements + num_elements); + } + + void remove(const Iterator &p_iter) { + if (p_iter) { + erase(p_iter->key); + } + } + + _FORCE_INLINE_ ConstIterator begin() const { + return ConstIterator(elements, elements, elements + num_elements); + } + _FORCE_INLINE_ ConstIterator end() const { + return ConstIterator(elements + num_elements, elements, elements + num_elements); + } + _FORCE_INLINE_ ConstIterator last() const { + if (unlikely(num_elements == 0)) { + return ConstIterator(nullptr, nullptr, nullptr); + } + return ConstIterator(elements + num_elements - 1, elements, elements + num_elements); + } + + ConstIterator find(const TKey &p_key) const { + uint32_t pos = 0; + uint32_t h_pos = 0; + bool exists = _lookup_pos(p_key, pos, h_pos); + if (!exists) { + return end(); + } + return ConstIterator(elements + pos, elements, elements + num_elements); + } + + /* Indexing */ + + const TValue &operator[](const TKey &p_key) const { + uint32_t pos = 0; + uint32_t h_pos = 0; + bool exists = _lookup_pos(p_key, pos, h_pos); + CRASH_COND(!exists); + return elements[pos].value; + } + + TValue &operator[](const TKey &p_key) { + uint32_t pos = 0; + uint32_t h_pos = 0; + uint32_t hash = _hash(p_key); + bool exists = _lookup_pos_with_hash(p_key, pos, h_pos, hash); + + if (exists) { + return elements[pos].value; + } else { + pos = _insert_element(p_key, TValue(), hash); + return elements[pos].value; + } + } + + /* Insert */ + + Iterator insert(const TKey &p_key, const TValue &p_value) { + uint32_t pos = 0; + uint32_t h_pos = 0; + uint32_t hash = _hash(p_key); + bool exists = _lookup_pos_with_hash(p_key, pos, h_pos, hash); + + if (!exists) { + pos = _insert_element(p_key, p_value, hash); + } else { + elements[pos].value = p_value; + } + return Iterator(elements + pos, elements, elements + num_elements); + } + + // Inserts an element without checking if it already exists. + void insert_new(const TKey &p_key, const TValue &p_value) { + DEV_ASSERT(!has(p_key)); + uint32_t hash = _hash(p_key); + _insert_element(p_key, p_value, hash); + } + + /* Array methods. */ + + // Unsafe. Changing keys and going outside the bounds of an array can lead to undefined behavior. + KeyValue<TKey, TValue> *get_elements_ptr() { + return elements; + } + + // Returns the element index. If not found, returns -1. + int get_index(const TKey &p_key) { + uint32_t pos = 0; + uint32_t h_pos = 0; + bool exists = _lookup_pos(p_key, pos, h_pos); + if (!exists) { + return -1; + } + return pos; + } + + KeyValue<TKey, TValue> &get_by_index(uint32_t p_index) { + CRASH_BAD_UNSIGNED_INDEX(p_index, num_elements); + return elements[p_index]; + } + + bool erase_by_index(uint32_t p_index) { + if (p_index >= size()) { + return false; + } + return erase(elements[p_index].key); + } + + /* Constructors */ + + AHashMap(const AHashMap &p_other) { + _init_from(p_other); + } + + AHashMap(const HashMap<TKey, TValue> &p_other) { + reserve(p_other.size()); + for (const KeyValue<TKey, TValue> &E : p_other) { + uint32_t hash = _hash(E.key); + _insert_element(E.key, E.value, hash); + } + } + + void operator=(const AHashMap &p_other) { + if (this == &p_other) { + return; // Ignore self assignment. + } + + reset(); + + _init_from(p_other); + } + + void operator=(const HashMap<TKey, TValue> &p_other) { + reset(); + if (p_other.size() > get_capacity()) { + reserve(p_other.size()); + } + for (const KeyValue<TKey, TValue> &E : p_other) { + uint32_t hash = _hash(E.key); + _insert_element(E.key, E.value, hash); + } + } + + AHashMap(uint32_t p_initial_capacity) { + // Capacity can't be 0 and must be 2^n - 1. + capacity = MAX(4u, p_initial_capacity); + capacity = next_power_of_2(capacity) - 1; + } + AHashMap() : + capacity(INITIAL_CAPACITY - 1) { + } + + void reset() { + if (elements != nullptr) { + if constexpr (!(std::is_trivially_destructible_v<TKey> && std::is_trivially_destructible_v<TValue>)) { + for (uint32_t i = 0; i < num_elements; i++) { + elements[i].key.~TKey(); + elements[i].value.~TValue(); + } + } + Memory::free_static(elements); + Memory::free_static(map_data); + elements = nullptr; + } + capacity = INITIAL_CAPACITY - 1; + num_elements = 0; + } + + ~AHashMap() { + reset(); + } +}; + +extern template class AHashMap<int, int>; +extern template class AHashMap<String, int>; +extern template class AHashMap<StringName, StringName>; +extern template class AHashMap<StringName, Variant>; +extern template class AHashMap<StringName, int>; + +#endif // A_HASH_MAP_H diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h index fedcfaec3b..5f260ee870 100644 --- a/core/templates/cowdata.h +++ b/core/templates/cowdata.h @@ -241,7 +241,7 @@ public: _FORCE_INLINE_ CowData() {} _FORCE_INLINE_ ~CowData(); - _FORCE_INLINE_ CowData(CowData<T> &p_from) { _ref(p_from); }; + _FORCE_INLINE_ CowData(CowData<T> &p_from) { _ref(p_from); } }; template <typename T> diff --git a/core/templates/hash_map.cpp b/core/templates/hash_map.cpp new file mode 100644 index 0000000000..93664dd2e1 --- /dev/null +++ b/core/templates/hash_map.cpp @@ -0,0 +1,43 @@ +/**************************************************************************/ +/* hash_map.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "hash_map.h" + +#include "core/variant/variant.h" + +bool _hashmap_variant_less_than(const Variant &p_left, const Variant &p_right) { + bool valid = false; + Variant res; + Variant::evaluate(Variant::OP_LESS, p_left, p_right, res, valid); + if (!valid) { + res = false; + } + return res; +} diff --git a/core/templates/hash_map.h b/core/templates/hash_map.h index a3e8c2c788..329952e8d4 100644 --- a/core/templates/hash_map.h +++ b/core/templates/hash_map.h @@ -61,6 +61,8 @@ struct HashMapElement { data(p_key, p_value) {} }; +bool _hashmap_variant_less_than(const Variant &p_left, const Variant &p_right); + template <typename TKey, typename TValue, typename Hasher = HashMapHasherDefault, typename Comparator = HashMapComparatorDefault<TKey>, @@ -271,6 +273,47 @@ public: num_elements = 0; } + void sort() { + if (elements == nullptr || num_elements < 2) { + return; // An empty or single element HashMap is already sorted. + } + // Use insertion sort because we want this operation to be fast for the + // common case where the input is already sorted or nearly sorted. + HashMapElement<TKey, TValue> *inserting = head_element->next; + while (inserting != nullptr) { + HashMapElement<TKey, TValue> *after = nullptr; + for (HashMapElement<TKey, TValue> *current = inserting->prev; current != nullptr; current = current->prev) { + if (_hashmap_variant_less_than(inserting->data.key, current->data.key)) { + after = current; + } else { + break; + } + } + HashMapElement<TKey, TValue> *next = inserting->next; + if (after != nullptr) { + // Modify the elements around `inserting` to remove it from its current position. + inserting->prev->next = next; + if (next == nullptr) { + tail_element = inserting->prev; + } else { + next->prev = inserting->prev; + } + // Modify `before` and `after` to insert `inserting` between them. + HashMapElement<TKey, TValue> *before = after->prev; + if (before == nullptr) { + head_element = inserting; + } else { + before->next = inserting; + } + after->prev = inserting; + // Point `inserting` to its new surroundings. + inserting->prev = before; + inserting->next = after; + } + inserting = next; + } + } + TValue &get(const TKey &p_key) { uint32_t pos = 0; bool exists = _lookup_pos(p_key, pos); diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h index fc7a78bcf5..7818ed0706 100644 --- a/core/templates/hashfuncs.h +++ b/core/templates/hashfuncs.h @@ -32,10 +32,17 @@ #define HASHFUNCS_H #include "core/math/aabb.h" +#include "core/math/basis.h" +#include "core/math/color.h" #include "core/math/math_defs.h" #include "core/math/math_funcs.h" +#include "core/math/plane.h" +#include "core/math/projection.h" +#include "core/math/quaternion.h" #include "core/math/rect2.h" #include "core/math/rect2i.h" +#include "core/math/transform_2d.h" +#include "core/math/transform_3d.h" #include "core/math/vector2.h" #include "core/math/vector2i.h" #include "core/math/vector3.h" @@ -386,6 +393,13 @@ struct HashMapHasherDefault { } }; +struct HashHasher { + static _FORCE_INLINE_ uint32_t hash(const int32_t hash) { return hash; } + static _FORCE_INLINE_ uint32_t hash(const uint32_t hash) { return hash; } + static _FORCE_INLINE_ uint64_t hash(const int64_t hash) { return hash; } + static _FORCE_INLINE_ uint64_t hash(const uint64_t hash) { return hash; } +}; + // TODO: Fold this into HashMapHasherDefault once C++20 concepts are allowed template <typename T> struct HashableHasher { @@ -414,6 +428,13 @@ struct HashMapComparatorDefault<double> { }; template <> +struct HashMapComparatorDefault<Color> { + static bool compare(const Color &p_lhs, const Color &p_rhs) { + return ((p_lhs.r == p_rhs.r) || (Math::is_nan(p_lhs.r) && Math::is_nan(p_rhs.r))) && ((p_lhs.g == p_rhs.g) || (Math::is_nan(p_lhs.g) && Math::is_nan(p_rhs.g))) && ((p_lhs.b == p_rhs.b) || (Math::is_nan(p_lhs.b) && Math::is_nan(p_rhs.b))) && ((p_lhs.a == p_rhs.a) || (Math::is_nan(p_lhs.a) && Math::is_nan(p_rhs.a))); + } +}; + +template <> struct HashMapComparatorDefault<Vector2> { static bool compare(const Vector2 &p_lhs, const Vector2 &p_rhs) { return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))); @@ -427,6 +448,87 @@ struct HashMapComparatorDefault<Vector3> { } }; +template <> +struct HashMapComparatorDefault<Vector4> { + static bool compare(const Vector4 &p_lhs, const Vector4 &p_rhs) { + return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z))) && ((p_lhs.w == p_rhs.w) || (Math::is_nan(p_lhs.w) && Math::is_nan(p_rhs.w))); + } +}; + +template <> +struct HashMapComparatorDefault<Rect2> { + static bool compare(const Rect2 &p_lhs, const Rect2 &p_rhs) { + return HashMapComparatorDefault<Vector2>().compare(p_lhs.position, p_rhs.position) && HashMapComparatorDefault<Vector2>().compare(p_lhs.size, p_rhs.size); + } +}; + +template <> +struct HashMapComparatorDefault<AABB> { + static bool compare(const AABB &p_lhs, const AABB &p_rhs) { + return HashMapComparatorDefault<Vector3>().compare(p_lhs.position, p_rhs.position) && HashMapComparatorDefault<Vector3>().compare(p_lhs.size, p_rhs.size); + } +}; + +template <> +struct HashMapComparatorDefault<Plane> { + static bool compare(const Plane &p_lhs, const Plane &p_rhs) { + return HashMapComparatorDefault<Vector3>().compare(p_lhs.normal, p_rhs.normal) && ((p_lhs.d == p_rhs.d) || (Math::is_nan(p_lhs.d) && Math::is_nan(p_rhs.d))); + } +}; + +template <> +struct HashMapComparatorDefault<Transform2D> { + static bool compare(const Transform2D &p_lhs, const Transform2D &p_rhs) { + for (int i = 0; i < 3; ++i) { + if (!HashMapComparatorDefault<Vector2>().compare(p_lhs.columns[i], p_rhs.columns[i])) { + return false; + } + } + + return true; + } +}; + +template <> +struct HashMapComparatorDefault<Basis> { + static bool compare(const Basis &p_lhs, const Basis &p_rhs) { + for (int i = 0; i < 3; ++i) { + if (!HashMapComparatorDefault<Vector3>().compare(p_lhs.rows[i], p_rhs.rows[i])) { + return false; + } + } + + return true; + } +}; + +template <> +struct HashMapComparatorDefault<Transform3D> { + static bool compare(const Transform3D &p_lhs, const Transform3D &p_rhs) { + return HashMapComparatorDefault<Basis>().compare(p_lhs.basis, p_rhs.basis) && HashMapComparatorDefault<Vector3>().compare(p_lhs.origin, p_rhs.origin); + } +}; + +template <> +struct HashMapComparatorDefault<Projection> { + static bool compare(const Projection &p_lhs, const Projection &p_rhs) { + for (int i = 0; i < 4; ++i) { + if (!HashMapComparatorDefault<Vector4>().compare(p_lhs.columns[i], p_rhs.columns[i])) { + return false; + } + } + + return true; + } +}; + +template <> +struct HashMapComparatorDefault<Quaternion> { + static bool compare(const Quaternion &p_lhs, const Quaternion &p_rhs) { + return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z))) && ((p_lhs.w == p_rhs.w) || (Math::is_nan(p_lhs.w) && Math::is_nan(p_rhs.w))); + } +}; + constexpr uint32_t HASH_TABLE_SIZE_MAX = 29; inline constexpr uint32_t hash_table_size_primes[HASH_TABLE_SIZE_MAX] = { diff --git a/core/templates/list.h b/core/templates/list.h index 6663f06c30..02afeec74d 100644 --- a/core/templates/list.h +++ b/core/templates/list.h @@ -224,7 +224,7 @@ private: Element *last = nullptr; int size_cache = 0; - bool erase(const Element *p_I) { + bool erase(Element *p_I) { ERR_FAIL_NULL_V(p_I, false); ERR_FAIL_COND_V(p_I->data != this, false); @@ -244,7 +244,7 @@ private: p_I->next_ptr->prev_ptr = p_I->prev_ptr; } - memdelete_allocator<Element, A>(const_cast<Element *>(p_I)); + memdelete_allocator<Element, A>(p_I); size_cache--; return true; @@ -430,7 +430,7 @@ public: /** * erase an element in the list, by iterator pointing to it. Return true if it was found/erased. */ - bool erase(const Element *p_I) { + bool erase(Element *p_I) { if (_data && p_I) { bool ret = _data->erase(p_I); diff --git a/core/templates/lru.h b/core/templates/lru.h index 919c5605aa..7f48c3b2e8 100644 --- a/core/templates/lru.h +++ b/core/templates/lru.h @@ -35,9 +35,21 @@ #include "hash_map.h" #include "list.h" -template <typename TKey, typename TData, typename Hasher = HashMapHasherDefault, typename Comparator = HashMapComparatorDefault<TKey>> +#if defined(__GNUC__) && !defined(__clang__) +#define ADDRESS_DIAGNOSTIC_WARNING_DISABLE \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Waddress\""); + +#define ADDRESS_DIAGNOSTIC_POP \ + _Pragma("GCC diagnostic pop"); +#else +#define ADDRESS_DIAGNOSTIC_WARNING_DISABLE +#define ADDRESS_DIAGNOSTIC_POP +#endif + +template <typename TKey, typename TData, typename Hasher = HashMapHasherDefault, typename Comparator = HashMapComparatorDefault<TKey>, void (*BeforeEvict)(TKey &, TData &) = nullptr> class LRUCache { -private: +public: struct Pair { TKey key; TData data; @@ -51,16 +63,22 @@ private: typedef typename List<Pair>::Element *Element; +private: List<Pair> _list; HashMap<TKey, Element, Hasher, Comparator> _map; size_t capacity; public: - const TData *insert(const TKey &p_key, const TData &p_value) { + const Pair *insert(const TKey &p_key, const TData &p_value) { Element *e = _map.getptr(p_key); Element n = _list.push_front(Pair(p_key, p_value)); if (e) { + ADDRESS_DIAGNOSTIC_WARNING_DISABLE; + if constexpr (BeforeEvict != nullptr) { + BeforeEvict((*e)->get().key, (*e)->get().data); + } + ADDRESS_DIAGNOSTIC_POP; _list.erase(*e); _map.erase(p_key); } @@ -68,11 +86,16 @@ public: while (_map.size() > capacity) { Element d = _list.back(); + ADDRESS_DIAGNOSTIC_WARNING_DISABLE + if constexpr (BeforeEvict != nullptr) { + BeforeEvict(d->get().key, d->get().data); + } + ADDRESS_DIAGNOSTIC_POP _map.erase(d->get().key); _list.pop_back(); } - return &n->get().data; + return &n->get(); } void clear() { @@ -84,12 +107,23 @@ public: return _map.getptr(p_key); } + bool erase(const TKey &p_key) { + Element *e = _map.getptr(p_key); + if (!e) { + return false; + } + _list.move_to_front(*e); + _map.erase(p_key); + _list.pop_front(); + return true; + } + const TData &get(const TKey &p_key) { Element *e = _map.getptr(p_key); CRASH_COND(!e); _list.move_to_front(*e); return (*e)->get().data; - }; + } const TData *getptr(const TKey &p_key) { Element *e = _map.getptr(p_key); @@ -109,6 +143,11 @@ public: capacity = p_capacity; while (_map.size() > capacity) { Element d = _list.back(); + ADDRESS_DIAGNOSTIC_WARNING_DISABLE; + if constexpr (BeforeEvict != nullptr) { + BeforeEvict(d->get().key, d->get().data); + } + ADDRESS_DIAGNOSTIC_POP; _map.erase(d->get().key); _list.pop_back(); } @@ -124,4 +163,7 @@ public: } }; +#undef ADDRESS_DIAGNOSTIC_WARNING_DISABLE +#undef ADDRESS_DIAGNOSTIC_POP + #endif // LRU_H diff --git a/core/templates/rb_set.h b/core/templates/rb_set.h index ac7a8df36a..1b69f2f0c2 100644 --- a/core/templates/rb_set.h +++ b/core/templates/rb_set.h @@ -76,7 +76,7 @@ public: } const T &get() const { return value; - }; + } Element() {} }; diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h index 537413e2ba..4200159054 100644 --- a/core/templates/rid_owner.h +++ b/core/templates/rid_owner.h @@ -32,7 +32,7 @@ #define RID_OWNER_H #include "core/os/memory.h" -#include "core/os/spin_lock.h" +#include "core/os/mutex.h" #include "core/string/print_string.h" #include "core/templates/hash_set.h" #include "core/templates/list.h" @@ -69,42 +69,54 @@ public: template <typename T, bool THREAD_SAFE = false> class RID_Alloc : public RID_AllocBase { - T **chunks = nullptr; + struct Chunk { + T data; + uint32_t validator; + }; + Chunk **chunks = nullptr; uint32_t **free_list_chunks = nullptr; - uint32_t **validator_chunks = nullptr; uint32_t elements_in_chunk; uint32_t max_alloc = 0; uint32_t alloc_count = 0; + uint32_t chunk_limit = 0; const char *description = nullptr; - mutable SpinLock spin_lock; + mutable Mutex mutex; _FORCE_INLINE_ RID _allocate_rid() { if constexpr (THREAD_SAFE) { - spin_lock.lock(); + mutex.lock(); } if (alloc_count == max_alloc) { //allocate a new chunk uint32_t chunk_count = alloc_count == 0 ? 0 : (max_alloc / elements_in_chunk); + if (THREAD_SAFE && chunk_count == chunk_limit) { + mutex.unlock(); + if (description != nullptr) { + ERR_FAIL_V_MSG(RID(), vformat("Element limit for RID of type '%s' reached.", String(description))); + } else { + ERR_FAIL_V_MSG(RID(), "Element limit reached."); + } + } //grow chunks - chunks = (T **)memrealloc(chunks, sizeof(T *) * (chunk_count + 1)); - chunks[chunk_count] = (T *)memalloc(sizeof(T) * elements_in_chunk); //but don't initialize - - //grow validators - validator_chunks = (uint32_t **)memrealloc(validator_chunks, sizeof(uint32_t *) * (chunk_count + 1)); - validator_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk); + if constexpr (!THREAD_SAFE) { + chunks = (Chunk **)memrealloc(chunks, sizeof(Chunk *) * (chunk_count + 1)); + } + chunks[chunk_count] = (Chunk *)memalloc(sizeof(Chunk) * elements_in_chunk); //but don't initialize //grow free lists - free_list_chunks = (uint32_t **)memrealloc(free_list_chunks, sizeof(uint32_t *) * (chunk_count + 1)); + if constexpr (!THREAD_SAFE) { + free_list_chunks = (uint32_t **)memrealloc(free_list_chunks, sizeof(uint32_t *) * (chunk_count + 1)); + } free_list_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk); //initialize for (uint32_t i = 0; i < elements_in_chunk; i++) { // Don't initialize chunk. - validator_chunks[chunk_count][i] = 0xFFFFFFFF; + chunks[chunk_count][i].validator = 0xFFFFFFFF; free_list_chunks[chunk_count][i] = alloc_count + i; } @@ -122,14 +134,13 @@ class RID_Alloc : public RID_AllocBase { id <<= 32; id |= free_index; - validator_chunks[free_chunk][free_element] = validator; - - validator_chunks[free_chunk][free_element] |= 0x80000000; //mark uninitialized bit + chunks[free_chunk][free_element].validator = validator; + chunks[free_chunk][free_element].validator |= 0x80000000; //mark uninitialized bit alloc_count++; if constexpr (THREAD_SAFE) { - spin_lock.unlock(); + mutex.unlock(); } return _make_from_id(id); @@ -156,16 +167,10 @@ public: if (p_rid == RID()) { return nullptr; } - if constexpr (THREAD_SAFE) { - spin_lock.lock(); - } uint64_t id = p_rid.get_id(); uint32_t idx = uint32_t(id & 0xFFFFFFFF); if (unlikely(idx >= max_alloc)) { - if constexpr (THREAD_SAFE) { - spin_lock.unlock(); - } return nullptr; } @@ -174,38 +179,26 @@ public: uint32_t validator = uint32_t(id >> 32); + Chunk &c = chunks[idx_chunk][idx_element]; if (unlikely(p_initialize)) { - if (unlikely(!(validator_chunks[idx_chunk][idx_element] & 0x80000000))) { - if constexpr (THREAD_SAFE) { - spin_lock.unlock(); - } + if (unlikely(!(c.validator & 0x80000000))) { ERR_FAIL_V_MSG(nullptr, "Initializing already initialized RID"); } - if (unlikely((validator_chunks[idx_chunk][idx_element] & 0x7FFFFFFF) != validator)) { - if constexpr (THREAD_SAFE) { - spin_lock.unlock(); - } + if (unlikely((c.validator & 0x7FFFFFFF) != validator)) { ERR_FAIL_V_MSG(nullptr, "Attempting to initialize the wrong RID"); } - validator_chunks[idx_chunk][idx_element] &= 0x7FFFFFFF; //initialized + c.validator &= 0x7FFFFFFF; //initialized - } else if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) { - if constexpr (THREAD_SAFE) { - spin_lock.unlock(); - } - if ((validator_chunks[idx_chunk][idx_element] & 0x80000000) && validator_chunks[idx_chunk][idx_element] != 0xFFFFFFFF) { + } else if (unlikely(c.validator != validator)) { + if ((c.validator & 0x80000000) && c.validator != 0xFFFFFFFF) { ERR_FAIL_V_MSG(nullptr, "Attempting to use an uninitialized RID"); } return nullptr; } - T *ptr = &chunks[idx_chunk][idx_element]; - - if constexpr (THREAD_SAFE) { - spin_lock.unlock(); - } + T *ptr = &c.data; return ptr; } @@ -222,14 +215,14 @@ public: _FORCE_INLINE_ bool owns(const RID &p_rid) const { if constexpr (THREAD_SAFE) { - spin_lock.lock(); + mutex.lock(); } uint64_t id = p_rid.get_id(); uint32_t idx = uint32_t(id & 0xFFFFFFFF); if (unlikely(idx >= max_alloc)) { if constexpr (THREAD_SAFE) { - spin_lock.unlock(); + mutex.unlock(); } return false; } @@ -239,10 +232,10 @@ public: uint32_t validator = uint32_t(id >> 32); - bool owned = (validator != 0x7FFFFFFF) && (validator_chunks[idx_chunk][idx_element] & 0x7FFFFFFF) == validator; + bool owned = (validator != 0x7FFFFFFF) && (chunks[idx_chunk][idx_element].validator & 0x7FFFFFFF) == validator; if constexpr (THREAD_SAFE) { - spin_lock.unlock(); + mutex.unlock(); } return owned; @@ -250,14 +243,14 @@ public: _FORCE_INLINE_ void free(const RID &p_rid) { if constexpr (THREAD_SAFE) { - spin_lock.lock(); + mutex.lock(); } uint64_t id = p_rid.get_id(); uint32_t idx = uint32_t(id & 0xFFFFFFFF); if (unlikely(idx >= max_alloc)) { if constexpr (THREAD_SAFE) { - spin_lock.unlock(); + mutex.unlock(); } ERR_FAIL(); } @@ -266,26 +259,26 @@ public: uint32_t idx_element = idx % elements_in_chunk; uint32_t validator = uint32_t(id >> 32); - if (unlikely(validator_chunks[idx_chunk][idx_element] & 0x80000000)) { + if (unlikely(chunks[idx_chunk][idx_element].validator & 0x80000000)) { if constexpr (THREAD_SAFE) { - spin_lock.unlock(); + mutex.unlock(); } - ERR_FAIL_MSG("Attempted to free an uninitialized or invalid RID."); - } else if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) { + ERR_FAIL_MSG("Attempted to free an uninitialized or invalid RID"); + } else if (unlikely(chunks[idx_chunk][idx_element].validator != validator)) { if constexpr (THREAD_SAFE) { - spin_lock.unlock(); + mutex.unlock(); } ERR_FAIL(); } - chunks[idx_chunk][idx_element].~T(); - validator_chunks[idx_chunk][idx_element] = 0xFFFFFFFF; // go invalid + chunks[idx_chunk][idx_element].data.~T(); + chunks[idx_chunk][idx_element].validator = 0xFFFFFFFF; // go invalid alloc_count--; free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk] = idx; if constexpr (THREAD_SAFE) { - spin_lock.unlock(); + mutex.unlock(); } } @@ -294,34 +287,35 @@ public: } void get_owned_list(List<RID> *p_owned) const { if constexpr (THREAD_SAFE) { - spin_lock.lock(); + mutex.lock(); } for (size_t i = 0; i < max_alloc; i++) { - uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk]; + uint64_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator; if (validator != 0xFFFFFFFF) { p_owned->push_back(_make_from_id((validator << 32) | i)); } } if constexpr (THREAD_SAFE) { - spin_lock.unlock(); + mutex.unlock(); } } //used for fast iteration in the elements or RIDs void fill_owned_buffer(RID *p_rid_buffer) const { if constexpr (THREAD_SAFE) { - spin_lock.lock(); + mutex.lock(); } uint32_t idx = 0; for (size_t i = 0; i < max_alloc; i++) { - uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk]; + uint64_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator; if (validator != 0xFFFFFFFF) { p_rid_buffer[idx] = _make_from_id((validator << 32) | i); idx++; } } + if constexpr (THREAD_SAFE) { - spin_lock.unlock(); + mutex.unlock(); } } @@ -329,8 +323,13 @@ public: description = p_descrption; } - RID_Alloc(uint32_t p_target_chunk_byte_size = 65536) { + RID_Alloc(uint32_t p_target_chunk_byte_size = 65536, uint32_t p_maximum_number_of_elements = 262144) { elements_in_chunk = sizeof(T) > p_target_chunk_byte_size ? 1 : (p_target_chunk_byte_size / sizeof(T)); + if constexpr (THREAD_SAFE) { + chunk_limit = (p_maximum_number_of_elements / elements_in_chunk) + 1; + chunks = (Chunk **)memalloc(sizeof(Chunk *) * chunk_limit); + free_list_chunks = (uint32_t **)memalloc(sizeof(uint32_t *) * chunk_limit); + } } ~RID_Alloc() { @@ -339,12 +338,12 @@ public: alloc_count, description ? description : typeid(T).name())); for (size_t i = 0; i < max_alloc; i++) { - uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk]; + uint64_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator; if (validator & 0x80000000) { continue; //uninitialized } if (validator != 0xFFFFFFFF) { - chunks[i / elements_in_chunk][i % elements_in_chunk].~T(); + chunks[i / elements_in_chunk][i % elements_in_chunk].data.~T(); } } } @@ -352,14 +351,12 @@ public: uint32_t chunk_count = max_alloc / elements_in_chunk; for (uint32_t i = 0; i < chunk_count; i++) { memfree(chunks[i]); - memfree(validator_chunks[i]); memfree(free_list_chunks[i]); } if (chunks) { memfree(chunks); memfree(free_list_chunks); - memfree(validator_chunks); } } }; @@ -419,8 +416,8 @@ public: alloc.set_description(p_descrption); } - RID_PtrOwner(uint32_t p_target_chunk_byte_size = 65536) : - alloc(p_target_chunk_byte_size) {} + RID_PtrOwner(uint32_t p_target_chunk_byte_size = 65536, uint32_t p_maximum_number_of_elements = 262144) : + alloc(p_target_chunk_byte_size, p_maximum_number_of_elements) {} }; template <typename T, bool THREAD_SAFE = false> @@ -473,8 +470,8 @@ public: void set_description(const char *p_descrption) { alloc.set_description(p_descrption); } - RID_Owner(uint32_t p_target_chunk_byte_size = 65536) : - alloc(p_target_chunk_byte_size) {} + RID_Owner(uint32_t p_target_chunk_byte_size = 65536, uint32_t p_maximum_number_of_elements = 262144) : + alloc(p_target_chunk_byte_size, p_maximum_number_of_elements) {} }; #endif // RID_OWNER_H diff --git a/core/variant/SCsub b/core/variant/SCsub index 7f4c8b7788..8264503a22 100644 --- a/core/variant/SCsub +++ b/core/variant/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/variant/array.cpp b/core/variant/array.cpp index 3e62d3dffa..f7a86b8fa3 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -385,7 +385,7 @@ int Array::find_custom(const Callable &p_callable, int p_from) const { Callable::CallError ce; p_callable.callp(argptrs, 1, res, ce); if (unlikely(ce.error != Callable::CallError::CALL_OK)) { - ERR_FAIL_V_MSG(ret, "Error calling method from 'find_custom': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce)); + ERR_FAIL_V_MSG(ret, vformat("Error calling method from 'find_custom': %s.", Variant::get_callable_error_text(p_callable, argptrs, 1, ce))); } ERR_FAIL_COND_V_MSG(res.get_type() != Variant::Type::BOOL, ret, "Error on method from 'find_custom': Return type of callable must be boolean."); @@ -445,7 +445,7 @@ int Array::rfind_custom(const Callable &p_callable, int p_from) const { Callable::CallError ce; p_callable.callp(argptrs, 1, res, ce); if (unlikely(ce.error != Callable::CallError::CALL_OK)) { - ERR_FAIL_V_MSG(-1, "Error calling method from 'rfind_custom': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce)); + ERR_FAIL_V_MSG(-1, vformat("Error calling method from 'rfind_custom': %s.", Variant::get_callable_error_text(p_callable, argptrs, 1, ce))); } ERR_FAIL_COND_V_MSG(res.get_type() != Variant::Type::BOOL, -1, "Error on method from 'rfind_custom': Return type of callable must be boolean."); @@ -574,7 +574,7 @@ Array Array::filter(const Callable &p_callable) const { Callable::CallError ce; p_callable.callp(argptrs, 1, result, ce); if (ce.error != Callable::CallError::CALL_OK) { - ERR_FAIL_V_MSG(Array(), "Error calling method from 'filter': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce)); + ERR_FAIL_V_MSG(Array(), vformat("Error calling method from 'filter': %s.", Variant::get_callable_error_text(p_callable, argptrs, 1, ce))); } if (result.operator bool()) { @@ -600,7 +600,7 @@ Array Array::map(const Callable &p_callable) const { Callable::CallError ce; p_callable.callp(argptrs, 1, result, ce); if (ce.error != Callable::CallError::CALL_OK) { - ERR_FAIL_V_MSG(Array(), "Error calling method from 'map': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce)); + ERR_FAIL_V_MSG(Array(), vformat("Error calling method from 'map': %s.", Variant::get_callable_error_text(p_callable, argptrs, 1, ce))); } new_arr[i] = result; @@ -626,7 +626,7 @@ Variant Array::reduce(const Callable &p_callable, const Variant &p_accum) const Callable::CallError ce; p_callable.callp(argptrs, 2, result, ce); if (ce.error != Callable::CallError::CALL_OK) { - ERR_FAIL_V_MSG(Variant(), "Error calling method from 'reduce': " + Variant::get_callable_error_text(p_callable, argptrs, 2, ce)); + ERR_FAIL_V_MSG(Variant(), vformat("Error calling method from 'reduce': %s.", Variant::get_callable_error_text(p_callable, argptrs, 2, ce))); } ret = result; } @@ -643,7 +643,7 @@ bool Array::any(const Callable &p_callable) const { Callable::CallError ce; p_callable.callp(argptrs, 1, result, ce); if (ce.error != Callable::CallError::CALL_OK) { - ERR_FAIL_V_MSG(false, "Error calling method from 'any': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce)); + ERR_FAIL_V_MSG(false, vformat("Error calling method from 'any': %s.", Variant::get_callable_error_text(p_callable, argptrs, 1, ce))); } if (result.operator bool()) { @@ -665,7 +665,7 @@ bool Array::all(const Callable &p_callable) const { Callable::CallError ce; p_callable.callp(argptrs, 1, result, ce); if (ce.error != Callable::CallError::CALL_OK) { - ERR_FAIL_V_MSG(false, "Error calling method from 'all': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce)); + ERR_FAIL_V_MSG(false, vformat("Error calling method from 'all': %s.", Variant::get_callable_error_text(p_callable, argptrs, 1, ce))); } if (!(result.operator bool())) { diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index fa49767d46..0aa49f6d68 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -466,7 +466,7 @@ void call_with_variant_argsc(T *p_instance, void (T::*p_method)(P...) const, con return; } #endif - call_with_variant_args_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); + call_with_variant_argsc_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); } template <typename T, typename... P> @@ -830,7 +830,7 @@ void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_ar } template <typename... P> -void call_with_variant_args_static_ret(void (*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) { +void call_with_variant_args_static(void (*p_method)(P...), const Variant **p_args, int p_argcount, Callable::CallError &r_error) { #ifdef DEBUG_METHODS_ENABLED if ((size_t)p_argcount > sizeof...(P)) { r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp index bb2d0313f6..5ce90cd8ff 100644 --- a/core/variant/callable.cpp +++ b/core/variant/callable.cpp @@ -315,31 +315,32 @@ bool Callable::operator<(const Callable &p_callable) const { } void Callable::operator=(const Callable &p_callable) { + CallableCustom *cleanup_ref = nullptr; if (is_custom()) { if (p_callable.is_custom()) { if (custom == p_callable.custom) { return; } } - - if (custom->ref_count.unref()) { - memdelete(custom); - custom = nullptr; - } + cleanup_ref = custom; + custom = nullptr; } if (p_callable.is_custom()) { method = StringName(); - if (!p_callable.custom->ref_count.ref()) { - object = 0; - } else { - object = 0; + object = 0; + if (p_callable.custom->ref_count.ref()) { custom = p_callable.custom; } } else { method = p_callable.method; object = p_callable.object; } + + if (cleanup_ref != nullptr && cleanup_ref->ref_count.unref()) { + memdelete(cleanup_ref); + } + cleanup_ref = nullptr; } Callable::operator String() const { diff --git a/core/variant/container_type_validate.h b/core/variant/container_type_validate.h index 0a23c69cb4..8971fadf73 100644 --- a/core/variant/container_type_validate.h +++ b/core/variant/container_type_validate.h @@ -94,7 +94,7 @@ struct ContainerTypeValidate { return true; } - ERR_FAIL_V_MSG(false, "Attempted to " + String(p_operation) + " a variable of type '" + Variant::get_type_name(inout_variant.get_type()) + "' into a " + where + " of type '" + Variant::get_type_name(type) + "'."); + ERR_FAIL_V_MSG(false, vformat("Attempted to %s a variable of type '%s' into a %s of type '%s'.", String(p_operation), Variant::get_type_name(inout_variant.get_type()), where, Variant::get_type_name(type))); } if (type != Variant::OBJECT) { @@ -113,7 +113,7 @@ struct ContainerTypeValidate { return true; // This is fine, it's null. } Object *object = ObjectDB::get_instance(object_id); - ERR_FAIL_NULL_V_MSG(object, false, "Attempted to " + String(p_operation) + " an invalid (previously freed?) object instance into a '" + String(where) + "."); + ERR_FAIL_NULL_V_MSG(object, false, vformat("Attempted to %s an invalid (previously freed?) object instance into a '%s'.", String(p_operation), String(where))); #else Object *object = p_variant; if (object == nullptr) { @@ -126,7 +126,7 @@ struct ContainerTypeValidate { StringName obj_class = object->get_class_name(); if (obj_class != class_name) { - ERR_FAIL_COND_V_MSG(!ClassDB::is_parent_class(object->get_class_name(), class_name), false, "Attempted to " + String(p_operation) + " an object of type '" + object->get_class() + "' into a " + where + ", which does not inherit from '" + String(class_name) + "'."); + ERR_FAIL_COND_V_MSG(!ClassDB::is_parent_class(object->get_class_name(), class_name), false, vformat("Attempted to %s an object of type '%s' into a %s, which does not inherit from '%s'.", String(p_operation), object->get_class(), where, String(class_name))); } if (script.is_null()) { @@ -136,8 +136,8 @@ struct ContainerTypeValidate { Ref<Script> other_script = object->get_script(); // Check base script.. - ERR_FAIL_COND_V_MSG(other_script.is_null(), false, "Attempted to " + String(p_operation) + " an object into a " + String(where) + ", that does not inherit from '" + String(script->get_class_name()) + "'."); - ERR_FAIL_COND_V_MSG(!other_script->inherits_script(script), false, "Attempted to " + String(p_operation) + " an object into a " + String(where) + ", that does not inherit from '" + String(script->get_class_name()) + "'."); + ERR_FAIL_COND_V_MSG(other_script.is_null(), false, vformat("Attempted to %s an object into a %s, that does not inherit from '%s'.", String(p_operation), String(where), String(script->get_class_name()))); + ERR_FAIL_COND_V_MSG(!other_script->inherits_script(script), false, vformat("Attempted to %s an object into a %s, that does not inherit from '%s'.", String(p_operation), String(where), String(script->get_class_name()))); return true; } diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp index 2db754438f..501ca69205 100644 --- a/core/variant/dictionary.cpp +++ b/core/variant/dictionary.cpp @@ -294,6 +294,11 @@ void Dictionary::clear() { _p->variant_map.clear(); } +void Dictionary::sort() { + ERR_FAIL_COND_MSG(_p->read_only, "Dictionary is in read-only state."); + _p->variant_map.sort(); +} + void Dictionary::merge(const Dictionary &p_dictionary, bool p_overwrite) { ERR_FAIL_COND_MSG(_p->read_only, "Dictionary is in read-only state."); for (const KeyValue<Variant, Variant> &E : p_dictionary._p->variant_map) { diff --git a/core/variant/dictionary.h b/core/variant/dictionary.h index 5f3ce40219..bbfb5b3083 100644 --- a/core/variant/dictionary.h +++ b/core/variant/dictionary.h @@ -64,6 +64,7 @@ public: int size() const; bool is_empty() const; void clear(); + void sort(); void merge(const Dictionary &p_dictionary, bool p_overwrite = false); Dictionary merged(const Dictionary &p_dictionary, bool p_overwrite = false) const; diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index 186643b024..65bfc29a55 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -1072,17 +1072,69 @@ bool Variant::is_null() const { } } +void Variant::ObjData::ref(const ObjData &p_from) { + // Mirrors Ref::ref in refcounted.h + if (p_from.id == id) { + return; + } + + ObjData cleanup_ref = *this; + + *this = p_from; + if (id.is_ref_counted()) { + RefCounted *reference = static_cast<RefCounted *>(obj); + // Assuming reference is not null because id.is_ref_counted() was true. + if (!reference->reference()) { + *this = ObjData(); + } + } + + cleanup_ref.unref(); +} + +void Variant::ObjData::ref_pointer(Object *p_object) { + // Mirrors Ref::ref_pointer in refcounted.h + if (p_object == obj) { + return; + } + + ObjData cleanup_ref = *this; + + if (p_object) { + *this = ObjData{ p_object->get_instance_id(), p_object }; + if (p_object->is_ref_counted()) { + RefCounted *reference = static_cast<RefCounted *>(p_object); + if (!reference->init_ref()) { + *this = ObjData(); + } + } + } else { + *this = ObjData(); + } + + cleanup_ref.unref(); +} + +void Variant::ObjData::unref() { + // Mirrors Ref::unref in refcounted.h + if (id.is_ref_counted()) { + RefCounted *reference = static_cast<RefCounted *>(obj); + // Assuming reference is not null because id.is_ref_counted() was true. + if (reference->unreference()) { + memdelete(reference); + } + } + *this = ObjData(); +} + void Variant::reference(const Variant &p_variant) { - switch (type) { - case NIL: - case BOOL: - case INT: - case FLOAT: - break; - default: - clear(); + if (type == OBJECT && p_variant.type == OBJECT) { + _get_obj().ref(p_variant._get_obj()); + return; } + clear(); + type = p_variant.type; switch (p_variant.type) { @@ -1165,18 +1217,7 @@ void Variant::reference(const Variant &p_variant) { } break; case OBJECT: { memnew_placement(_data._mem, ObjData); - - if (p_variant._get_obj().obj && p_variant._get_obj().id.is_ref_counted()) { - RefCounted *ref_counted = static_cast<RefCounted *>(p_variant._get_obj().obj); - if (!ref_counted->reference()) { - _get_obj().obj = nullptr; - _get_obj().id = ObjectID(); - break; - } - } - - _get_obj().obj = const_cast<Object *>(p_variant._get_obj().obj); - _get_obj().id = p_variant._get_obj().id; + _get_obj().ref(p_variant._get_obj()); } break; case CALLABLE: { memnew_placement(_data._mem, Callable(*reinterpret_cast<const Callable *>(p_variant._data._mem))); @@ -1375,15 +1416,7 @@ void Variant::_clear_internal() { reinterpret_cast<NodePath *>(_data._mem)->~NodePath(); } break; case OBJECT: { - if (_get_obj().id.is_ref_counted()) { - // We are safe that there is a reference here. - RefCounted *ref_counted = static_cast<RefCounted *>(_get_obj().obj); - if (ref_counted->unreference()) { - memdelete(ref_counted); - } - } - _get_obj().obj = nullptr; - _get_obj().id = ObjectID(); + _get_obj().unref(); } break; case RID: { // Not much need probably. @@ -1703,7 +1736,7 @@ String Variant::stringify(int recursion_count) const { case INT: return itos(_data._int); case FLOAT: - return rtos(_data._float); + return String::num_real(_data._float, true); case STRING: return *reinterpret_cast<const String *>(_data._mem); case VECTOR2: @@ -2589,24 +2622,8 @@ Variant::Variant(const ::RID &p_rid) : Variant::Variant(const Object *p_object) : type(OBJECT) { - memnew_placement(_data._mem, ObjData); - - if (p_object) { - if (p_object->is_ref_counted()) { - RefCounted *ref_counted = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_object)); - if (!ref_counted->init_ref()) { - _get_obj().obj = nullptr; - _get_obj().id = ObjectID(); - return; - } - } - - _get_obj().obj = const_cast<Object *>(p_object); - _get_obj().id = p_object->get_instance_id(); - } else { - _get_obj().obj = nullptr; - _get_obj().id = ObjectID(); - } + _get_obj() = ObjData(); + _get_obj().ref_pointer(const_cast<Object *>(p_object)); } Variant::Variant(const Callable &p_callable) : @@ -2702,8 +2719,7 @@ Variant::Variant(const Vector<Plane> &p_array) : } } -Variant::Variant(const Vector<Face3> &p_face_array) : - type(NIL) { +Variant::Variant(const Vector<Face3> &p_face_array) { PackedVector3Array vertices; int face_count = p_face_array.size(); vertices.resize(face_count * 3); @@ -2722,8 +2738,7 @@ Variant::Variant(const Vector<Face3> &p_face_array) : *this = vertices; } -Variant::Variant(const Vector<Variant> &p_array) : - type(NIL) { +Variant::Variant(const Vector<Variant> &p_array) { Array arr; arr.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { @@ -2732,8 +2747,7 @@ Variant::Variant(const Vector<Variant> &p_array) : *this = arr; } -Variant::Variant(const Vector<StringName> &p_array) : - type(NIL) { +Variant::Variant(const Vector<StringName> &p_array) { PackedStringArray v; int len = p_array.size(); v.resize(len); @@ -2828,26 +2842,7 @@ void Variant::operator=(const Variant &p_variant) { *reinterpret_cast<::RID *>(_data._mem) = *reinterpret_cast<const ::RID *>(p_variant._data._mem); } break; case OBJECT: { - if (_get_obj().id.is_ref_counted()) { - //we are safe that there is a reference here - RefCounted *ref_counted = static_cast<RefCounted *>(_get_obj().obj); - if (ref_counted->unreference()) { - memdelete(ref_counted); - } - } - - if (p_variant._get_obj().obj && p_variant._get_obj().id.is_ref_counted()) { - RefCounted *ref_counted = static_cast<RefCounted *>(p_variant._get_obj().obj); - if (!ref_counted->reference()) { - _get_obj().obj = nullptr; - _get_obj().id = ObjectID(); - break; - } - } - - _get_obj().obj = const_cast<Object *>(p_variant._get_obj().obj); - _get_obj().id = p_variant._get_obj().id; - + _get_obj().ref(p_variant._get_obj()); } break; case CALLABLE: { *reinterpret_cast<Callable *>(_data._mem) = *reinterpret_cast<const Callable *>(p_variant._data._mem); @@ -2910,8 +2905,7 @@ Variant::Variant(const IPAddress &p_address) : memnew_placement(_data._mem, String(p_address)); } -Variant::Variant(const Variant &p_variant) : - type(NIL) { +Variant::Variant(const Variant &p_variant) { reference(p_variant); } diff --git a/core/variant/variant.h b/core/variant/variant.h index d4e4b330cd..9702c67a37 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -62,6 +62,10 @@ #include "core/variant/dictionary.h" class Object; +class RefCounted; + +template <typename T> +class Ref; struct PropertyInfo; struct MethodInfo; @@ -175,6 +179,20 @@ private: struct ObjData { ObjectID id; Object *obj = nullptr; + + void ref(const ObjData &p_from); + void ref_pointer(Object *p_object); + void ref_pointer(RefCounted *p_object); + void unref(); + + template <typename T> + _ALWAYS_INLINE_ void ref(const Ref<T> &p_from) { + if (p_from.is_valid()) { + ref(ObjData{ p_from->get_instance_id(), p_from.ptr() }); + } else { + unref(); + } + } }; /* array helpers */ @@ -796,8 +814,7 @@ public: static void unregister_types(); Variant(const Variant &p_variant); - _FORCE_INLINE_ Variant() : - type(NIL) {} + _FORCE_INLINE_ Variant() {} _FORCE_INLINE_ ~Variant() { clear(); } @@ -836,6 +853,19 @@ struct StringLikeVariantComparator { static bool compare(const Variant &p_lhs, const Variant &p_rhs); }; +struct StringLikeVariantOrder { + static _ALWAYS_INLINE_ bool compare(const Variant &p_lhs, const Variant &p_rhs) { + if (p_lhs.is_string() && p_rhs.is_string()) { + return p_lhs.operator String() < p_rhs.operator String(); + } + return p_lhs < p_rhs; + } + + _ALWAYS_INLINE_ bool operator()(const Variant &p_lhs, const Variant &p_rhs) const { + return compare(p_lhs, p_rhs); + } +}; + Variant::ObjData &Variant::_get_obj() { return *reinterpret_cast<ObjData *>(&_data._mem[0]); } diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 63fb5e8d94..29e11462c9 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -657,7 +657,23 @@ static _FORCE_INLINE_ void vc_ptrcall(void (*method)(T *, P...), void *p_base, c } \ }; +#define VARCALL_PACKED_GETTER(m_packed_type, m_return_type) \ + static m_return_type func_##m_packed_type##_get(m_packed_type *p_instance, int64_t p_index) { \ + return p_instance->get(p_index); \ + } + struct _VariantCall { + VARCALL_PACKED_GETTER(PackedByteArray, uint8_t) + VARCALL_PACKED_GETTER(PackedColorArray, Color) + VARCALL_PACKED_GETTER(PackedFloat32Array, float) + VARCALL_PACKED_GETTER(PackedFloat64Array, double) + VARCALL_PACKED_GETTER(PackedInt32Array, int32_t) + VARCALL_PACKED_GETTER(PackedInt64Array, int64_t) + VARCALL_PACKED_GETTER(PackedStringArray, String) + VARCALL_PACKED_GETTER(PackedVector2Array, Vector2) + VARCALL_PACKED_GETTER(PackedVector3Array, Vector3) + VARCALL_PACKED_GETTER(PackedVector4Array, Vector4) + static String func_PackedByteArray_get_string_from_ascii(PackedByteArray *p_instance) { String s; if (p_instance->size() > 0) { @@ -2256,6 +2272,7 @@ static void _register_variant_builtin_methods_misc() { bind_method(Dictionary, is_empty, sarray(), varray()); bind_method(Dictionary, clear, sarray(), varray()); bind_method(Dictionary, assign, sarray("dictionary"), varray()); + bind_method(Dictionary, sort, sarray(), varray()); bind_method(Dictionary, merge, sarray("dictionary", "overwrite"), varray(false)); bind_method(Dictionary, merged, sarray("dictionary", "overwrite"), varray(false)); bind_method(Dictionary, has, sarray("key"), varray()); @@ -2268,6 +2285,7 @@ static void _register_variant_builtin_methods_misc() { bind_method(Dictionary, duplicate, sarray("deep"), varray(false)); bind_method(Dictionary, get, sarray("key", "default"), varray(Variant())); bind_method(Dictionary, get_or_add, sarray("key", "default"), varray(Variant())); + bind_method(Dictionary, set, sarray("key", "value"), varray()); bind_method(Dictionary, is_typed, sarray(), varray()); bind_method(Dictionary, is_typed_key, sarray(), varray()); bind_method(Dictionary, is_typed_value, sarray(), varray()); @@ -2293,6 +2311,8 @@ static void _register_variant_builtin_methods_array() { bind_method(Array, clear, sarray(), varray()); bind_method(Array, hash, sarray(), varray()); bind_method(Array, assign, sarray("array"), varray()); + bind_method(Array, get, sarray("index"), varray()); + bind_method(Array, set, sarray("index", "value"), varray()); bind_method(Array, push_back, sarray("value"), varray()); bind_method(Array, push_front, sarray("value"), varray()); bind_method(Array, append, sarray("value"), varray()); @@ -2337,6 +2357,18 @@ static void _register_variant_builtin_methods_array() { bind_method(Array, make_read_only, sarray(), varray()); bind_method(Array, is_read_only, sarray(), varray()); + /* Packed*Array get (see VARCALL_PACKED_GETTER macro) */ + bind_function(PackedByteArray, get, _VariantCall::func_PackedByteArray_get, sarray("index"), varray()); + bind_function(PackedColorArray, get, _VariantCall::func_PackedColorArray_get, sarray("index"), varray()); + bind_function(PackedFloat32Array, get, _VariantCall::func_PackedFloat32Array_get, sarray("index"), varray()); + bind_function(PackedFloat64Array, get, _VariantCall::func_PackedFloat64Array_get, sarray("index"), varray()); + bind_function(PackedInt32Array, get, _VariantCall::func_PackedInt32Array_get, sarray("index"), varray()); + bind_function(PackedInt64Array, get, _VariantCall::func_PackedInt64Array_get, sarray("index"), varray()); + bind_function(PackedStringArray, get, _VariantCall::func_PackedStringArray_get, sarray("index"), varray()); + bind_function(PackedVector2Array, get, _VariantCall::func_PackedVector2Array_get, sarray("index"), varray()); + bind_function(PackedVector3Array, get, _VariantCall::func_PackedVector3Array_get, sarray("index"), varray()); + bind_function(PackedVector4Array, get, _VariantCall::func_PackedVector4Array_get, sarray("index"), varray()); + /* Byte Array */ bind_method(PackedByteArray, size, sarray(), varray()); bind_method(PackedByteArray, is_empty, sarray(), varray()); diff --git a/core/variant/variant_construct.cpp b/core/variant/variant_construct.cpp index fb75a874e7..9706457549 100644 --- a/core/variant/variant_construct.cpp +++ b/core/variant/variant_construct.cpp @@ -43,7 +43,7 @@ static LocalVector<VariantConstructData> construct_data[Variant::VARIANT_MAX]; template <typename T> static void add_constructor(const Vector<String> &arg_names) { - ERR_FAIL_COND_MSG(arg_names.size() != T::get_argument_count(), "Argument names size mismatch for " + Variant::get_type_name(T::get_base_type()) + "."); + ERR_FAIL_COND_MSG(arg_names.size() != T::get_argument_count(), vformat("Argument names size mismatch for '%s'.", Variant::get_type_name(T::get_base_type()))); VariantConstructData cd; cd.construct = T::construct; @@ -323,36 +323,6 @@ String Variant::get_constructor_argument_name(Variant::Type p_type, int p_constr return construct_data[p_type][p_constructor].arg_names[p_argument]; } -void VariantInternal::refcounted_object_assign(Variant *v, const RefCounted *rc) { - if (!rc || !const_cast<RefCounted *>(rc)->init_ref()) { - v->_get_obj().obj = nullptr; - v->_get_obj().id = ObjectID(); - return; - } - - v->_get_obj().obj = const_cast<RefCounted *>(rc); - v->_get_obj().id = rc->get_instance_id(); -} - -void VariantInternal::object_assign(Variant *v, const Object *o) { - if (o) { - if (o->is_ref_counted()) { - RefCounted *ref_counted = const_cast<RefCounted *>(static_cast<const RefCounted *>(o)); - if (!ref_counted->init_ref()) { - v->_get_obj().obj = nullptr; - v->_get_obj().id = ObjectID(); - return; - } - } - - v->_get_obj().obj = const_cast<Object *>(o); - v->_get_obj().id = o->get_instance_id(); - } else { - v->_get_obj().obj = nullptr; - v->_get_obj().id = ObjectID(); - } -} - void Variant::get_constructor_list(Type p_type, List<MethodInfo> *r_list) { ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX); diff --git a/core/variant/variant_construct.h b/core/variant/variant_construct.h index 68210a9451..f625419da7 100644 --- a/core/variant/variant_construct.h +++ b/core/variant/variant_construct.h @@ -156,14 +156,14 @@ public: if (p_args[0]->get_type() == Variant::NIL) { VariantInternal::clear(&r_ret); VariantTypeChanger<Object *>::change(&r_ret); - VariantInternal::object_assign_null(&r_ret); + VariantInternal::object_reset_data(&r_ret); r_error.error = Callable::CallError::CALL_OK; } else if (p_args[0]->get_type() == Variant::OBJECT) { - VariantInternal::clear(&r_ret); VariantTypeChanger<Object *>::change(&r_ret); VariantInternal::object_assign(&r_ret, p_args[0]); r_error.error = Callable::CallError::CALL_OK; } else { + VariantInternal::clear(&r_ret); r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; r_error.expected = Variant::OBJECT; @@ -171,7 +171,6 @@ public: } static inline void validated_construct(Variant *r_ret, const Variant **p_args) { - VariantInternal::clear(r_ret); VariantTypeChanger<Object *>::change(r_ret); VariantInternal::object_assign(r_ret, p_args[0]); } @@ -203,13 +202,13 @@ public: VariantInternal::clear(&r_ret); VariantTypeChanger<Object *>::change(&r_ret); - VariantInternal::object_assign_null(&r_ret); + VariantInternal::object_reset_data(&r_ret); } static inline void validated_construct(Variant *r_ret, const Variant **p_args) { VariantInternal::clear(r_ret); VariantTypeChanger<Object *>::change(r_ret); - VariantInternal::object_assign_null(r_ret); + VariantInternal::object_reset_data(r_ret); } static void ptr_construct(void *base, const void **p_args) { PtrConstruct<Object *>::construct(nullptr, base); diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index 58a45c0a1f..58652d26e0 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -220,7 +220,7 @@ public: // Should be in the same order as Variant::Type for consistency. // Those primitive and vector types don't need an `init_` method: // Nil, bool, float, Vector2/i, Rect2/i, Vector3/i, Plane, Quat, RID. - // Object is a special case, handled via `object_assign_null`. + // Object is a special case, handled via `object_reset_data`. _FORCE_INLINE_ static void init_string(Variant *v) { memnew_placement(v->_data._mem, String); v->type = Variant::STRING; @@ -319,7 +319,7 @@ public: v->type = Variant::PACKED_VECTOR4_ARRAY; } _FORCE_INLINE_ static void init_object(Variant *v) { - object_assign_null(v); + object_reset_data(v); v->type = Variant::OBJECT; } @@ -327,19 +327,28 @@ public: v->clear(); } - static void object_assign(Variant *v, const Object *o); // Needs RefCounted, so it's implemented elsewhere. - static void refcounted_object_assign(Variant *v, const RefCounted *rc); + _FORCE_INLINE_ static void object_assign(Variant *v, const Variant *vo) { + v->_get_obj().ref(vo->_get_obj()); + } + + _FORCE_INLINE_ static void object_assign(Variant *v, Object *o) { + v->_get_obj().ref_pointer(o); + } - _FORCE_INLINE_ static void object_assign(Variant *v, const Variant *o) { - object_assign(v, o->_get_obj().obj); + _FORCE_INLINE_ static void object_assign(Variant *v, const Object *o) { + v->_get_obj().ref_pointer(const_cast<Object *>(o)); + } + + template <typename T> + _FORCE_INLINE_ static void object_assign(Variant *v, const Ref<T> &r) { + v->_get_obj().ref(r); } - _FORCE_INLINE_ static void object_assign_null(Variant *v) { - v->_get_obj().obj = nullptr; - v->_get_obj().id = ObjectID(); + _FORCE_INLINE_ static void object_reset_data(Variant *v) { + v->_get_obj() = Variant::ObjData(); } - static void update_object_id(Variant *v) { + _FORCE_INLINE_ static void update_object_id(Variant *v) { const Object *o = v->_get_obj().obj; if (o) { v->_get_obj().id = o->get_instance_id(); diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp index d2c1cde970..ce27fbdf67 100644 --- a/core/variant/variant_op.cpp +++ b/core/variant/variant_op.cpp @@ -980,6 +980,7 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorInDictionaryHas<Color>>(Variant::OP_IN, Variant::COLOR, Variant::DICTIONARY); register_op<OperatorEvaluatorInDictionaryHas<StringName>>(Variant::OP_IN, Variant::STRING_NAME, Variant::DICTIONARY); register_op<OperatorEvaluatorInDictionaryHas<NodePath>>(Variant::OP_IN, Variant::NODE_PATH, Variant::DICTIONARY); + register_op<OperatorEvaluatorInDictionaryHas<::RID>>(Variant::OP_IN, Variant::RID, Variant::DICTIONARY); register_op<OperatorEvaluatorInDictionaryHasObject>(Variant::OP_IN, Variant::OBJECT, Variant::DICTIONARY); register_op<OperatorEvaluatorInDictionaryHas<Callable>>(Variant::OP_IN, Variant::CALLABLE, Variant::DICTIONARY); register_op<OperatorEvaluatorInDictionaryHas<Signal>>(Variant::OP_IN, Variant::SIGNAL, Variant::DICTIONARY); @@ -1021,6 +1022,7 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorInArrayFind<Color, Array>>(Variant::OP_IN, Variant::COLOR, Variant::ARRAY); register_op<OperatorEvaluatorInArrayFind<StringName, Array>>(Variant::OP_IN, Variant::STRING_NAME, Variant::ARRAY); register_op<OperatorEvaluatorInArrayFind<NodePath, Array>>(Variant::OP_IN, Variant::NODE_PATH, Variant::ARRAY); + register_op<OperatorEvaluatorInArrayFind<::RID, Array>>(Variant::OP_IN, Variant::RID, Variant::ARRAY); register_op<OperatorEvaluatorInArrayFindObject>(Variant::OP_IN, Variant::OBJECT, Variant::ARRAY); register_op<OperatorEvaluatorInArrayFind<Callable, Array>>(Variant::OP_IN, Variant::CALLABLE, Variant::ARRAY); register_op<OperatorEvaluatorInArrayFind<Signal, Array>>(Variant::OP_IN, Variant::SIGNAL, Variant::ARRAY); diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index f5f96456d3..f05b9cd83a 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -2245,7 +2245,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } else { List<Variant> keys; dict.get_key_list(&keys); - keys.sort(); + keys.sort_custom<StringLikeVariantOrder>(); if (keys.is_empty()) { // Avoid unnecessary line break. diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 384fe6c4a6..50932afbb6 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -1671,7 +1671,7 @@ static void register_utility_function(const String &p_name, const Vector<String> bfi.argnames = argnames; bfi.argcount = T::get_argument_count(); if (!bfi.is_vararg) { - ERR_FAIL_COND_MSG(argnames.size() != bfi.argcount, "wrong number of arguments binding utility function: " + name); + ERR_FAIL_COND_MSG(argnames.size() != bfi.argcount, vformat("Wrong number of arguments binding utility function: '%s'.", name)); } bfi.get_arg_type = T::get_argument_type; bfi.return_type = T::get_return_type(); |