diff options
Diffstat (limited to 'core')
121 files changed, 2714 insertions, 1339 deletions
diff --git a/core/config/engine.cpp b/core/config/engine.cpp index 7fdea7d1aa..17d3bdb744 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -74,6 +74,14 @@ int Engine::get_max_fps() const { return _max_fps; } +void Engine::set_audio_output_latency(int p_msec) { + _audio_output_latency = p_msec > 1 ? p_msec : 1; +} + +int Engine::get_audio_output_latency() const { + return _audio_output_latency; +} + uint64_t Engine::get_frames_drawn() { return frames_drawn; } @@ -239,6 +247,10 @@ bool Engine::is_validation_layers_enabled() const { return use_validation_layers; } +bool Engine::is_generate_spirv_debug_info_enabled() const { + return generate_spirv_debug_info; +} + void Engine::set_print_error_messages(bool p_enabled) { CoreGlobals::print_error_enabled = p_enabled; } diff --git a/core/config/engine.h b/core/config/engine.h index 5ea653ba6c..ff88fbc787 100644 --- a/core/config/engine.h +++ b/core/config/engine.h @@ -61,12 +61,14 @@ private: double physics_jitter_fix = 0.5; double _fps = 1; int _max_fps = 0; + int _audio_output_latency = 0; double _time_scale = 1.0; uint64_t _physics_frames = 0; int max_physics_steps_per_frame = 8; double _physics_interpolation_fraction = 0.0f; bool abort_on_gpu_errors = false; bool use_validation_layers = false; + bool generate_spirv_debug_info = false; int32_t gpu_idx = -1; uint64_t _process_frames = 0; @@ -98,6 +100,9 @@ public: virtual void set_max_fps(int p_fps); virtual int get_max_fps() const; + virtual void set_audio_output_latency(int p_msec); + virtual int get_audio_output_latency() const; + virtual double get_frames_per_second() const { return _fps; } uint64_t get_frames_drawn(); @@ -156,6 +161,7 @@ public: bool is_abort_on_gpu_errors_enabled() const; bool is_validation_layers_enabled() const; + bool is_generate_spirv_debug_info_enabled() const; int32_t get_gpu_index() const; Engine(); diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 715ed61770..9ffbac3553 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -146,30 +146,30 @@ const PackedStringArray ProjectSettings::_trim_to_supported_features(const Packe #endif // TOOLS_ENABLED String ProjectSettings::localize_path(const String &p_path) const { - if (resource_path.is_empty() || (p_path.is_absolute_path() && !p_path.begins_with(resource_path))) { - return p_path.simplify_path(); + String path = p_path.simplify_path(); + + if (resource_path.is_empty() || (path.is_absolute_path() && !path.begins_with(resource_path))) { + return path; } // Check if we have a special path (like res://) or a protocol identifier. - int p = p_path.find("://"); + int p = path.find("://"); bool found = false; if (p > 0) { found = true; for (int i = 0; i < p; i++) { - if (!is_ascii_alphanumeric_char(p_path[i])) { + if (!is_ascii_alphanumeric_char(path[i])) { found = false; break; } } } if (found) { - return p_path.simplify_path(); + return path; } Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - String path = p_path.replace("\\", "/").simplify_path(); - if (dir->change_dir(path) == OK) { String cwd = dir->get_current_dir(); cwd = cwd.replace("\\", "/"); @@ -187,7 +187,7 @@ String ProjectSettings::localize_path(const String &p_path) const { cwd = cwd.path_join(""); if (!cwd.begins_with(res_path)) { - return p_path; + return path; } return cwd.replace_first(res_path, "res://"); @@ -249,6 +249,11 @@ bool ProjectSettings::get_ignore_value_in_docs(const String &p_name) const { #endif } +void ProjectSettings::add_hidden_prefix(const String &p_prefix) { + ERR_FAIL_COND_MSG(hidden_prefixes.find(p_prefix) > -1, vformat("Hidden prefix '%s' already exists.", p_prefix)); + hidden_prefixes.push_back(p_prefix); +} + String ProjectSettings::globalize_path(const String &p_path) const { if (p_path.begins_with("res://")) { if (!resource_path.is_empty()) { @@ -283,6 +288,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { for (int i = 0; i < custom_feature_array.size(); i++) { custom_features.insert(custom_feature_array[i]); } + _queue_changed(); return true; } @@ -324,6 +330,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { } } + _queue_changed(); return true; } @@ -386,7 +393,18 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const { vc.name = E.key; vc.order = v->order; vc.type = v->variant.get_type(); - if (v->internal || vc.name.begins_with("input/") || vc.name.begins_with("importer_defaults/") || vc.name.begins_with("import/") || vc.name.begins_with("autoload/") || vc.name.begins_with("editor_plugins/") || vc.name.begins_with("shader_globals/")) { + + bool internal = v->internal; + if (!internal) { + for (const String &F : hidden_prefixes) { + if (vc.name.begins_with(F)) { + internal = true; + break; + } + } + } + + if (internal) { vc.flags = PROPERTY_USAGE_STORAGE; } else { vc.flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE; @@ -424,6 +442,22 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const { } } +void ProjectSettings::_queue_changed() { + if (is_changed || !MessageQueue::get_singleton() || MessageQueue::get_singleton()->get_max_buffer_usage() == 0) { + return; + } + is_changed = true; + callable_mp(this, &ProjectSettings::_emit_changed).call_deferred(); +} + +void ProjectSettings::_emit_changed() { + if (!is_changed) { + return; + } + is_changed = false; + emit_signal("settings_changed"); +} + bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset) { if (PackedData::get_singleton()->is_disabled()) { return false; @@ -1225,6 +1259,8 @@ void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("load_resource_pack", "pack", "replace_files", "offset"), &ProjectSettings::_load_resource_pack, DEFVAL(true), DEFVAL(0)); ClassDB::bind_method(D_METHOD("save_custom", "file"), &ProjectSettings::_save_custom_bnd); + + ADD_SIGNAL(MethodInfo("settings_changed")); } void ProjectSettings::_add_builtin_input_map() { @@ -1260,6 +1296,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_BASIC("application/config/name", ""); GLOBAL_DEF_BASIC(PropertyInfo(Variant::DICTIONARY, "application/config/name_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/config/description", PROPERTY_HINT_MULTILINE_TEXT), ""); + GLOBAL_DEF_BASIC("application/config/version", ""); GLOBAL_DEF_INTERNAL(PropertyInfo(Variant::STRING, "application/config/tags"), PackedStringArray()); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "*.tscn,*.scn,*.res"), ""); GLOBAL_DEF("application/run/disable_stdout", false); @@ -1328,6 +1365,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/mode", PROPERTY_HINT_ENUM, "disabled,canvas_items,viewport"), "disabled"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/aspect", PROPERTY_HINT_ENUM, "ignore,keep,keep_width,keep_height,expand"), "keep"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "display/window/stretch/scale", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), 1.0); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/scale_mode", PROPERTY_HINT_ENUM, "fractional,integer"), "fractional"); GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1"), 16384); @@ -1360,11 +1398,13 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/textures/canvas_textures/default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Linear Mipmap,Nearest Mipmap"), 1); GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/textures/canvas_textures/default_texture_repeat", PROPERTY_HINT_ENUM, "Disable,Enable,Mirror"), 0); - // These properties will not show up in the dialog nor in the documentation. If you want to exclude whole groups, see _get_property_list() method. + // These properties will not show up in the dialog. If you want to exclude whole groups, use add_hidden_prefix(). GLOBAL_DEF_INTERNAL("application/config/features", PackedStringArray()); GLOBAL_DEF_INTERNAL("internationalization/locale/translation_remaps", PackedStringArray()); GLOBAL_DEF_INTERNAL("internationalization/locale/translations", PackedStringArray()); GLOBAL_DEF_INTERNAL("internationalization/locale/translations_pot_files", PackedStringArray()); + + ProjectSettings::get_singleton()->add_hidden_prefix("input/"); } ProjectSettings::~ProjectSettings() { diff --git a/core/config/project_settings.h b/core/config/project_settings.h index b89e6694b0..302df7e8d0 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -43,6 +43,9 @@ class TypedArray; class ProjectSettings : public Object { GDCLASS(ProjectSettings, Object); _THREAD_SAFE_CLASS_ + friend class TestProjectSettingsInternalsAccessor; + + bool is_changed = false; public: typedef HashMap<String, Variant> CustomMap; @@ -101,6 +104,7 @@ protected: HashSet<String> custom_features; HashMap<StringName, LocalVector<Pair<StringName, StringName>>> feature_overrides; + LocalVector<String> hidden_prefixes; HashMap<StringName, AutoloadInfo> autoloads; Array global_class_list; @@ -114,6 +118,9 @@ protected: bool _property_can_revert(const StringName &p_name) const; bool _property_get_revert(const StringName &p_name, Variant &r_property) const; + void _queue_changed(); + void _emit_changed(); + static ProjectSettings *singleton; Error _load_settings_text(const String &p_path); @@ -162,6 +169,7 @@ public: void set_restart_if_changed(const String &p_name, bool p_restart); void set_ignore_value_in_docs(const String &p_name, bool p_ignore); bool get_ignore_value_in_docs(const String &p_name) const; + void add_hidden_prefix(const String &p_prefix); String get_project_data_dir_name() const; String get_project_data_path() const; diff --git a/core/core_bind.cpp b/core/core_bind.cpp index a73b198be2..05fe393a2f 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -929,6 +929,17 @@ Geometry3D *Geometry3D::get_singleton() { return singleton; } +Vector<Vector3> Geometry3D::compute_convex_mesh_points(const TypedArray<Plane> &p_planes) { + Vector<Plane> planes_vec; + int size = p_planes.size(); + planes_vec.resize(size); + for (int i = 0; i < size; ++i) { + planes_vec.set(i, p_planes[i]); + } + Variant ret = ::Geometry3D::compute_convex_mesh_points(planes_vec.ptr(), size); + return ret; +} + TypedArray<Plane> Geometry3D::build_box_planes(const Vector3 &p_extents) { Variant ret = ::Geometry3D::build_box_planes(p_extents); return ret; @@ -961,6 +972,11 @@ Vector3 Geometry3D::get_closest_point_to_segment_uncapped(const Vector3 &p_point return ::Geometry3D::get_closest_point_to_segment_uncapped(p_point, s); } +Vector3 Geometry3D::get_triangle_barycentric_coords(const Vector3 &p_point, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) { + Vector3 res = ::Geometry3D::triangle_get_barycentric_coords(p_v0, p_v1, p_v2, p_point); + return res; +} + Variant Geometry3D::ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) { Vector3 res; if (::Geometry3D::ray_intersects_triangle(p_from, p_dir, p_v0, p_v1, p_v2, &res)) { @@ -1024,6 +1040,7 @@ Vector<Vector3> Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const } void Geometry3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("compute_convex_mesh_points", "planes"), &Geometry3D::compute_convex_mesh_points); ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &Geometry3D::build_box_planes); ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &Geometry3D::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z)); ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &Geometry3D::build_capsule_planes, DEFVAL(Vector3::AXIS_Z)); @@ -1034,6 +1051,8 @@ void Geometry3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &Geometry3D::get_closest_point_to_segment_uncapped); + ClassDB::bind_method(D_METHOD("get_triangle_barycentric_coords", "point", "a", "b", "c"), &Geometry3D::get_triangle_barycentric_coords); + ClassDB::bind_method(D_METHOD("ray_intersects_triangle", "from", "dir", "a", "b", "c"), &Geometry3D::ray_intersects_triangle); ClassDB::bind_method(D_METHOD("segment_intersects_triangle", "from", "to", "a", "b", "c"), &Geometry3D::segment_intersects_triangle); ClassDB::bind_method(D_METHOD("segment_intersects_sphere", "from", "to", "sphere_position", "sphere_radius"), &Geometry3D::segment_intersects_sphere); diff --git a/core/core_bind.h b/core/core_bind.h index dc0b2a1cf5..5f51b64eb7 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -320,12 +320,14 @@ protected: public: static Geometry3D *get_singleton(); + Vector<Vector3> compute_convex_mesh_points(const TypedArray<Plane> &p_planes); TypedArray<Plane> build_box_planes(const Vector3 &p_extents); TypedArray<Plane> build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z); TypedArray<Plane> build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z); Vector<Vector3> get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2); Vector3 get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b); Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b); + Vector3 get_triangle_barycentric_coords(const Vector3 &p_point, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); Variant ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); Variant segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 2332bc235b..33b3271495 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -420,6 +420,10 @@ void register_global_constants() { BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHD); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHE); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHF); + BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, GLOBE); + BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KEYBOARD); + BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, JIS_EISU); + BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, JIS_KANA); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, UNKNOWN); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SPACE); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, EXCLAM); @@ -492,10 +496,6 @@ void register_global_constants() { BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ASCIITILDE); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, YEN); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SECTION); - BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, GLOBE); - BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KEYBOARD); - BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, JIS_EISU); - BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, JIS_KANA); BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(KeyModifierMask, KEY_CODE_MASK, CODE_MASK); BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(KeyModifierMask, KEY_MODIFIER_MASK, MODIFIER_MASK); diff --git a/core/crypto/crypto_core.cpp b/core/crypto/crypto_core.cpp index 3ca2ec282f..17b34c08e2 100644 --- a/core/crypto/crypto_core.cpp +++ b/core/crypto/crypto_core.cpp @@ -73,7 +73,7 @@ Error CryptoCore::RandomGenerator::init() { } Error CryptoCore::RandomGenerator::get_random_bytes(uint8_t *r_buffer, size_t p_bytes) { - ERR_FAIL_COND_V(!ctx, ERR_UNCONFIGURED); + 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)); return OK; diff --git a/core/crypto/hashing_context.cpp b/core/crypto/hashing_context.cpp index 35f491cd0c..157a0c091b 100644 --- a/core/crypto/hashing_context.cpp +++ b/core/crypto/hashing_context.cpp @@ -35,7 +35,7 @@ Error HashingContext::start(HashType p_type) { ERR_FAIL_COND_V(ctx != nullptr, ERR_ALREADY_IN_USE); _create_ctx(p_type); - ERR_FAIL_COND_V(ctx == nullptr, ERR_UNAVAILABLE); + ERR_FAIL_NULL_V(ctx, ERR_UNAVAILABLE); switch (type) { case HASH_MD5: return ((CryptoCore::MD5Context *)ctx)->start(); @@ -48,7 +48,7 @@ Error HashingContext::start(HashType p_type) { } Error HashingContext::update(PackedByteArray p_chunk) { - ERR_FAIL_COND_V(ctx == nullptr, ERR_UNCONFIGURED); + ERR_FAIL_NULL_V(ctx, ERR_UNCONFIGURED); size_t len = p_chunk.size(); ERR_FAIL_COND_V(len == 0, FAILED); const uint8_t *r = p_chunk.ptr(); @@ -64,7 +64,7 @@ Error HashingContext::update(PackedByteArray p_chunk) { } PackedByteArray HashingContext::finish() { - ERR_FAIL_COND_V(ctx == nullptr, PackedByteArray()); + ERR_FAIL_NULL_V(ctx, PackedByteArray()); PackedByteArray out; Error err = FAILED; switch (type) { diff --git a/core/debugger/debugger_marshalls.cpp b/core/debugger/debugger_marshalls.cpp index 591b44869f..3e6b7501c7 100644 --- a/core/debugger/debugger_marshalls.cpp +++ b/core/debugger/debugger_marshalls.cpp @@ -67,6 +67,7 @@ Array DebuggerMarshalls::ScriptStackVariable::serialize(int max_size) { Array arr; arr.push_back(name); arr.push_back(type); + arr.push_back(value.get_type()); Variant var = value; if (value.get_type() == Variant::OBJECT && value.get_validated_object() == nullptr) { @@ -74,7 +75,7 @@ Array DebuggerMarshalls::ScriptStackVariable::serialize(int max_size) { } int len = 0; - Error err = encode_variant(var, nullptr, len, true); + Error err = encode_variant(var, nullptr, len, false); if (err != OK) { ERR_PRINT("Failed to encode variant."); } @@ -88,11 +89,12 @@ Array DebuggerMarshalls::ScriptStackVariable::serialize(int max_size) { } bool DebuggerMarshalls::ScriptStackVariable::deserialize(const Array &p_arr) { - CHECK_SIZE(p_arr, 3, "ScriptStackVariable"); + CHECK_SIZE(p_arr, 4, "ScriptStackVariable"); name = p_arr[0]; type = p_arr[1]; - value = p_arr[2]; - CHECK_END(p_arr, 3, "ScriptStackVariable"); + var_type = p_arr[2]; + value = p_arr[3]; + CHECK_END(p_arr, 4, "ScriptStackVariable"); return true; } diff --git a/core/debugger/debugger_marshalls.h b/core/debugger/debugger_marshalls.h index 8ba93c3092..1b81623688 100644 --- a/core/debugger/debugger_marshalls.h +++ b/core/debugger/debugger_marshalls.h @@ -38,6 +38,7 @@ struct DebuggerMarshalls { String name; Variant value; int type = -1; + int var_type = -1; Array serialize(int max_size = 1 << 20); // 1 MiB default. bool deserialize(const Array &p_arr); diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp index 6c9293a2cf..32dc060aa2 100644 --- a/core/debugger/engine_debugger.cpp +++ b/core/debugger/engine_debugger.cpp @@ -111,14 +111,6 @@ Error EngineDebugger::capture_parse(const StringName &p_name, const String &p_ms return cap.capture(cap.data, p_msg, p_args, r_captured); } -void EngineDebugger::line_poll() { - // The purpose of this is just processing events every now and then when the script might get too busy otherwise bugs like infinite loops can't be caught - if (poll_every % 2048 == 0) { - poll_events(false); - } - poll_every++; -} - void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, double p_physics_frame_time) { frame_time = USEC_TO_SEC(p_frame_ticks); process_time = USEC_TO_SEC(p_process_ticks); diff --git a/core/debugger/engine_debugger.h b/core/debugger/engine_debugger.h index 1bae71e37a..88d5490794 100644 --- a/core/debugger/engine_debugger.h +++ b/core/debugger/engine_debugger.h @@ -126,7 +126,13 @@ public: void profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts = Array()); Error capture_parse(const StringName &p_name, const String &p_msg, const Array &p_args, bool &r_captured); - void line_poll(); + void line_poll() { + // The purpose of this is just processing events every now and then when the script might get too busy otherwise bugs like infinite loops can't be caught. + if (unlikely(poll_every % 2048) == 0) { + poll_events(false); + } + poll_every++; + } virtual void poll_events(bool p_is_idle) {} virtual void send_message(const String &p_msg, const Array &p_data) = 0; diff --git a/core/debugger/engine_profiler.h b/core/debugger/engine_profiler.h index f74481c84b..d3d0021e67 100644 --- a/core/debugger/engine_profiler.h +++ b/core/debugger/engine_profiler.h @@ -31,10 +31,8 @@ #ifndef ENGINE_PROFILER_H #define ENGINE_PROFILER_H -#include "core/object/ref_counted.h" - #include "core/object/gdvirtual.gen.inc" -#include "core/object/script_language.h" +#include "core/object/ref_counted.h" class EngineProfiler : public RefCounted { GDCLASS(EngineProfiler, RefCounted); diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index b7471d7c82..a817ea871d 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -94,6 +94,7 @@ public: Error RemoteDebugger::_put_msg(String p_message, Array p_data) { Array msg; msg.push_back(p_message); + msg.push_back(Thread::get_caller_id()); msg.push_back(p_data); Error err = peer->put_message(msg); if (err != OK) { @@ -185,9 +186,9 @@ RemoteDebugger::ErrorMessage RemoteDebugger::_create_overflow_error(const String } void RemoteDebugger::flush_output() { + MutexLock lock(mutex); flush_thread = Thread::get_caller_id(); flushing = true; - MutexLock lock(mutex); if (!is_peer_connected()) { return; } @@ -348,18 +349,65 @@ Error RemoteDebugger::_try_capture(const String &p_msg, const Array &p_data, boo return capture_parse(cap, msg, p_data, r_captured); } +void RemoteDebugger::_poll_messages() { + MutexLock mutex_lock(mutex); + + peer->poll(); + while (peer->has_message()) { + Array cmd = peer->get_message(); + ERR_CONTINUE(cmd.size() != 3); + ERR_CONTINUE(cmd[0].get_type() != Variant::STRING); + ERR_CONTINUE(cmd[1].get_type() != Variant::INT); + ERR_CONTINUE(cmd[2].get_type() != Variant::ARRAY); + + Thread::ID thread = cmd[1]; + + if (!messages.has(thread)) { + continue; // This thread is not around to receive the messages + } + + Message msg; + msg.message = cmd[0]; + msg.data = cmd[2]; + messages[thread].push_back(msg); + } +} + +bool RemoteDebugger::_has_messages() { + MutexLock mutex_lock(mutex); + return messages.has(Thread::get_caller_id()) && !messages[Thread::get_caller_id()].is_empty(); +} + +Array RemoteDebugger::_get_message() { + MutexLock mutex_lock(mutex); + ERR_FAIL_COND_V(!messages.has(Thread::get_caller_id()), Array()); + List<Message> &message_list = messages[Thread::get_caller_id()]; + ERR_FAIL_COND_V(message_list.is_empty(), Array()); + + Array msg; + msg.resize(2); + msg[0] = message_list.front()->get().message; + msg[1] = message_list.front()->get().data; + message_list.pop_front(); + return msg; +} + void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { //this function is called when there is a debugger break (bug on script) //or when execution is paused from editor - if (script_debugger->is_skipping_breakpoints() && !p_is_error_breakpoint) { - return; - } + { + MutexLock lock(mutex); + // Tests that require mutex. + if (script_debugger->is_skipping_breakpoints() && !p_is_error_breakpoint) { + return; + } - ERR_FAIL_COND_MSG(!is_peer_connected(), "Script Debugger failed to connect, but being used anyway."); + ERR_FAIL_COND_MSG(!is_peer_connected(), "Script Debugger failed to connect, but being used anyway."); - if (!peer->can_block()) { - return; // Peer does not support blocking IO. We could at least send the error though. + if (!peer->can_block()) { + return; // Peer does not support blocking IO. We could at least send the error though. + } } ScriptLanguage *script_lang = script_debugger->get_break_language(); @@ -367,24 +415,35 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { Array msg; msg.push_back(p_can_continue); msg.push_back(error_str); - ERR_FAIL_COND(!script_lang); + ERR_FAIL_NULL(script_lang); msg.push_back(script_lang->debug_get_stack_level_count() > 0); + msg.push_back(Thread::get_caller_id() == Thread::get_main_id() ? String(RTR("Main Thread")) : itos(Thread::get_caller_id())); if (allow_focus_steal_fn) { allow_focus_steal_fn(); } send_message("debug_enter", msg); - Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode(); - if (mouse_mode != Input::MOUSE_MODE_VISIBLE) { - Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + Input::MouseMode mouse_mode = Input::MOUSE_MODE_VISIBLE; + + if (Thread::get_caller_id() == Thread::get_main_id()) { + mouse_mode = Input::get_singleton()->get_mouse_mode(); + if (mouse_mode != Input::MOUSE_MODE_VISIBLE) { + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + } + } else { + MutexLock mutex_lock(mutex); + messages.insert(Thread::get_caller_id(), List<Message>()); } + mutex.lock(); while (is_peer_connected()) { + mutex.unlock(); flush_output(); - peer->poll(); - if (peer->has_message()) { - Array cmd = peer->get_message(); + _poll_messages(); + + if (_has_messages()) { + Array cmd = _get_message(); ERR_CONTINUE(cmd.size() != 2); ERR_CONTINUE(cmd[0].get_type() != Variant::STRING); @@ -426,7 +485,7 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } else if (command == "get_stack_frame_vars") { ERR_FAIL_COND(data.size() != 1); - ERR_FAIL_COND(!script_lang); + ERR_FAIL_NULL(script_lang); int lv = data[0]; List<String> members; @@ -479,14 +538,22 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } } else { OS::get_singleton()->delay_usec(10000); - OS::get_singleton()->process_and_drop_events(); + if (Thread::get_caller_id() == Thread::get_main_id()) { + // If this is a busy loop on the main thread, events still need to be processed. + OS::get_singleton()->process_and_drop_events(); + } } } send_message("debug_exit", Array()); - if (mouse_mode != Input::MOUSE_MODE_VISIBLE) { - Input::get_singleton()->set_mouse_mode(mouse_mode); + if (Thread::get_caller_id() == Thread::get_main_id()) { + if (mouse_mode != Input::MOUSE_MODE_VISIBLE) { + Input::get_singleton()->set_mouse_mode(mouse_mode); + } + } else { + MutexLock mutex_lock(mutex); + messages.erase(Thread::get_caller_id()); } } @@ -496,9 +563,11 @@ void RemoteDebugger::poll_events(bool p_is_idle) { } flush_output(); - peer->poll(); - while (peer->has_message()) { - Array arr = peer->get_message(); + + _poll_messages(); + + while (_has_messages()) { + Array arr = _get_message(); ERR_CONTINUE(arr.size() != 2); ERR_CONTINUE(arr[0].get_type() != Variant::STRING); @@ -604,6 +673,8 @@ RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) { eh.errfunc = _err_handler; eh.userdata = this; add_error_handler(&eh); + + messages.insert(Thread::get_main_id(), List<Message>()); } RemoteDebugger::~RemoteDebugger() { diff --git a/core/debugger/remote_debugger.h b/core/debugger/remote_debugger.h index 24283b0ed6..7c399178c6 100644 --- a/core/debugger/remote_debugger.h +++ b/core/debugger/remote_debugger.h @@ -80,6 +80,17 @@ private: bool flushing = false; Thread::ID flush_thread = 0; + struct Message { + String message; + Array data; + }; + + HashMap<Thread::ID, List<Message>> messages; + + void _poll_messages(); + bool _has_messages(); + Array _get_message(); + PrintHandlerList phl; static void _print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich); ErrorHandlerList eh; diff --git a/core/debugger/script_debugger.cpp b/core/debugger/script_debugger.cpp index 32725b76c1..e7d8654a0b 100644 --- a/core/debugger/script_debugger.cpp +++ b/core/debugger/script_debugger.cpp @@ -32,22 +32,19 @@ #include "core/debugger/engine_debugger.h" +thread_local int ScriptDebugger::lines_left = -1; +thread_local int ScriptDebugger::depth = -1; +thread_local ScriptLanguage *ScriptDebugger::break_lang = nullptr; +thread_local Vector<ScriptDebugger::StackInfo> ScriptDebugger::error_stack_info; + void ScriptDebugger::set_lines_left(int p_left) { lines_left = p_left; } -int ScriptDebugger::get_lines_left() const { - return lines_left; -} - void ScriptDebugger::set_depth(int p_depth) { depth = p_depth; } -int ScriptDebugger::get_depth() const { - return depth; -} - void ScriptDebugger::insert_breakpoint(int p_line, const StringName &p_source) { if (!breakpoints.has(p_line)) { breakpoints[p_line] = HashSet<StringName>(); @@ -66,13 +63,6 @@ void ScriptDebugger::remove_breakpoint(int p_line, const StringName &p_source) { } } -bool ScriptDebugger::is_breakpoint(int p_line, const StringName &p_source) const { - if (!breakpoints.has(p_line)) { - return false; - } - return breakpoints[p_line].has(p_source); -} - String ScriptDebugger::breakpoint_find_source(const String &p_source) const { return p_source; } @@ -100,7 +90,7 @@ void ScriptDebugger::send_error(const String &p_func, const String &p_file, int // Store stack info, this is ugly, but allows us to separate EngineDebugger and ScriptDebugger. There might be a better way. error_stack_info.append_array(p_stack_info); EngineDebugger::get_singleton()->send_error(p_func, p_file, p_line, p_err, p_descr, p_editor_notify, p_type); - error_stack_info.clear(); + error_stack_info.clear(); // Clear because this is thread local } Vector<ScriptLanguage::StackInfo> ScriptDebugger::get_error_stack_info() const { diff --git a/core/debugger/script_debugger.h b/core/debugger/script_debugger.h index edce089179..ee037b91fa 100644 --- a/core/debugger/script_debugger.h +++ b/core/debugger/script_debugger.h @@ -40,21 +40,25 @@ class ScriptDebugger { typedef ScriptLanguage::StackInfo StackInfo; - int lines_left = -1; - int depth = -1; bool skip_breakpoints = false; HashMap<int, HashSet<StringName>> breakpoints; - ScriptLanguage *break_lang = nullptr; - Vector<StackInfo> error_stack_info; + static thread_local int lines_left; + static thread_local int depth; + static thread_local ScriptLanguage *break_lang; + static thread_local Vector<StackInfo> error_stack_info; public: void set_lines_left(int p_left); - int get_lines_left() const; + _ALWAYS_INLINE_ int get_lines_left() const { + return lines_left; + } void set_depth(int p_depth); - int get_depth() const; + _ALWAYS_INLINE_ int get_depth() const { + return depth; + } String breakpoint_find_source(const String &p_source) const; void set_break_language(ScriptLanguage *p_lang) { break_lang = p_lang; } @@ -63,7 +67,12 @@ public: bool is_skipping_breakpoints(); void insert_breakpoint(int p_line, const StringName &p_source); void remove_breakpoint(int p_line, const StringName &p_source); - bool is_breakpoint(int p_line, const StringName &p_source) const; + _ALWAYS_INLINE_ bool is_breakpoint(int p_line, const StringName &p_source) const { + if (likely(!breakpoints.has(p_line))) { + return false; + } + return breakpoints[p_line].has(p_source); + } void clear_breakpoints(); const HashMap<int, HashSet<StringName>> &get_breakpoints() const { return breakpoints; } diff --git a/core/doc_data.h b/core/doc_data.h index 0fe7414b98..b8c92a4b67 100644 --- a/core/doc_data.h +++ b/core/doc_data.h @@ -532,6 +532,42 @@ public: } }; + struct EnumDoc { + String description; + bool is_deprecated = false; + bool is_experimental = false; + static EnumDoc from_dict(const Dictionary &p_dict) { + EnumDoc doc; + + if (p_dict.has("description")) { + doc.description = p_dict["description"]; + } + + if (p_dict.has("is_deprecated")) { + doc.is_deprecated = p_dict["is_deprecated"]; + } + + if (p_dict.has("is_experimental")) { + doc.is_experimental = p_dict["is_experimental"]; + } + + return doc; + } + static Dictionary to_dict(const EnumDoc &p_doc) { + Dictionary dict; + + if (!p_doc.description.is_empty()) { + dict["description"] = p_doc.description; + } + + dict["is_deprecated"] = p_doc.is_deprecated; + + dict["is_experimental"] = p_doc.is_experimental; + + return dict; + } + }; + struct ClassDoc { String name; String inherits; @@ -543,7 +579,7 @@ public: Vector<MethodDoc> operators; Vector<MethodDoc> signals; Vector<ConstantDoc> constants; - HashMap<String, String> enums; + HashMap<String, EnumDoc> enums; Vector<PropertyDoc> properties; Vector<MethodDoc> annotations; Vector<ThemeItemDoc> theme_properties; @@ -626,7 +662,7 @@ public: enums = p_dict["enums"]; } for (int i = 0; i < enums.size(); i++) { - doc.enums[enums.get_key_at_index(i)] = enums.get_value_at_index(i); + doc.enums[enums.get_key_at_index(i)] = EnumDoc::from_dict(enums.get_value_at_index(i)); } Array properties; @@ -740,8 +776,8 @@ public: if (!p_doc.enums.is_empty()) { Dictionary enums; - for (const KeyValue<String, String> &E : p_doc.enums) { - enums[E.key] = E.value; + for (const KeyValue<String, EnumDoc> &E : p_doc.enums) { + enums[E.key] = EnumDoc::to_dict(E.value); } dict["enums"] = enums; } diff --git a/core/error/error_macros.h b/core/error/error_macros.h index 65804b7796..c8182975d5 100644 --- a/core/error/error_macros.h +++ b/core/error/error_macros.h @@ -786,8 +786,19 @@ void _err_flush_stdout(); ((void)0) /** - * This should be a 'free' assert for program flow and should not be needed in any releases, - * only used in dev builds. + * Note: IN MOST CASES YOU SHOULD NOT USE THIS MACRO. + * Do not use unless you understand the trade-offs. + * + * DEV macros will be compiled out in releases, they are wrapped in DEV_ENABLED. + * + * Prefer WARNINGS / ERR_FAIL macros (which fail without crashing) - ERR_FAIL should be used in most cases. + * Then CRASH_NOW_MSG macros (on rare occasions where error cannot be recovered). + * + * DEV_ASSERT should generally only be used when both of the following conditions are met: + * 1) Bottleneck code where a check in release would be too expensive. + * 2) Situations where the check would fail obviously and straight away during the maintenance of the code + * (i.e. strict conditions that should be true no matter what) + * and that can't fail for other contributors once the code is finished and merged. */ #ifdef DEV_ENABLED #define DEV_ASSERT(m_cond) \ diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index c67867f65d..97ead0b425 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -750,6 +750,9 @@ Dictionary GDExtensionAPIDump::generate_extension_api() { class_list.sort_custom<StringName::AlphCompare>(); for (const StringName &class_name : class_list) { + if (!ClassDB::is_class_exposed(class_name)) { + continue; + } Dictionary d; d["name"] = String(class_name); d["is_refcounted"] = ClassDB::is_parent_class(class_name, "RefCounted"); diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 73526fae3e..96ad1570cd 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -281,7 +281,42 @@ public: } }; +#ifndef DISABLE_DEPRECATED void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs) { + const GDExtensionClassCreationInfo2 class_info2 = { + p_extension_funcs->is_virtual, // GDExtensionBool is_virtual; + p_extension_funcs->is_abstract, // GDExtensionBool is_abstract; + true, // GDExtensionBool is_exposed; + p_extension_funcs->set_func, // GDExtensionClassSet set_func; + p_extension_funcs->get_func, // GDExtensionClassGet get_func; + p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func; + p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func; + p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func; + p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func; + nullptr, // GDExtensionClassValidateProperty validate_property_func; + nullptr, // GDExtensionClassNotification2 notification_func; + p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func; + p_extension_funcs->reference_func, // GDExtensionClassReference reference_func; + p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func; + p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */ + p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ + p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; + p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; + p_extension_funcs->class_userdata, // void *class_userdata; + }; + + const ClassCreationDeprecatedInfo legacy = { + p_extension_funcs->notification_func, + }; + _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info2, &legacy); +} +#endif // DISABLE_DEPRECATED + +void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs) { + _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, p_extension_funcs); +} + +void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) { GDExtension *self = reinterpret_cast<GDExtension *>(p_library); StringName class_name = *reinterpret_cast<const StringName *>(p_class_name); @@ -319,13 +354,20 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library extension->gdextension.editor_class = self->level_initialized == INITIALIZATION_LEVEL_EDITOR; extension->gdextension.is_virtual = p_extension_funcs->is_virtual; extension->gdextension.is_abstract = p_extension_funcs->is_abstract; + extension->gdextension.is_exposed = p_extension_funcs->is_exposed; extension->gdextension.set = p_extension_funcs->set_func; extension->gdextension.get = p_extension_funcs->get_func; extension->gdextension.get_property_list = p_extension_funcs->get_property_list_func; extension->gdextension.free_property_list = p_extension_funcs->free_property_list_func; extension->gdextension.property_can_revert = p_extension_funcs->property_can_revert_func; extension->gdextension.property_get_revert = p_extension_funcs->property_get_revert_func; - extension->gdextension.notification = p_extension_funcs->notification_func; + extension->gdextension.validate_property = p_extension_funcs->validate_property_func; +#ifndef DISABLE_DEPRECATED + if (p_deprecated_funcs) { + extension->gdextension.notification = p_deprecated_funcs->notification_func; + } +#endif // DISABLE_DEPRECATED + extension->gdextension.notification2 = p_extension_funcs->notification_func; extension->gdextension.to_string = p_extension_funcs->to_string_func; extension->gdextension.reference = p_extension_funcs->reference_func; extension->gdextension.unreference = p_extension_funcs->unreference_func; @@ -337,6 +379,7 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library ClassDB::register_extension_class(&extension->gdextension); } + void GDExtension::_register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info) { GDExtension *self = reinterpret_cast<GDExtension *>(p_library); @@ -363,6 +406,10 @@ void GDExtension::_register_extension_class_integer_constant(GDExtensionClassLib } void GDExtension::_register_extension_class_property(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter) { + _register_extension_class_property_indexed(p_library, p_class_name, p_info, p_setter, p_getter, -1); +} + +void GDExtension::_register_extension_class_property_indexed(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter, GDExtensionInt p_index) { GDExtension *self = reinterpret_cast<GDExtension *>(p_library); StringName class_name = *reinterpret_cast<const StringName *>(p_class_name); @@ -371,10 +418,9 @@ void GDExtension::_register_extension_class_property(GDExtensionClassLibraryPtr 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 + "'."); - //Extension *extension = &self->extension_classes[class_name]; PropertyInfo pinfo(*p_info); - ClassDB::add_property(class_name, pinfo, setter, getter); + ClassDB::add_property(class_name, pinfo, setter, getter, p_index); } void GDExtension::_register_extension_class_property_group(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringPtr p_group_name, GDExtensionConstStringPtr p_prefix) { @@ -445,7 +491,7 @@ void GDExtension::register_interface_function(StringName p_function_name, GDExte GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(StringName p_function_name) { GDExtensionInterfaceFunctionPtr *function = gdextension_interface_functions.getptr(p_function_name); - ERR_FAIL_COND_V_MSG(function == nullptr, nullptr, "Attempt to get non-existent interface function: " + p_function_name); + ERR_FAIL_NULL_V_MSG(function, nullptr, "Attempt to get non-existent interface function: " + String(p_function_name) + "."); return *function; } @@ -479,9 +525,16 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb } void GDExtension::close_library() { - ERR_FAIL_COND(library == nullptr); + ERR_FAIL_NULL(library); OS::get_singleton()->close_dynamic_library(library); +#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED) + // Delete temporary copy of library if it exists. + if (!temp_lib_path.is_empty() && Engine::get_singleton()->is_editor_hint()) { + DirAccess::remove_absolute(temp_lib_path); + } +#endif + library = nullptr; } @@ -490,12 +543,12 @@ bool GDExtension::is_library_open() const { } GDExtension::InitializationLevel GDExtension::get_minimum_library_initialization_level() const { - ERR_FAIL_COND_V(library == nullptr, INITIALIZATION_LEVEL_CORE); + ERR_FAIL_NULL_V(library, INITIALIZATION_LEVEL_CORE); return InitializationLevel(initialization.minimum_initialization_level); } void GDExtension::initialize_library(InitializationLevel p_level) { - ERR_FAIL_COND(library == nullptr); + ERR_FAIL_NULL(library); ERR_FAIL_COND_MSG(p_level <= int32_t(level_initialized), vformat("Level '%d' must be higher than the current level '%d'", p_level, level_initialized)); level_initialized = int32_t(p_level); @@ -505,7 +558,7 @@ void GDExtension::initialize_library(InitializationLevel p_level) { initialization.initialize(initialization.userdata, GDExtensionInitializationLevel(p_level)); } void GDExtension::deinitialize_library(InitializationLevel p_level) { - ERR_FAIL_COND(library == nullptr); + ERR_FAIL_NULL(library); ERR_FAIL_COND(p_level > int32_t(level_initialized)); level_initialized = int32_t(p_level) - 1; @@ -538,10 +591,14 @@ GDExtension::~GDExtension() { void GDExtension::initialize_gdextensions() { gdextension_setup_interface(); +#ifndef DISABLE_DEPRECATED register_interface_function("classdb_register_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class); +#endif // DISABLE_DEPRECATED + register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2); register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method); register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant); register_interface_function("classdb_register_extension_class_property", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property); + register_interface_function("classdb_register_extension_class_property_indexed", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_indexed); register_interface_function("classdb_register_extension_class_property_group", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_group); register_interface_function("classdb_register_extension_class_property_subgroup", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_subgroup); register_interface_function("classdb_register_extension_class_signal", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_signal); @@ -603,12 +660,13 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String } bool compatible = true; - if (VERSION_MAJOR < compatibility_minimum[0]) { - compatible = false; - } else if (VERSION_MINOR < compatibility_minimum[1]) { - compatible = false; - } else if (VERSION_PATCH < compatibility_minimum[2]) { - compatible = false; + // Check version lexicographically. + if (VERSION_MAJOR != compatibility_minimum[0]) { + compatible = VERSION_MAJOR > compatibility_minimum[0]; + } else if (VERSION_MINOR != compatibility_minimum[1]) { + compatible = VERSION_MINOR > compatibility_minimum[1]; + } else { + compatible = VERSION_PATCH >= compatibility_minimum[2]; } if (!compatible) { if (r_error) { @@ -636,6 +694,40 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String Ref<GDExtension> lib; lib.instantiate(); String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path); + +#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) + // If running on the editor on Windows, we copy the library and open the copy. + // This is so the original file isn't locked and can be updated by a compiler. + if (Engine::get_singleton()->is_editor_hint()) { + if (!FileAccess::exists(abs_path)) { + if (r_error) { + *r_error = ERR_FILE_NOT_FOUND; + } + ERR_PRINT("GDExtension library not found: " + library_path); + return Ref<Resource>(); + } + + // Copy the file to the same directory as the original with a prefix in the name. + // This is so relative path to dependencies are satisfied. + String copy_path = abs_path.get_base_dir().path_join("~" + abs_path.get_file()); + + Error copy_err = DirAccess::copy_absolute(abs_path, copy_path); + if (copy_err) { + if (r_error) { + *r_error = ERR_CANT_CREATE; + } + ERR_PRINT("Error copying GDExtension library: " + library_path); + return Ref<Resource>(); + } + FileAccess::set_hidden_attribute(copy_path, true); + + // Save the copied path so it can be deleted later. + lib->set_temp_library_path(copy_path); + + // Use the copy to open the library. + abs_path = copy_path; + } +#endif err = lib->open_library(abs_path, entry_symbol); if (r_error) { @@ -643,6 +735,12 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String } if (err != OK) { +#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) + // If the DLL fails to load, make sure that temporary DLL copies are cleaned up. + if (Engine::get_singleton()->is_editor_hint()) { + DirAccess::remove_absolute(lib->get_temp_library_path()); + } +#endif // Errors already logged in open_library() return Ref<Resource>(); } diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index 77ec458d30..628cfae8c0 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -43,6 +43,9 @@ class GDExtension : public Resource { void *library = nullptr; // pointer if valid, String library_path; +#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) + String temp_lib_path; +#endif struct Extension { ObjectGDExtension gdextension; @@ -50,10 +53,21 @@ class GDExtension : public Resource { HashMap<StringName, Extension> extension_classes; + struct ClassCreationDeprecatedInfo { +#ifndef DISABLE_DEPRECATED + GDExtensionClassNotification notification_func = nullptr; +#endif // DISABLE_DEPRECATED + }; + +#ifndef DISABLE_DEPRECATED static void _register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs); +#endif // DISABLE_DEPRECATED + static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs); + static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr); static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info); static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield); static void _register_extension_class_property(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter); + static void _register_extension_class_property_indexed(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter, GDExtensionInt p_index); static void _register_extension_class_property_group(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_group_name, GDExtensionConstStringNamePtr p_prefix); static void _register_extension_class_property_subgroup(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_subgroup_name, GDExtensionConstStringNamePtr p_prefix); static void _register_extension_class_signal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_signal_name, const GDExtensionPropertyInfo *p_argument_info, GDExtensionInt p_argument_count); @@ -75,6 +89,11 @@ public: Error open_library(const String &p_path, const String &p_entry_symbol); void close_library(); +#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) + void set_temp_library_path(const String &p_path) { temp_lib_path = p_path; } + String get_temp_library_path() const { return temp_lib_path; } +#endif + enum InitializationLevel { INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE, INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS, diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index 7fbf2d00a1..f59ee291ed 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -726,6 +726,11 @@ static void gdextension_string_operator_plus_eq_c32str(GDExtensionStringPtr p_se *self += p_b; } +static GDExtensionInt gdextension_string_resize(GDExtensionStringPtr p_self, GDExtensionInt p_length) { + String *self = (String *)p_self; + return (*self).resize(p_length); +} + static GDExtensionInt gdextension_xml_parser_open_buffer(GDExtensionObjectPtr p_instance, const uint8_t *p_buffer, size_t p_size) { XMLParser *xml = (XMLParser *)p_instance; return (GDExtensionInt)xml->_open_buffer(p_buffer, p_size); @@ -1036,13 +1041,104 @@ static void gdextension_ref_set_object(GDExtensionRefPtr p_ref, GDExtensionObjec ref->reference_ptr(o); } +#ifndef DISABLE_DEPRECATED static GDExtensionScriptInstancePtr gdextension_script_instance_create(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data) { + GDExtensionScriptInstanceInfo2 *info_2 = memnew(GDExtensionScriptInstanceInfo2); + info_2->set_func = p_info->set_func; + info_2->get_func = p_info->get_func; + info_2->get_property_list_func = p_info->get_property_list_func; + info_2->free_property_list_func = p_info->free_property_list_func; + info_2->property_can_revert_func = p_info->property_can_revert_func; + info_2->property_get_revert_func = p_info->property_get_revert_func; + info_2->get_owner_func = p_info->get_owner_func; + info_2->get_property_state_func = p_info->get_property_state_func; + info_2->get_method_list_func = p_info->get_method_list_func; + info_2->free_method_list_func = p_info->free_method_list_func; + info_2->get_property_type_func = p_info->get_property_type_func; + info_2->validate_property_func = nullptr; + info_2->has_method_func = p_info->has_method_func; + info_2->call_func = p_info->call_func; + info_2->notification_func = nullptr; + info_2->to_string_func = p_info->to_string_func; + info_2->refcount_incremented_func = p_info->refcount_incremented_func; + info_2->refcount_decremented_func = p_info->refcount_decremented_func; + info_2->get_script_func = p_info->get_script_func; + info_2->is_placeholder_func = p_info->is_placeholder_func; + info_2->set_fallback_func = p_info->set_fallback_func; + info_2->get_fallback_func = p_info->get_fallback_func; + info_2->get_language_func = p_info->get_language_func; + info_2->free_func = p_info->free_func; + + ScriptInstanceExtension *script_instance_extension = memnew(ScriptInstanceExtension); + script_instance_extension->instance = p_instance_data; + script_instance_extension->native_info = info_2; + script_instance_extension->free_native_info = true; + script_instance_extension->deprecated_native_info.notification_func = p_info->notification_func; + return reinterpret_cast<GDExtensionScriptInstancePtr>(script_instance_extension); +} +#endif // DISABLE_DEPRECATED + +static GDExtensionScriptInstancePtr gdextension_script_instance_create2(const GDExtensionScriptInstanceInfo2 *p_info, GDExtensionScriptInstanceDataPtr p_instance_data) { ScriptInstanceExtension *script_instance_extension = memnew(ScriptInstanceExtension); script_instance_extension->instance = p_instance_data; script_instance_extension->native_info = p_info; return reinterpret_cast<GDExtensionScriptInstancePtr>(script_instance_extension); } +static GDExtensionScriptInstancePtr gdextension_placeholder_script_instance_create(GDExtensionObjectPtr p_language, GDExtensionObjectPtr p_script, GDExtensionObjectPtr p_owner) { + ScriptLanguage *language = (ScriptLanguage *)p_language; + Ref<Script> script; + script.reference_ptr((Script *)p_script); + Object *owner = (Object *)p_owner; + + PlaceHolderScriptInstance *placeholder = memnew(PlaceHolderScriptInstance(language, script, owner)); + return reinterpret_cast<GDExtensionScriptInstancePtr>(placeholder); +} + +static void gdextension_placeholder_script_instance_update(GDExtensionScriptInstancePtr p_placeholder, GDExtensionConstTypePtr p_properties, GDExtensionConstTypePtr p_values) { + PlaceHolderScriptInstance *placeholder = dynamic_cast<PlaceHolderScriptInstance *>(reinterpret_cast<ScriptInstance *>(p_placeholder)); + ERR_FAIL_NULL_MSG(placeholder, "Unable to update placeholder, expected a PlaceHolderScriptInstance but received an invalid type."); + + const Array &properties = *reinterpret_cast<const Array *>(p_properties); + const Dictionary &values = *reinterpret_cast<const Dictionary *>(p_values); + + List<PropertyInfo> properties_list; + HashMap<StringName, Variant> values_map; + + for (int i = 0; i < properties.size(); i++) { + Dictionary d = properties[i]; + properties_list.push_back(PropertyInfo::from_dict(d)); + } + + List<Variant> keys; + values.get_key_list(&keys); + + for (const Variant &E : keys) { + values_map.insert(E, values[E]); + } + + placeholder->update(properties_list, values_map); +} + +static GDExtensionScriptInstancePtr gdextension_object_get_script_instance(GDExtensionConstObjectPtr p_object, GDExtensionConstObjectPtr p_language) { + if (!p_object || !p_language) { + return nullptr; + } + + const Object *o = (const Object *)p_object; + ScriptInstanceExtension *script_instance_extension = reinterpret_cast<ScriptInstanceExtension *>(o->get_script_instance()); + if (!script_instance_extension) { + return nullptr; + } + + const ScriptLanguage *language = script_instance_extension->get_language(); + if (language != p_language) { + return nullptr; + } + + return script_instance_extension->instance; +} + static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionConstStringNamePtr p_classname, GDExtensionConstStringNamePtr p_methodname, GDExtensionInt p_hash) { const StringName classname = *reinterpret_cast<const StringName *>(p_classname); const StringName methodname = *reinterpret_cast<const StringName *>(p_methodname); @@ -1052,7 +1148,7 @@ static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionC ERR_PRINT("Method '" + classname + "." + methodname + "' has changed and no compatibility fallback has been provided. Please open an issue."); return nullptr; } - ERR_FAIL_COND_V(!mb, nullptr); + ERR_FAIL_NULL_V(mb, nullptr); if (mb->get_hash() != p_hash) { ERR_PRINT("Hash mismatch for method '" + classname + "." + methodname + "'."); return nullptr; @@ -1167,6 +1263,7 @@ void gdextension_setup_interface() { REGISTER_INTERFACE_FUNC(string_operator_plus_eq_cstr); REGISTER_INTERFACE_FUNC(string_operator_plus_eq_wcstr); REGISTER_INTERFACE_FUNC(string_operator_plus_eq_c32str); + REGISTER_INTERFACE_FUNC(string_resize); REGISTER_INTERFACE_FUNC(xml_parser_open_buffer); REGISTER_INTERFACE_FUNC(file_access_store_buffer); REGISTER_INTERFACE_FUNC(file_access_get_buffer); @@ -1209,7 +1306,13 @@ void gdextension_setup_interface() { REGISTER_INTERFACE_FUNC(object_get_instance_id); REGISTER_INTERFACE_FUNC(ref_get_object); REGISTER_INTERFACE_FUNC(ref_set_object); +#ifndef DISABLE_DEPRECATED REGISTER_INTERFACE_FUNC(script_instance_create); +#endif // DISABLE_DEPRECATED + REGISTER_INTERFACE_FUNC(script_instance_create2); + REGISTER_INTERFACE_FUNC(placeholder_script_instance_create); + REGISTER_INTERFACE_FUNC(placeholder_script_instance_update); + REGISTER_INTERFACE_FUNC(object_get_script_instance); REGISTER_INTERFACE_FUNC(classdb_construct_object); REGISTER_INTERFACE_FUNC(classdb_get_method_bind); REGISTER_INTERFACE_FUNC(classdb_get_class_tag); diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 4d7bdf9502..9d9ae20c51 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -258,7 +258,9 @@ typedef const GDExtensionPropertyInfo *(*GDExtensionClassGetPropertyList)(GDExte typedef void (*GDExtensionClassFreePropertyList)(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list); typedef GDExtensionBool (*GDExtensionClassPropertyCanRevert)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name); typedef GDExtensionBool (*GDExtensionClassPropertyGetRevert)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); -typedef void (*GDExtensionClassNotification)(GDExtensionClassInstancePtr p_instance, int32_t p_what); +typedef GDExtensionBool (*GDExtensionClassValidateProperty)(GDExtensionClassInstancePtr p_instance, GDExtensionPropertyInfo *p_property); +typedef void (*GDExtensionClassNotification)(GDExtensionClassInstancePtr p_instance, int32_t p_what); // Deprecated. Use GDExtensionClassNotification2 instead. +typedef void (*GDExtensionClassNotification2)(GDExtensionClassInstancePtr p_instance, int32_t p_what, GDExtensionBool p_reversed); typedef void (*GDExtensionClassToString)(GDExtensionClassInstancePtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr p_out); typedef void (*GDExtensionClassReference)(GDExtensionClassInstancePtr p_instance); typedef void (*GDExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance); @@ -285,7 +287,29 @@ typedef struct { GDExtensionClassGetVirtual get_virtual_func; // Queries a virtual function by name and returns a callback to invoke the requested virtual function. GDExtensionClassGetRID get_rid_func; void *class_userdata; // Per-class user data, later accessible in instance bindings. -} GDExtensionClassCreationInfo; +} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo2 instead. + +typedef struct { + GDExtensionBool is_virtual; + GDExtensionBool is_abstract; + GDExtensionBool is_exposed; + GDExtensionClassSet set_func; + GDExtensionClassGet get_func; + GDExtensionClassGetPropertyList get_property_list_func; + GDExtensionClassFreePropertyList free_property_list_func; + GDExtensionClassPropertyCanRevert property_can_revert_func; + GDExtensionClassPropertyGetRevert property_get_revert_func; + GDExtensionClassValidateProperty validate_property_func; + GDExtensionClassNotification2 notification_func; + GDExtensionClassToString to_string_func; + GDExtensionClassReference reference_func; + GDExtensionClassUnreference unreference_func; + GDExtensionClassCreateInstance create_instance_func; // (Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract. + GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory. + GDExtensionClassGetVirtual get_virtual_func; // Queries a virtual function by name and returns a callback to invoke the requested virtual function. + GDExtensionClassGetRID get_rid_func; + void *class_userdata; // Per-class user data, later accessible in instance bindings. +} GDExtensionClassCreationInfo2; typedef void *GDExtensionClassLibraryPtr; @@ -352,6 +376,7 @@ typedef GDExtensionBool (*GDExtensionScriptInstanceGet)(GDExtensionScriptInstanc typedef const GDExtensionPropertyInfo *(*GDExtensionScriptInstanceGetPropertyList)(GDExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count); typedef void (*GDExtensionScriptInstanceFreePropertyList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionPropertyInfo *p_list); typedef GDExtensionVariantType (*GDExtensionScriptInstanceGetPropertyType)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionBool *r_is_valid); +typedef GDExtensionBool (*GDExtensionScriptInstanceValidateProperty)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionPropertyInfo *p_property); typedef GDExtensionBool (*GDExtensionScriptInstancePropertyCanRevert)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name); typedef GDExtensionBool (*GDExtensionScriptInstancePropertyGetRevert)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); @@ -366,7 +391,8 @@ typedef void (*GDExtensionScriptInstanceFreeMethodList)(GDExtensionScriptInstanc typedef GDExtensionBool (*GDExtensionScriptInstanceHasMethod)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name); typedef void (*GDExtensionScriptInstanceCall)(GDExtensionScriptInstanceDataPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); -typedef void (*GDExtensionScriptInstanceNotification)(GDExtensionScriptInstanceDataPtr p_instance, int32_t p_what); +typedef void (*GDExtensionScriptInstanceNotification)(GDExtensionScriptInstanceDataPtr p_instance, int32_t p_what); // Deprecated. Use GDExtensionScriptInstanceNotification2 instead. +typedef void (*GDExtensionScriptInstanceNotification2)(GDExtensionScriptInstanceDataPtr p_instance, int32_t p_what, GDExtensionBool p_reversed); typedef void (*GDExtensionScriptInstanceToString)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out); typedef void (*GDExtensionScriptInstanceRefCountIncremented)(GDExtensionScriptInstanceDataPtr p_instance); @@ -420,7 +446,47 @@ typedef struct { GDExtensionScriptInstanceFree free_func; -} GDExtensionScriptInstanceInfo; +} GDExtensionScriptInstanceInfo; // Deprecated. Use GDExtensionScriptInstanceInfo2 instead. + +typedef struct { + GDExtensionScriptInstanceSet set_func; + GDExtensionScriptInstanceGet get_func; + GDExtensionScriptInstanceGetPropertyList get_property_list_func; + GDExtensionScriptInstanceFreePropertyList free_property_list_func; + + GDExtensionScriptInstancePropertyCanRevert property_can_revert_func; + GDExtensionScriptInstancePropertyGetRevert property_get_revert_func; + + GDExtensionScriptInstanceGetOwner get_owner_func; + GDExtensionScriptInstanceGetPropertyState get_property_state_func; + + GDExtensionScriptInstanceGetMethodList get_method_list_func; + GDExtensionScriptInstanceFreeMethodList free_method_list_func; + GDExtensionScriptInstanceGetPropertyType get_property_type_func; + GDExtensionScriptInstanceValidateProperty validate_property_func; + + GDExtensionScriptInstanceHasMethod has_method_func; + + GDExtensionScriptInstanceCall call_func; + GDExtensionScriptInstanceNotification2 notification_func; + + GDExtensionScriptInstanceToString to_string_func; + + GDExtensionScriptInstanceRefCountIncremented refcount_incremented_func; + GDExtensionScriptInstanceRefCountDecremented refcount_decremented_func; + + GDExtensionScriptInstanceGetScript get_script_func; + + GDExtensionScriptInstanceIsPlaceholder is_placeholder_func; + + GDExtensionScriptInstanceSet set_fallback_func; + GDExtensionScriptInstanceGet get_fallback_func; + + GDExtensionScriptInstanceGetLanguage get_language_func; + + GDExtensionScriptInstanceFree free_func; + +} GDExtensionScriptInstanceInfo2; /* INITIALIZATION */ @@ -1526,6 +1592,25 @@ typedef void (*GDExtensionInterfaceStringOperatorPlusEqWcstr)(GDExtensionStringP */ typedef void (*GDExtensionInterfaceStringOperatorPlusEqC32str)(GDExtensionStringPtr p_self, const char32_t *p_b); +/** + * @name string_resize + * @since 4.2 + * + * Resizes the underlying string data to the given number of characters. + * + * Space needs to be allocated for the null terminating character ('\0') which + * also must be added manually, in order for all string functions to work correctly. + * + * Warning: This is an error-prone operation - only use it if there's no other + * efficient way to accomplish your goal. + * + * @param p_self A pointer to the String. + * @param p_resize The new length for the String. + * + * @return Error code signifying if the operation successful. + */ +typedef GDExtensionInt (*GDExtensionInterfaceStringResize)(GDExtensionStringPtr p_self, GDExtensionInt p_resize); + /* INTERFACE: XMLParser Utilities */ /** @@ -2097,6 +2182,7 @@ typedef void (*GDExtensionInterfaceRefSetObject)(GDExtensionRefPtr p_ref, GDExte /** * @name script_instance_create * @since 4.1 + * @deprecated in Godot 4.2. Use `script_instance_create2` instead. * * Creates a script instance that contains the given info and instance data. * @@ -2107,6 +2193,63 @@ typedef void (*GDExtensionInterfaceRefSetObject)(GDExtensionRefPtr p_ref, GDExte */ typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate)(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data); +/** + * @name script_instance_create2 + * @since 4.2 + * + * Creates a script instance that contains the given info and instance data. + * + * @param p_info A pointer to a GDExtensionScriptInstanceInfo2 struct. + * @param p_instance_data A pointer to a data representing the script instance in the GDExtension. This will be passed to all the function pointers on p_info. + * + * @return A pointer to a ScriptInstanceExtension object. + */ +typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate2)(const GDExtensionScriptInstanceInfo2 *p_info, GDExtensionScriptInstanceDataPtr p_instance_data); + +/** + * @name placeholder_script_instance_create + * @since 4.2 + * + * Creates a placeholder script instance for a given script and instance. + * + * This interface is optional as a custom placeholder could also be created with script_instance_create(). + * + * @param p_language A pointer to a ScriptLanguage. + * @param p_script A pointer to a Script. + * @param p_owner A pointer to an Object. + * + * @return A pointer to a PlaceHolderScriptInstance object. + */ +typedef GDExtensionScriptInstancePtr (*GDExtensionInterfacePlaceHolderScriptInstanceCreate)(GDExtensionObjectPtr p_language, GDExtensionObjectPtr p_script, GDExtensionObjectPtr p_owner); + +/** + * @name placeholder_script_instance_update + * @since 4.2 + * + * Updates a placeholder script instance with the given properties and values. + * + * The passed in placeholder must be an instance of PlaceHolderScriptInstance + * such as the one returned by placeholder_script_instance_create(). + * + * @param p_placeholder A pointer to a PlaceHolderScriptInstance. + * @param p_properties A pointer to an Array of Dictionary representing PropertyInfo. + * @param p_values A pointer to a Dictionary mapping StringName to Variant values. + */ +typedef void (*GDExtensionInterfacePlaceHolderScriptInstanceUpdate)(GDExtensionScriptInstancePtr p_placeholder, GDExtensionConstTypePtr p_properties, GDExtensionConstTypePtr p_values); + +/** + * @name object_get_script_instance + * @since 4.2 + * + * Get the script instance data attached to this object. + * + * @param p_object A pointer to the Object. + * @param p_language A pointer to the language expected for this script instance. + * + * @return A GDExtensionScriptInstanceDataPtr that was attached to this object as part of script_instance_create. + */ +typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptInstance)(GDExtensionConstObjectPtr p_object, GDExtensionObjectPtr p_language); + /* INTERFACE: ClassDB */ /** @@ -2154,6 +2297,7 @@ typedef void *(*GDExtensionInterfaceClassdbGetClassTag)(GDExtensionConstStringNa /** * @name classdb_register_extension_class * @since 4.1 + * @deprecated in Godot 4.2. Use `classdb_register_extension_class2` instead. * * Registers an extension class in the ClassDB. * @@ -2167,6 +2311,21 @@ typedef void *(*GDExtensionInterfaceClassdbGetClassTag)(GDExtensionConstStringNa typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs); /** + * @name classdb_register_extension_class2 + * @since 4.2 + * + * Registers an extension class in the ClassDB. + * + * Provided struct can be safely freed once the function returns. + * + * @param p_library A pointer the library received by the GDExtension's entry point function. + * @param p_class_name A pointer to a StringName with the class name. + * @param p_parent_class_name A pointer to a StringName with the parent class name. + * @param p_extension_funcs A pointer to a GDExtensionClassCreationInfo2 struct. + */ +typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs); + +/** * @name classdb_register_extension_class_method * @since 4.1 * @@ -2212,6 +2371,23 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant) typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassProperty)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter); /** + * @name classdb_register_extension_class_property_indexed + * @since 4.2 + * + * Registers an indexed property on an extension class in the ClassDB. + * + * Provided struct can be safely freed once the function returns. + * + * @param p_library A pointer the library received by the GDExtension's entry point function. + * @param p_class_name A pointer to a StringName with the class name. + * @param p_info A pointer to a GDExtensionPropertyInfo struct. + * @param p_setter A pointer to a StringName with the name of the setter method. + * @param p_getter A pointer to a StringName with the name of the getter method. + * @param p_index The index to pass as the first argument to the getter and setter methods. + */ +typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassPropertyIndexed)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter, GDExtensionInt p_index); + +/** * @name classdb_register_extension_class_property_group * @since 4.1 * diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp index 63e809bc7c..0f0e2fad41 100644 --- a/core/extension/gdextension_manager.cpp +++ b/core/extension/gdextension_manager.cpp @@ -143,6 +143,8 @@ void GDExtensionManager::load_extensions() { ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, "Error loading extension: " + s); } } + + OS::get_singleton()->load_platform_gdextensions(); } GDExtensionManager *GDExtensionManager::get_singleton() { diff --git a/core/input/godotcontrollerdb.txt b/core/input/godotcontrollerdb.txt index 7c51e20b4c..f5158bfabb 100644 --- a/core/input/godotcontrollerdb.txt +++ b/core/input/godotcontrollerdb.txt @@ -33,6 +33,3 @@ Linux20d6a713,Bensussen Deutsch & Associates Inc.(BDA) NSW Wired controller,a:b1 Linux054c05c4,Sony Computer Entertainment Wireless Controller,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web Linux18d19400,18d1-9400-Google LLC Stadia Controller rev. A,a:b0,b:b1,y:b3,x:b2,start:b7,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Web Linux054c0268,054c-0268-Sony PLAYSTATION(R)3 Controller,a:b0,b:b1,y:b2,x:b3,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b15,dpdown:b14,dpright:b16,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web - -# UWP -__UWP_GAMEPAD__,Xbox Controller,a:b2,b:b3,x:b4,y:b5,start:b0,back:b1,leftstick:b12,rightstick:b13,leftshoulder:b10,rightshoulder:b11,dpup:b6,dpdown:b7,dpleft:b8,dpright:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:UWP, diff --git a/core/input/input.cpp b/core/input/input.cpp index d481acf005..19ea8c7317 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -113,6 +113,7 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("get_joy_axis", "device", "axis"), &Input::get_joy_axis); ClassDB::bind_method(D_METHOD("get_joy_name", "device"), &Input::get_joy_name); ClassDB::bind_method(D_METHOD("get_joy_guid", "device"), &Input::get_joy_guid); + ClassDB::bind_method(D_METHOD("get_joy_info", "device"), &Input::get_joy_info); ClassDB::bind_method(D_METHOD("should_ignore_device", "vendor_id", "product_id"), &Input::should_ignore_device); ClassDB::bind_method(D_METHOD("get_connected_joypads"), &Input::get_connected_joypads); ClassDB::bind_method(D_METHOD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength); @@ -284,7 +285,7 @@ bool Input::is_joy_button_pressed(int p_device, JoyButton p_button) const { 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)); - return action_state.has(p_action) && action_state[p_action].pressed && (p_exact ? action_state[p_action].exact : true); + return action_state.has(p_action) && action_state[p_action].pressed > 0 && (p_exact ? action_state[p_action].exact : true); } bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const { @@ -437,11 +438,12 @@ static String _hex_str(uint8_t p_byte) { return ret; } -void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid) { +void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid, Dictionary p_joypad_info) { _THREAD_SAFE_METHOD_ Joypad js; js.name = p_connected ? p_name : ""; js.uid = p_connected ? p_guid : ""; + js.info = p_connected ? p_joypad_info : Dictionary(); if (p_connected) { String uidname = p_guid; @@ -473,7 +475,8 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, S } joy_names[p_idx] = js; - emit_signal(SNAME("joy_connection_changed"), p_idx, p_connected); + // Ensure this signal is emitted on the main thread, as some platforms (e.g. Linux) call this from a different thread. + call_deferred("emit_signal", SNAME("joy_connection_changed"), p_idx, p_connected); } Vector3 Input::get_gravity() const { @@ -694,23 +697,51 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em for (const KeyValue<StringName, InputMap::Action> &E : InputMap::get_singleton()->get_action_map()) { if (InputMap::get_singleton()->event_is_action(p_event, E.key)) { Action &action = action_state[E.key]; - // If not echo and action pressed state has changed - if (!p_event->is_echo() && is_action_pressed(E.key, false) != p_event->is_action_pressed(E.key)) { + bool is_pressed = false; + + if (!p_event->is_echo()) { if (p_event->is_action_pressed(E.key)) { - action.pressed = true; - action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); - action.pressed_process_frame = Engine::get_singleton()->get_process_frames(); + if (jm.is_valid()) { + // If axis is already pressed, don't increase the pressed counter. + if (!action.axis_pressed) { + action.pressed++; + action.axis_pressed = true; + } + } else { + action.pressed++; + } + + is_pressed = true; + if (action.pressed == 1) { + action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); + action.pressed_process_frame = Engine::get_singleton()->get_process_frames(); + } } else { - action.pressed = false; - action.released_physics_frame = Engine::get_singleton()->get_physics_frames(); - action.released_process_frame = Engine::get_singleton()->get_process_frames(); + bool is_released = true; + if (jm.is_valid()) { + // Same as above. Don't release axis when not pressed. + if (action.axis_pressed) { + action.axis_pressed = false; + } else { + is_released = false; + } + } + + if (is_released) { + if (action.pressed == 1) { + action.released_physics_frame = Engine::get_singleton()->get_physics_frames(); + action.released_process_frame = Engine::get_singleton()->get_process_frames(); + } + action.pressed = MAX(action.pressed - 1, 0); + } } - action.strength = 0.0f; - action.raw_strength = 0.0f; action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true); } - action.strength = p_event->get_action_strength(E.key); - action.raw_strength = p_event->get_action_raw_strength(E.key); + + if (is_pressed || action.pressed == 0) { + action.strength = p_event->get_action_strength(E.key); + action.raw_strength = p_event->get_action_raw_strength(E.key); + } } } @@ -828,9 +859,11 @@ void Input::action_press(const StringName &p_action, float p_strength) { // Create or retrieve existing action. Action &action = action_state[p_action]; - action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); - action.pressed_process_frame = Engine::get_singleton()->get_process_frames(); - action.pressed = true; + action.pressed++; + if (action.pressed == 1) { + action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); + action.pressed_process_frame = Engine::get_singleton()->get_process_frames(); + } action.strength = p_strength; action.raw_strength = p_strength; action.exact = true; @@ -840,9 +873,11 @@ void Input::action_release(const StringName &p_action) { // Create or retrieve existing action. Action &action = action_state[p_action]; - action.released_physics_frame = Engine::get_singleton()->get_physics_frames(); - action.released_process_frame = Engine::get_singleton()->get_process_frames(); - action.pressed = false; + action.pressed--; + if (action.pressed == 0) { + action.released_physics_frame = Engine::get_singleton()->get_physics_frames(); + action.released_process_frame = Engine::get_singleton()->get_process_frames(); + } action.strength = 0.0f; action.raw_strength = 0.0f; action.exact = true; @@ -1003,8 +1038,10 @@ void Input::release_pressed_events() { joy_buttons_pressed.clear(); _joy_axis.clear(); - for (const KeyValue<StringName, Input::Action> &E : action_state) { - if (E.value.pressed) { + for (KeyValue<StringName, Input::Action> &E : action_state) { + if (E.value.pressed > 0) { + // Make sure the action is really released. + E.value.pressed = 1; action_release(E.key); } } @@ -1499,6 +1536,11 @@ String Input::get_joy_guid(int p_device) const { return joy_names[p_device].uid; } +Dictionary Input::get_joy_info(int p_device) const { + ERR_FAIL_COND_V(!joy_names.has(p_device), Dictionary()); + return joy_names[p_device].info; +} + bool Input::should_ignore_device(int p_vendor_id, int p_product_id) const { uint32_t full_id = (((uint32_t)p_vendor_id) << 16) | ((uint16_t)p_product_id); return ignored_device_ids.has(full_id); diff --git a/core/input/input.h b/core/input/input.h index ec16871b72..8ce5f64a6a 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -103,7 +103,8 @@ private: uint64_t pressed_process_frame = UINT64_MAX; uint64_t released_physics_frame = UINT64_MAX; uint64_t released_process_frame = UINT64_MAX; - bool pressed = false; + int pressed = 0; + bool axis_pressed = false; bool exact = true; float strength = 0.0f; float raw_strength = 0.0f; @@ -149,6 +150,7 @@ private: HatMask last_hat = HatMask::CENTER; int mapping = -1; int hat_current = 0; + Dictionary info; }; VelocityTrack mouse_velocity_track; @@ -276,7 +278,7 @@ public: Vector2 get_joy_vibration_strength(int p_device); float get_joy_vibration_duration(int p_device); uint64_t get_joy_vibration_timestamp(int p_device); - void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = ""); + void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = "", Dictionary p_joypad_info = Dictionary()); Vector3 get_gravity() const; Vector3 get_accelerometer() const; @@ -332,6 +334,7 @@ public: bool is_joy_known(int p_device); String get_joy_guid(int p_device) const; bool should_ignore_device(int p_vendor_id, int p_product_id) const; + Dictionary get_joy_info(int p_device) const; void set_fallback_mapping(String p_guid); void flush_buffered_events(); diff --git a/core/input/input_builders.py b/core/input/input_builders.py index 76e199e0ff..e98e2441e2 100644 --- a/core/input/input_builders.py +++ b/core/input/input_builders.py @@ -51,7 +51,6 @@ def make_default_controller_mappings(target, source, env): "Android": "#if defined(__ANDROID__)", "iOS": "#ifdef IOS_ENABLED", "Web": "#ifdef WEB_ENABLED", - "UWP": "#ifdef UWP_ENABLED", } g.write("const char* DefaultControllerMappings::mappings[] = {\n") diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index e37886cbe9..9e341b2157 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -1722,7 +1722,27 @@ String InputEventMIDI::as_text() const { } String InputEventMIDI::to_string() { - return vformat("InputEventMIDI: channel=%d, message=%d, pitch=%d, velocity=%d, pressure=%d, controller_number=%d, controller_value=%d", channel, message, pitch, velocity, pressure, controller_number, controller_value); + String ret; + switch (message) { + case MIDIMessage::NOTE_ON: + ret = vformat("Note On: channel=%d, pitch=%d, velocity=%d", channel, pitch, velocity); + break; + case MIDIMessage::NOTE_OFF: + ret = vformat("Note Off: channel=%d, pitch=%d, velocity=%d", channel, pitch, velocity); + break; + case MIDIMessage::PITCH_BEND: + ret = vformat("Pitch Bend: channel=%d, pitch=%d", channel, pitch); + break; + case MIDIMessage::CHANNEL_PRESSURE: + ret = vformat("Channel Pressure: channel=%d, pressure=%d", channel, pressure); + break; + case MIDIMessage::CONTROL_CHANGE: + ret = vformat("Control Change: channel=%d, controller_number=%d, controller_value=%d", channel, controller_number, controller_value); + break; + default: + ret = vformat("channel=%d, message=%d, pitch=%d, velocity=%d, pressure=%d, controller_number=%d, controller_value=%d, instrument=%d", channel, message, pitch, velocity, pressure, controller_number, controller_value, instrument); + } + return "InputEventMIDI: " + ret; } void InputEventMIDI::_bind_methods() { diff --git a/core/io/compression.cpp b/core/io/compression.cpp index e36fb0afa4..4e4627c40b 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -215,7 +215,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s #ifdef BROTLI_ENABLED BrotliDecoderResult ret; BrotliDecoderState *state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); - ERR_FAIL_COND_V(state == nullptr, Z_DATA_ERROR); + ERR_FAIL_NULL_V(state, Z_DATA_ERROR); // Setup the stream inputs. const uint8_t *next_in = p_src; diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index b669afdc99..6026dbf896 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -583,7 +583,7 @@ uint64_t FileAccess::get_modified_time(const String &p_file) { return mt; } -uint32_t FileAccess::get_unix_permissions(const String &p_file) { +BitField<FileAccess::UnixPermissionFlags> FileAccess::get_unix_permissions(const String &p_file) { if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { return 0; } @@ -591,11 +591,10 @@ uint32_t FileAccess::get_unix_permissions(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 + "'."); - uint32_t mt = fa->_get_unix_permissions(p_file); - return mt; + return fa->_get_unix_permissions(p_file); } -Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissions) { +Error FileAccess::set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) { if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { return ERR_UNAVAILABLE; } @@ -607,6 +606,52 @@ Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissi return err; } +bool FileAccess::get_hidden_attribute(const String &p_file) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { + return false; + } + + Ref<FileAccess> fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'."); + + return fa->_get_hidden_attribute(p_file); +} + +Error FileAccess::set_hidden_attribute(const String &p_file, bool p_hidden) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { + return ERR_UNAVAILABLE; + } + + 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 + "'."); + + Error err = fa->_set_hidden_attribute(p_file, p_hidden); + return err; +} + +bool FileAccess::get_read_only_attribute(const String &p_file) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { + return false; + } + + Ref<FileAccess> fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'."); + + return fa->_get_read_only_attribute(p_file); +} + +Error FileAccess::set_read_only_attribute(const String &p_file, bool p_ro) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { + return ERR_UNAVAILABLE; + } + + 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 + "'."); + + Error err = fa->_set_read_only_attribute(p_file, p_ro); + return err; +} + void FileAccess::store_string(const String &p_string) { if (p_string.length() == 0) { return; @@ -865,6 +910,14 @@ void FileAccess::_bind_methods() { ClassDB::bind_static_method("FileAccess", D_METHOD("file_exists", "path"), &FileAccess::exists); ClassDB::bind_static_method("FileAccess", D_METHOD("get_modified_time", "file"), &FileAccess::get_modified_time); + ClassDB::bind_static_method("FileAccess", D_METHOD("get_unix_permissions", "file"), &FileAccess::get_unix_permissions); + ClassDB::bind_static_method("FileAccess", D_METHOD("set_unix_permissions", "file", "permissions"), &FileAccess::set_unix_permissions); + + ClassDB::bind_static_method("FileAccess", D_METHOD("get_hidden_attribute", "file"), &FileAccess::get_hidden_attribute); + ClassDB::bind_static_method("FileAccess", D_METHOD("set_hidden_attribute", "file", "hidden"), &FileAccess::set_hidden_attribute); + ClassDB::bind_static_method("FileAccess", D_METHOD("set_read_only_attribute", "file", "ro"), &FileAccess::set_read_only_attribute); + ClassDB::bind_static_method("FileAccess", D_METHOD("get_read_only_attribute", "file"), &FileAccess::get_read_only_attribute); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian"); BIND_ENUM_CONSTANT(READ); @@ -877,4 +930,17 @@ void FileAccess::_bind_methods() { BIND_ENUM_CONSTANT(COMPRESSION_ZSTD); BIND_ENUM_CONSTANT(COMPRESSION_GZIP); BIND_ENUM_CONSTANT(COMPRESSION_BROTLI); + + BIND_BITFIELD_FLAG(UNIX_READ_OWNER); + BIND_BITFIELD_FLAG(UNIX_WRITE_OWNER); + BIND_BITFIELD_FLAG(UNIX_EXECUTE_OWNER); + BIND_BITFIELD_FLAG(UNIX_READ_GROUP); + BIND_BITFIELD_FLAG(UNIX_WRITE_GROUP); + BIND_BITFIELD_FLAG(UNIX_EXECUTE_GROUP); + BIND_BITFIELD_FLAG(UNIX_READ_OTHER); + BIND_BITFIELD_FLAG(UNIX_WRITE_OTHER); + BIND_BITFIELD_FLAG(UNIX_EXECUTE_OTHER); + BIND_BITFIELD_FLAG(UNIX_SET_USER_ID); + BIND_BITFIELD_FLAG(UNIX_SET_GROUP_ID); + BIND_BITFIELD_FLAG(UNIX_RESTRICTED_DELETE); } diff --git a/core/io/file_access.h b/core/io/file_access.h index ad1ac665f3..7b9e66bb83 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -60,6 +60,21 @@ public: WRITE_READ = 7, }; + enum UnixPermissionFlags { + UNIX_EXECUTE_OTHER = 0x001, + UNIX_WRITE_OTHER = 0x002, + UNIX_READ_OTHER = 0x004, + UNIX_EXECUTE_GROUP = 0x008, + UNIX_WRITE_GROUP = 0x010, + UNIX_READ_GROUP = 0x020, + UNIX_EXECUTE_OWNER = 0x040, + UNIX_WRITE_OWNER = 0x080, + UNIX_READ_OWNER = 0x100, + UNIX_RESTRICTED_DELETE = 0x200, + UNIX_SET_GROUP_ID = 0x400, + UNIX_SET_USER_ID = 0x800, + }; + enum CompressionMode { COMPRESSION_FASTLZ = Compression::MODE_FASTLZ, COMPRESSION_DEFLATE = Compression::MODE_DEFLATE, @@ -74,8 +89,13 @@ public: bool big_endian = false; bool real_is_double = false; - virtual uint32_t _get_unix_permissions(const String &p_file) = 0; - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) = 0; + virtual BitField<UnixPermissionFlags> _get_unix_permissions(const String &p_file) = 0; + virtual Error _set_unix_permissions(const String &p_file, BitField<UnixPermissionFlags> p_permissions) = 0; + + virtual bool _get_hidden_attribute(const String &p_file) = 0; + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) = 0; + virtual bool _get_read_only_attribute(const String &p_file) = 0; + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) = 0; protected: static void _bind_methods(); @@ -185,8 +205,13 @@ public: static CreateFunc get_create_func(AccessType p_access); static bool exists(const String &p_name); ///< return true if a file exists static uint64_t get_modified_time(const String &p_file); - static uint32_t get_unix_permissions(const String &p_file); - static Error set_unix_permissions(const String &p_file, uint32_t p_permissions); + static BitField<FileAccess::UnixPermissionFlags> get_unix_permissions(const String &p_file); + static Error set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions); + + static bool get_hidden_attribute(const String &p_file); + static Error set_hidden_attribute(const String &p_file, bool p_hidden); + 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; }; @@ -212,5 +237,6 @@ public: VARIANT_ENUM_CAST(FileAccess::CompressionMode); VARIANT_ENUM_CAST(FileAccess::ModeFlags); +VARIANT_BITFIELD_CAST(FileAccess::UnixPermissionFlags); #endif // FILE_ACCESS_H diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index 3e5a1217dd..0f00bd292c 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -365,20 +365,48 @@ uint64_t FileAccessCompressed::_get_modified_time(const String &p_file) { } } -uint32_t FileAccessCompressed::_get_unix_permissions(const String &p_file) { +BitField<FileAccess::UnixPermissionFlags> FileAccessCompressed::_get_unix_permissions(const String &p_file) { if (f.is_valid()) { return f->_get_unix_permissions(p_file); } return 0; } -Error FileAccessCompressed::_set_unix_permissions(const String &p_file, uint32_t p_permissions) { +Error FileAccessCompressed::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) { if (f.is_valid()) { return f->_set_unix_permissions(p_file, p_permissions); } return FAILED; } +bool FileAccessCompressed::_get_hidden_attribute(const String &p_file) { + if (f.is_valid()) { + return f->_get_hidden_attribute(p_file); + } + return false; +} + +Error FileAccessCompressed::_set_hidden_attribute(const String &p_file, bool p_hidden) { + if (f.is_valid()) { + return f->_set_hidden_attribute(p_file, p_hidden); + } + return FAILED; +} + +bool FileAccessCompressed::_get_read_only_attribute(const String &p_file) { + if (f.is_valid()) { + return f->_get_read_only_attribute(p_file); + } + return false; +} + +Error FileAccessCompressed::_set_read_only_attribute(const String &p_file, bool p_ro) { + if (f.is_valid()) { + return f->_set_read_only_attribute(p_file, p_ro); + } + return FAILED; +} + void FileAccessCompressed::close() { _close(); } diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index 601b74a9c1..bf57eaa07c 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -94,8 +94,13 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override; - virtual uint32_t _get_unix_permissions(const String &p_file) override; - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override; + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override; + + virtual bool _get_hidden_attribute(const String &p_file) override; + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override; + virtual bool _get_read_only_attribute(const String &p_file) override; + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override; virtual void close() override; diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index c39d19d52b..b689f5b628 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -285,13 +285,46 @@ uint64_t FileAccessEncrypted::_get_modified_time(const String &p_file) { return 0; } -uint32_t FileAccessEncrypted::_get_unix_permissions(const String &p_file) { +BitField<FileAccess::UnixPermissionFlags> FileAccessEncrypted::_get_unix_permissions(const String &p_file) { + if (file.is_valid()) { + return file->_get_unix_permissions(p_file); + } return 0; } -Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, uint32_t p_permissions) { - ERR_PRINT("Setting UNIX permissions on encrypted files is not implemented yet."); - return ERR_UNAVAILABLE; +Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) { + if (file.is_valid()) { + return file->_set_unix_permissions(p_file, p_permissions); + } + return FAILED; +} + +bool FileAccessEncrypted::_get_hidden_attribute(const String &p_file) { + if (file.is_valid()) { + return file->_get_hidden_attribute(p_file); + } + return false; +} + +Error FileAccessEncrypted::_set_hidden_attribute(const String &p_file, bool p_hidden) { + if (file.is_valid()) { + return file->_set_hidden_attribute(p_file, p_hidden); + } + return FAILED; +} + +bool FileAccessEncrypted::_get_read_only_attribute(const String &p_file) { + if (file.is_valid()) { + return file->_get_read_only_attribute(p_file); + } + return false; +} + +Error FileAccessEncrypted::_set_read_only_attribute(const String &p_file, bool p_ro) { + if (file.is_valid()) { + return file->_set_read_only_attribute(p_file, p_ro); + } + return FAILED; } void FileAccessEncrypted::close() { diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index 9702b5a517..489d213b8f 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -85,8 +85,13 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override; - virtual uint32_t _get_unix_permissions(const String &p_file) override; - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override; + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override; + + virtual bool _get_hidden_attribute(const String &p_file) override; + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override; + virtual bool _get_read_only_attribute(const String &p_file) override; + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override; virtual void close() override; diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index 14ec0be092..0983920b94 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -79,7 +79,7 @@ Error FileAccessMemory::open_custom(const uint8_t *p_data, uint64_t p_len) { } Error FileAccessMemory::open_internal(const String &p_path, int p_mode_flags) { - ERR_FAIL_COND_V(!files, ERR_FILE_NOT_FOUND); + ERR_FAIL_NULL_V(files, ERR_FILE_NOT_FOUND); String name = fix_path(p_path); //name = DirAccess::normalize_path(name); @@ -99,22 +99,22 @@ bool FileAccessMemory::is_open() const { } void FileAccessMemory::seek(uint64_t p_position) { - ERR_FAIL_COND(!data); + ERR_FAIL_NULL(data); pos = p_position; } void FileAccessMemory::seek_end(int64_t p_position) { - ERR_FAIL_COND(!data); + ERR_FAIL_NULL(data); pos = length + p_position; } uint64_t FileAccessMemory::get_position() const { - ERR_FAIL_COND_V(!data, 0); + ERR_FAIL_NULL_V(data, 0); return pos; } uint64_t FileAccessMemory::get_length() const { - ERR_FAIL_COND_V(!data, 0); + ERR_FAIL_NULL_V(data, 0); return length; } @@ -134,7 +134,7 @@ uint8_t FileAccessMemory::get_8() const { uint64_t FileAccessMemory::get_buffer(uint8_t *p_dst, uint64_t p_length) const { ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); - ERR_FAIL_COND_V(!data, -1); + ERR_FAIL_NULL_V(data, -1); uint64_t left = length - pos; uint64_t read = MIN(p_length, left); @@ -154,11 +154,11 @@ Error FileAccessMemory::get_error() const { } void FileAccessMemory::flush() { - ERR_FAIL_COND(!data); + ERR_FAIL_NULL(data); } void FileAccessMemory::store_8(uint8_t p_byte) { - ERR_FAIL_COND(!data); + ERR_FAIL_NULL(data); ERR_FAIL_COND(pos >= length); data[pos++] = p_byte; } diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index 43fe6ab658..ac08e5406f 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -68,8 +68,13 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } - virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; } - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; } + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; } + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; } + + virtual bool _get_hidden_attribute(const String &p_file) override { return false; } + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; } + virtual bool _get_read_only_attribute(const String &p_file) override { return false; } + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; } virtual void close() override {} diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 1538b302c2..97391a5611 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -150,8 +150,13 @@ class FileAccessPack : public FileAccess { Ref<FileAccess> f; virtual Error open_internal(const String &p_path, int p_mode_flags) override; virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } - virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; } - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; } + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; } + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; } + + virtual bool _get_hidden_attribute(const String &p_file) override { return false; } + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; } + virtual bool _get_read_only_attribute(const String &p_file) override { return false; } + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; } public: virtual bool is_open() const override; diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index c7f1a73f97..d085c29728 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -239,7 +239,7 @@ Error FileAccessZip::open_internal(const String &p_path, int p_mode_flags) { ERR_FAIL_COND_V(p_mode_flags & FileAccess::WRITE, FAILED); ZipArchive *arch = ZipArchive::get_singleton(); - ERR_FAIL_COND_V(!arch, FAILED); + ERR_FAIL_NULL_V(arch, FAILED); zfile = arch->get_file_handle(p_path); ERR_FAIL_COND_V(!zfile, FAILED); @@ -255,7 +255,7 @@ void FileAccessZip::_close() { } ZipArchive *arch = ZipArchive::get_singleton(); - ERR_FAIL_COND(!arch); + ERR_FAIL_NULL(arch); arch->close_handle(zfile); zfile = nullptr; } diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index f8b640946c..1062a06238 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -106,8 +106,13 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } // todo - virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; } - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; } + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; } + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; } + + virtual bool _get_hidden_attribute(const String &p_file) override { return false; } + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; } + virtual bool _get_read_only_attribute(const String &p_file) override { return false; } + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; } virtual void close() override; diff --git a/core/io/image.cpp b/core/io/image.cpp index 7326563f18..204db687d8 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -2356,7 +2356,7 @@ void Image::initialize_data(const char **p_xpm) { } Color *colorptr = colormap.getptr(pixelstr); - ERR_FAIL_COND(!colorptr); + ERR_FAIL_NULL(colorptr); uint8_t pixel[4]; for (uint32_t i = 0; i < pixel_size; i++) { pixel[i] = CLAMP((*colorptr)[i] * 255, 0, 255); @@ -2646,23 +2646,23 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels switch (p_mode) { case COMPRESS_S3TC: { - ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE); + ERR_FAIL_NULL_V(_image_compress_bc_func, ERR_UNAVAILABLE); _image_compress_bc_func(this, p_channels); } break; case COMPRESS_ETC: { - ERR_FAIL_COND_V(!_image_compress_etc1_func, ERR_UNAVAILABLE); + ERR_FAIL_NULL_V(_image_compress_etc1_func, ERR_UNAVAILABLE); _image_compress_etc1_func(this); } break; case COMPRESS_ETC2: { - ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE); + ERR_FAIL_NULL_V(_image_compress_etc2_func, ERR_UNAVAILABLE); _image_compress_etc2_func(this, p_channels); } break; case COMPRESS_BPTC: { - ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE); + ERR_FAIL_NULL_V(_image_compress_bptc_func, ERR_UNAVAILABLE); _image_compress_bptc_func(this, p_channels); } break; case COMPRESS_ASTC: { - ERR_FAIL_COND_V(!_image_compress_astc_func, ERR_UNAVAILABLE); + ERR_FAIL_NULL_V(_image_compress_astc_func, ERR_UNAVAILABLE); _image_compress_astc_func(this, p_astc_format); } break; case COMPRESS_MAX: { @@ -3017,6 +3017,7 @@ ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr; ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr; ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr; ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr; +ImageMemLoadFunc Image::_ktx_mem_loader_func = nullptr; void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr; void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr; @@ -3488,6 +3489,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("load_webp_from_buffer", "buffer"), &Image::load_webp_from_buffer); ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer); ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer); + ClassDB::bind_method(D_METHOD("load_ktx_from_buffer", "buffer"), &Image::load_ktx_from_buffer); ClassDB::bind_method(D_METHOD("load_svg_from_buffer", "buffer", "scale"), &Image::load_svg_from_buffer, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("load_svg_from_string", "svg_str", "scale"), &Image::load_svg_from_string, DEFVAL(1.0)); @@ -3653,7 +3655,7 @@ void Image::bump_map_to_normal_map(float bump_scale) { const uint8_t *rp = data.ptr(); uint8_t *wp = result_image.ptrw(); - ERR_FAIL_COND(!rp); + ERR_FAIL_NULL(rp); unsigned char *write_ptr = wp; float *read_ptr = (float *)rp; @@ -3863,6 +3865,14 @@ Error Image::load_svg_from_string(const String &p_svg_str, float scale) { return load_svg_from_buffer(p_svg_str.to_utf8_buffer(), scale); } +Error Image::load_ktx_from_buffer(const Vector<uint8_t> &p_array) { + ERR_FAIL_NULL_V_MSG( + _ktx_mem_loader_func, + ERR_UNAVAILABLE, + "The KTX module isn't enabled. Recompile the Godot editor or export template binary with the `module_ktx_enabled=yes` SCons option."); + return _load_from_buffer(p_array, _ktx_mem_loader_func); +} + void Image::convert_rg_to_ra_rgba8() { ERR_FAIL_COND(format != FORMAT_RGBA8); ERR_FAIL_COND(!data.size()); @@ -3906,7 +3916,7 @@ Error Image::_load_from_buffer(const Vector<uint8_t> &p_array, ImageMemLoadFunc int buffer_size = p_array.size(); ERR_FAIL_COND_V(buffer_size == 0, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!p_loader, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_loader, ERR_INVALID_PARAMETER); const uint8_t *r = p_array.ptr(); diff --git a/core/io/image.h b/core/io/image.h index f877b00ee6..a21d05187b 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -150,6 +150,7 @@ public: static ImageMemLoadFunc _tga_mem_loader_func; static ImageMemLoadFunc _bmp_mem_loader_func; static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func; + static ImageMemLoadFunc _ktx_mem_loader_func; static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels); static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels); @@ -402,6 +403,7 @@ public: Error load_webp_from_buffer(const Vector<uint8_t> &p_array); Error load_tga_from_buffer(const Vector<uint8_t> &p_array); Error load_bmp_from_buffer(const Vector<uint8_t> &p_array); + Error load_ktx_from_buffer(const Vector<uint8_t> &p_array); Error load_svg_from_buffer(const Vector<uint8_t> &p_array, float scale = 1.0); Error load_svg_from_string(const String &p_svg_str, float scale = 1.0); diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 772f700916..254351897d 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -333,7 +333,7 @@ IP *(*IP::_create)() = nullptr; IP *IP::create() { ERR_FAIL_COND_V_MSG(singleton, nullptr, "IP singleton already exist."); - ERR_FAIL_COND_V(!_create, nullptr); + ERR_FAIL_NULL_V(_create, nullptr); return _create(); } diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 6ca7b0f831..bfef9ebeaf 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -640,7 +640,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } else { Object *obj = ClassDB::instantiate(str); - ERR_FAIL_COND_V(!obj, ERR_UNAVAILABLE); + ERR_FAIL_NULL_V(obj, ERR_UNAVAILABLE); ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); @@ -1576,7 +1576,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo buf += len; } Variant *v = d.getptr(E); - ERR_FAIL_COND_V(!v, ERR_BUG); + ERR_FAIL_NULL_V(v, ERR_BUG); err = encode_variant(*v, buf, len, p_full_objects, p_depth + 1); ERR_FAIL_COND_V(err, err); ERR_FAIL_COND_V(len % 4, ERR_BUG); diff --git a/core/io/packed_data_container.cpp b/core/io/packed_data_container.cpp index ce4edb18fe..11b0c69774 100644 --- a/core/io/packed_data_container.cpp +++ b/core/io/packed_data_container.cpp @@ -125,7 +125,7 @@ Variant PackedDataContainer::_get_at_ofs(uint32_t p_ofs, const uint8_t *p_buf, b uint32_t PackedDataContainer::_type_at_ofs(uint32_t p_ofs) const { ERR_FAIL_COND_V(p_ofs + 4 > (uint32_t)data.size(), 0); const uint8_t *rd = data.ptr(); - ERR_FAIL_COND_V(!rd, 0); + ERR_FAIL_NULL_V(rd, 0); const uint8_t *r = &rd[p_ofs]; uint32_t type = decode_uint32(r); @@ -135,7 +135,7 @@ uint32_t PackedDataContainer::_type_at_ofs(uint32_t p_ofs) const { int PackedDataContainer::_size(uint32_t p_ofs) const { ERR_FAIL_COND_V(p_ofs + 4 > (uint32_t)data.size(), 0); const uint8_t *rd = data.ptr(); - ERR_FAIL_COND_V(!rd, 0); + ERR_FAIL_NULL_V(rd, 0); const uint8_t *r = &rd[p_ofs]; uint32_t type = decode_uint32(r); @@ -156,7 +156,7 @@ Variant PackedDataContainer::_key_at_ofs(uint32_t p_ofs, const Variant &p_key, b const uint8_t *rd = data.ptr(); if (!rd) { err = true; - ERR_FAIL_COND_V(!rd, Variant()); + ERR_FAIL_NULL_V(rd, Variant()); } const uint8_t *r = &rd[p_ofs]; uint32_t type = decode_uint32(r); diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index 7383ab84df..86ebe32cb6 100644 --- a/core/io/packet_peer.h +++ b/core/io/packet_peer.h @@ -37,7 +37,6 @@ #include "core/extension/ext_wrappers.gen.inc" #include "core/object/gdvirtual.gen.inc" -#include "core/object/script_language.h" #include "core/variant/native_ptr.h" class PacketPeer : public RefCounted { diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 9b49cc3d8c..a7c715c318 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -205,7 +205,7 @@ Error PCKPacker::flush(bool p_verbose) { int header_padding = _get_pad(alignment, file->get_position()); for (int i = 0; i < header_padding; i++) { - file->store_8(Math::rand() % 256); + file->store_8(0); } int64_t file_base = file->get_position(); @@ -244,7 +244,7 @@ Error PCKPacker::flush(bool p_verbose) { int pad = _get_pad(alignment, file->get_position()); for (int j = 0; j < pad; j++) { - file->store_8(Math::rand() % 256); + file->store_8(0); } count += 1; diff --git a/core/io/remote_filesystem_client.cpp b/core/io/remote_filesystem_client.cpp index f22e442a34..1198810441 100644 --- a/core/io/remote_filesystem_client.cpp +++ b/core/io/remote_filesystem_client.cpp @@ -270,7 +270,7 @@ Error RemoteFilesystemClient::_synchronize_with_server(const String &p_host, int String file = temp_file_cache[i].path; if (temp_file_cache[i].server_modified_time == 0 || server_disconnected) { - // File was removed, or server disconnected before tranferring it. Since it's no longer valid, remove anyway. + // File was removed, or server disconnected before transferring it. Since it's no longer valid, remove anyway. _remove_file(file); continue; } diff --git a/core/io/remote_filesystem_client.h b/core/io/remote_filesystem_client.h index 42eba98eb1..fcb5c1cfc3 100644 --- a/core/io/remote_filesystem_client.h +++ b/core/io/remote_filesystem_client.h @@ -44,8 +44,8 @@ protected: String _get_cache_path() { return cache_path; } struct FileCache { String path; // Local path (as in "folder/to/file.png") - uint64_t server_modified_time; // MD5 checksum. - uint64_t modified_time; + uint64_t server_modified_time = 0; // MD5 checksum. + uint64_t modified_time = 0; }; virtual bool _is_configured() { return !cache_path.is_empty(); } // Can be re-implemented per platform. If so, feel free to ignore get_cache_path() diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 07677337b4..68cdeabac7 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -239,6 +239,7 @@ void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource List<PropertyInfo> plist; get_property_list(&plist); + reset_local_to_scene(); local_scene = p_for_scene; for (const PropertyInfo &E : plist) { @@ -382,6 +383,10 @@ void Resource::setup_local_to_scene() { emit_signal(SNAME("setup_local_to_scene_requested")); } +void Resource::reset_local_to_scene() { + // Restores the state as if setup_local_to_scene() hadn't been called. +} + Node *(*Resource::_get_local_scene_func)() = nullptr; void (*Resource::_update_configuration_warning)() = nullptr; diff --git a/core/io/resource.h b/core/io/resource.h index af8c275a1c..f848bdba99 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -80,6 +80,8 @@ protected: void _set_path(const String &p_path); void _take_over_path(const String &p_path); + virtual void reset_local_to_scene(); + public: static Node *(*_get_local_scene_func)(); //used by editor static void (*_update_configuration_warning)(); //used by editor diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 551d3268b8..ea97e5ecce 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -36,6 +36,7 @@ #include "core/io/image.h" #include "core/io/marshalls.h" #include "core/io/missing_resource.h" +#include "core/object/script_language.h" #include "core/version.h" //#define print_bl(m_what) print_line(m_what) diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 1fe662b1fa..f7915261af 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "core/io/file_access.h" #include "core/io/resource_importer.h" +#include "core/object/script_language.h" #include "core/os/condition_variable.h" #include "core/os/os.h" #include "core/string/print_string.h" @@ -275,10 +276,10 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin #ifdef TOOLS_ENABLED Ref<FileAccess> file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES); - ERR_FAIL_COND_V_MSG(!file_check->file_exists(p_path), Ref<Resource>(), "Resource file not found: " + p_path + "."); + ERR_FAIL_COND_V_MSG(!file_check->file_exists(p_path), Ref<Resource>(), vformat("Resource file not found: %s (expected type: %s)", p_path, p_type_hint)); #endif - ERR_FAIL_V_MSG(Ref<Resource>(), "No loader found for resource: " + p_path + "."); + ERR_FAIL_V_MSG(Ref<Resource>(), vformat("No loader found for resource: %s (expected type: %s)", p_path, p_type_hint)); } void ResourceLoader::_thread_load_function(void *p_userdata) { @@ -1113,7 +1114,7 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) { Object *obj = ClassDB::instantiate(ibt); - ERR_FAIL_COND_V_MSG(obj == nullptr, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + "."); + ERR_FAIL_NULL_V_MSG(obj, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + "."); Ref<ResourceFormatLoader> crl = Object::cast_to<ResourceFormatLoader>(obj); crl->set_script(s); diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 2701caa3f4..0c7d6c0feb 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -33,7 +33,6 @@ #include "core/io/resource.h" #include "core/object/gdvirtual.gen.inc" -#include "core/object/script_language.h" #include "core/object/worker_thread_pool.h" #include "core/os/semaphore.h" #include "core/os/thread.h" diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 6e377847a8..564a54b913 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -241,7 +241,7 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) { Object *obj = ClassDB::instantiate(ibt); - ERR_FAIL_COND_V_MSG(obj == nullptr, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + "."); + ERR_FAIL_NULL_V_MSG(obj, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + "."); Ref<ResourceFormatSaver> crl = Object::cast_to<ResourceFormatSaver>(obj); crl->set_script(s); diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h index 572742d129..4828df297a 100644 --- a/core/io/resource_saver.h +++ b/core/io/resource_saver.h @@ -33,7 +33,6 @@ #include "core/io/resource.h" #include "core/object/gdvirtual.gen.inc" -#include "core/object/script_language.h" class ResourceFormatSaver : public RefCounted { GDCLASS(ResourceFormatSaver, RefCounted); diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index ba11144e33..29cdb82615 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -35,7 +35,6 @@ #include "core/extension/ext_wrappers.gen.inc" #include "core/object/gdvirtual.gen.inc" -#include "core/object/script_language.h" #include "core/variant/native_ptr.h" class StreamPeer : public RefCounted { diff --git a/core/io/stream_peer_gzip.cpp b/core/io/stream_peer_gzip.cpp index 74216f3cff..4daa71a22a 100644 --- a/core/io/stream_peer_gzip.cpp +++ b/core/io/stream_peer_gzip.cpp @@ -102,7 +102,7 @@ Error StreamPeerGZIP::_start(bool p_compress, bool p_is_deflate, int buffer_size } Error StreamPeerGZIP::_process(uint8_t *p_dst, int p_dst_size, const uint8_t *p_src, int p_src_size, int &r_consumed, int &r_out, bool p_close) { - ERR_FAIL_COND_V(!ctx, ERR_UNCONFIGURED); + ERR_FAIL_NULL_V(ctx, ERR_UNCONFIGURED); z_stream &strm = *(z_stream *)ctx; strm.avail_in = p_src_size; strm.avail_out = p_dst_size; @@ -132,7 +132,7 @@ Error StreamPeerGZIP::put_data(const uint8_t *p_data, int p_bytes) { } Error StreamPeerGZIP::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { - ERR_FAIL_COND_V(!ctx, ERR_UNCONFIGURED); + ERR_FAIL_NULL_V(ctx, ERR_UNCONFIGURED); ERR_FAIL_COND_V(p_bytes < 0, ERR_INVALID_PARAMETER); // Ensure we have enough space in temporary buffer. diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index 958734addf..faf7d75172 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -336,7 +336,7 @@ uint64_t XMLParser::get_node_offset() const { } Error XMLParser::seek(uint64_t p_pos) { - ERR_FAIL_COND_V(!data, ERR_FILE_EOF); + ERR_FAIL_NULL_V(data, ERR_FILE_EOF); ERR_FAIL_COND_V(p_pos >= length, ERR_FILE_EOF); P = data + p_pos; @@ -474,7 +474,7 @@ Error XMLParser::open_buffer(const Vector<uint8_t> &p_buffer) { Error XMLParser::_open_buffer(const uint8_t *p_buffer, size_t p_size) { ERR_FAIL_COND_V(p_size == 0, ERR_INVALID_DATA); - ERR_FAIL_COND_V(!p_buffer, ERR_INVALID_DATA); + ERR_FAIL_NULL_V(p_buffer, ERR_INVALID_DATA); if (data_copy) { memdelete_arr(data_copy); diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp index a0e6bd62de..a89c6253f1 100644 --- a/core/io/zip_io.cpp +++ b/core/io/zip_io.cpp @@ -74,7 +74,7 @@ int godot_unzip_locate_file(unzFile p_zip_file, String p_filepath, bool p_case_s void *zipio_open(voidpf opaque, const char *p_fname, int mode) { Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque); - ERR_FAIL_COND_V(fa == nullptr, nullptr); + ERR_FAIL_NULL_V(fa, nullptr); String fname; fname.parse_utf8(p_fname); @@ -100,7 +100,7 @@ void *zipio_open(voidpf opaque, const char *p_fname, int mode) { uLong zipio_read(voidpf opaque, voidpf stream, void *buf, uLong size) { Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque); - ERR_FAIL_COND_V(fa == nullptr, 0); + ERR_FAIL_NULL_V(fa, 0); ERR_FAIL_COND_V(fa->is_null(), 0); return (*fa)->get_buffer((uint8_t *)buf, size); @@ -108,7 +108,7 @@ uLong zipio_read(voidpf opaque, voidpf stream, void *buf, uLong size) { uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size) { Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque); - ERR_FAIL_COND_V(fa == nullptr, 0); + ERR_FAIL_NULL_V(fa, 0); ERR_FAIL_COND_V(fa->is_null(), 0); (*fa)->store_buffer((uint8_t *)buf, size); @@ -117,7 +117,7 @@ uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size) { long zipio_tell(voidpf opaque, voidpf stream) { Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque); - ERR_FAIL_COND_V(fa == nullptr, 0); + ERR_FAIL_NULL_V(fa, 0); ERR_FAIL_COND_V(fa->is_null(), 0); return (*fa)->get_position(); @@ -125,7 +125,7 @@ long zipio_tell(voidpf opaque, voidpf stream) { long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque); - ERR_FAIL_COND_V(fa == nullptr, 0); + ERR_FAIL_NULL_V(fa, 0); ERR_FAIL_COND_V(fa->is_null(), 0); uint64_t pos = offset; @@ -146,7 +146,7 @@ long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { int zipio_close(voidpf opaque, voidpf stream) { Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque); - ERR_FAIL_COND_V(fa == nullptr, 0); + ERR_FAIL_NULL_V(fa, 0); ERR_FAIL_COND_V(fa->is_null(), 0); fa->unref(); @@ -155,7 +155,7 @@ int zipio_close(voidpf opaque, voidpf stream) { int zipio_testerror(voidpf opaque, voidpf stream) { Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque); - ERR_FAIL_COND_V(fa == nullptr, 1); + ERR_FAIL_NULL_V(fa, 1); ERR_FAIL_COND_V(fa->is_null(), 0); return (fa->is_valid() && (*fa)->get_error() != OK) ? 1 : 0; diff --git a/core/math/a_star.h b/core/math/a_star.h index fc4bb09f03..0758500c8a 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -33,7 +33,6 @@ #include "core/object/gdvirtual.gen.inc" #include "core/object/ref_counted.h" -#include "core/object/script_language.h" #include "core/templates/oa_hash_map.h" /** diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp index 63f7c80bdd..9ba4c2ff9a 100644 --- a/core/math/a_star_grid_2d.cpp +++ b/core/math/a_star_grid_2d.cpp @@ -194,6 +194,38 @@ real_t AStarGrid2D::get_point_weight_scale(const Vector2i &p_id) const { return GET_POINT_UNCHECKED(p_id).weight_scale; } +void AStarGrid2D::fill_solid_region(const Rect2i &p_region, bool p_solid) { + ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method."); + + Rect2i safe_region = p_region.intersection(region); + int from_x = safe_region.get_position().x; + int from_y = safe_region.get_position().y; + int end_x = safe_region.get_end().x; + int end_y = safe_region.get_end().y; + + for (int x = from_x; x < end_x; x++) { + for (int y = from_y; y < end_y; y++) { + GET_POINT_UNCHECKED(Vector2i(x, y)).solid = p_solid; + } + } +} + +void AStarGrid2D::fill_weight_scale_region(const Rect2i &p_region, real_t p_weight_scale) { + ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method."); + ERR_FAIL_COND_MSG(p_weight_scale < 0.0, vformat("Can't set point's weight scale less than 0.0: %f.", p_weight_scale)); + + Rect2i safe_region = p_region.intersection(region); + int from_x = safe_region.get_position().x; + int from_y = safe_region.get_position().y; + int end_x = safe_region.get_end().x; + int end_y = safe_region.get_end().y; + for (int x = from_x; x < end_x; x++) { + for (int y = from_y; y < end_y; y++) { + GET_POINT_UNCHECKED(Vector2i(x, y)).weight_scale = p_weight_scale; + } + } +} + AStarGrid2D::Point *AStarGrid2D::_jump(Point *p_from, Point *p_to) { if (!p_to || p_to->solid) { return nullptr; @@ -606,6 +638,8 @@ void AStarGrid2D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_point_solid", "id"), &AStarGrid2D::is_point_solid); ClassDB::bind_method(D_METHOD("set_point_weight_scale", "id", "weight_scale"), &AStarGrid2D::set_point_weight_scale); ClassDB::bind_method(D_METHOD("get_point_weight_scale", "id"), &AStarGrid2D::get_point_weight_scale); + ClassDB::bind_method(D_METHOD("fill_solid_region", "region", "solid"), &AStarGrid2D::fill_solid_region, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("fill_weight_scale_region", "region", "weight_scale"), &AStarGrid2D::fill_weight_scale_region); ClassDB::bind_method(D_METHOD("clear"), &AStarGrid2D::clear); ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStarGrid2D::get_point_position); diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h index 50df58e0e9..ecc9bb01f9 100644 --- a/core/math/a_star_grid_2d.h +++ b/core/math/a_star_grid_2d.h @@ -33,7 +33,6 @@ #include "core/object/gdvirtual.gen.inc" #include "core/object/ref_counted.h" -#include "core/object/script_language.h" #include "core/templates/list.h" #include "core/templates/local_vector.h" @@ -177,6 +176,9 @@ public: void set_point_weight_scale(const Vector2i &p_id, real_t p_weight_scale); real_t get_point_weight_scale(const Vector2i &p_id) const; + void fill_solid_region(const Rect2i &p_region, bool p_solid = true); + void fill_weight_scale_region(const Rect2i &p_region, real_t p_weight_scale); + void clear(); Vector2 get_point_position(const Vector2i &p_id) const; diff --git a/core/math/delaunay_3d.h b/core/math/delaunay_3d.h index 55923e0133..7df8c37e3c 100644 --- a/core/math/delaunay_3d.h +++ b/core/math/delaunay_3d.h @@ -105,8 +105,8 @@ class Delaunay3D { }; _FORCE_INLINE_ static void circum_sphere_compute(const Vector3 *p_points, Simplex *p_simplex) { - // the only part in the algorithm where there may be precision errors is this one, so ensure that - // we do it as maximum precision as possible + // The only part in the algorithm where there may be precision errors is this one, + // so ensure that we do it with the maximum precision possible. R128 v0_x = p_points[p_simplex->points[0]].x; R128 v0_y = p_points[p_simplex->points[0]].y; @@ -121,7 +121,7 @@ class Delaunay3D { R128 v3_y = p_points[p_simplex->points[3]].y; R128 v3_z = p_points[p_simplex->points[3]].z; - //Create the rows of our "unrolled" 3x3 matrix + // Create the rows of our "unrolled" 3x3 matrix. R128 row1_x = v1_x - v0_x; R128 row1_y = v1_y - v0_y; R128 row1_z = v1_z - v0_z; @@ -138,10 +138,10 @@ class Delaunay3D { R128 sq_lenght2 = row2_x * row2_x + row2_y * row2_y + row2_z * row2_z; R128 sq_lenght3 = row3_x * row3_x + row3_y * row3_y + row3_z * row3_z; - //Compute the determinant of said matrix + // Compute the determinant of said matrix. R128 determinant = row1_x * (row2_y * row3_z - row3_y * row2_z) - row2_x * (row1_y * row3_z - row3_y * row1_z) + row3_x * (row1_y * row2_z - row2_y * row1_z); - // Compute the volume of the tetrahedron, and precompute a scalar quantity for re-use in the formula + // Compute the volume of the tetrahedron, and precompute a scalar quantity for reuse in the formula. R128 volume = determinant / R128(6.f); R128 i12volume = R128(1.f) / (volume * R128(12.f)); @@ -149,8 +149,7 @@ class Delaunay3D { R128 center_y = v0_y + i12volume * (-(row2_x * row3_z - row3_x * row2_z) * sq_lenght1 + (row1_x * row3_z - row3_x * row1_z) * sq_lenght2 - (row1_x * row2_z - row2_x * row1_z) * sq_lenght3); R128 center_z = v0_z + i12volume * ((row2_x * row3_y - row3_x * row2_y) * sq_lenght1 - (row1_x * row3_y - row3_x * row1_y) * sq_lenght2 + (row1_x * row2_y - row2_x * row1_y) * sq_lenght3); - //Once we know the center, the radius is clearly the distance to any vertex - + // Once we know the center, the radius is clearly the distance to any vertex. R128 rel1_x = center_x - v0_x; R128 rel1_y = center_y - v0_y; R128 rel1_z = center_z - v0_z; diff --git a/core/math/disjoint_set.h b/core/math/disjoint_set.h index 78ae77e72c..2ece991fea 100644 --- a/core/math/disjoint_set.h +++ b/core/math/disjoint_set.h @@ -134,7 +134,7 @@ void DisjointSet<T, H, C, AL>::get_representatives(Vector<T> &out_representative template <typename T, class H, class C, class AL> void DisjointSet<T, H, C, AL>::get_members(Vector<T> &out_members, T representative) { typename MapT::Iterator rep_itr = elements.find(representative); - ERR_FAIL_COND(rep_itr == nullptr); + ERR_FAIL_NULL(rep_itr); Element *rep_element = rep_itr->value; ERR_FAIL_COND(rep_element->parent != rep_element); diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h index 21b5340aaa..dbc1cb31de 100644 --- a/core/math/dynamic_bvh.h +++ b/core/math/dynamic_bvh.h @@ -190,7 +190,7 @@ private: _FORCE_INLINE_ bool is_internal() const { return (!is_leaf()); } _FORCE_INLINE_ int get_index_in_parent() const { - ERR_FAIL_COND_V(!parent, 0); + ERR_FAIL_NULL_V(parent, 0); return (parent->children[1] == this) ? 1 : 0; } void get_max_depth(int depth, int &maxdepth) { diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index f96d3a909f..934c75b5d3 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -88,6 +88,17 @@ public: static _ALWAYS_INLINE_ double atan2(double p_y, double p_x) { return ::atan2(p_y, p_x); } static _ALWAYS_INLINE_ float atan2(float p_y, float p_x) { return ::atan2f(p_y, p_x); } + static _ALWAYS_INLINE_ double asinh(double p_x) { return ::asinh(p_x); } + static _ALWAYS_INLINE_ float asinh(float p_x) { return ::asinhf(p_x); } + + // Always does clamping so always safe to use. + static _ALWAYS_INLINE_ double acosh(double p_x) { return p_x < 1 ? 0 : ::acosh(p_x); } + static _ALWAYS_INLINE_ float acosh(float p_x) { return p_x < 1 ? 0 : ::acoshf(p_x); } + + // Always does clamping so always safe to use. + static _ALWAYS_INLINE_ double atanh(double p_x) { return p_x <= -1 ? -INFINITY : (p_x >= 1 ? INFINITY : ::atanh(p_x)); } + static _ALWAYS_INLINE_ float atanh(float p_x) { return p_x <= -1 ? -INFINITY : (p_x >= 1 ? INFINITY : ::atanhf(p_x)); } + static _ALWAYS_INLINE_ double sqrt(double p_x) { return ::sqrt(p_x); } static _ALWAYS_INLINE_ float sqrt(float p_x) { return ::sqrtf(p_x); } diff --git a/core/object/callable_method_pointer.cpp b/core/object/callable_method_pointer.cpp index b53985e6b7..ed400788b1 100644 --- a/core/object/callable_method_pointer.cpp +++ b/core/object/callable_method_pointer.cpp @@ -38,13 +38,10 @@ bool CallableCustomMethodPointerBase::compare_equal(const CallableCustom *p_a, c return false; } - for (uint32_t i = 0; i < a->comp_size; i++) { - if (a->comp_ptr[i] != b->comp_ptr[i]) { - return false; - } - } - - return true; + // Avoid sorting by memory address proximity, which leads to unpredictable performance over time + // due to the reuse of old addresses for newer objects. Use byte-wise comparison to leverage the + // backwards encoding of little-endian systems as a way to decouple spatiality and time. + return memcmp(a->comp_ptr, b->comp_ptr, a->comp_size * 4) == 0; } bool CallableCustomMethodPointerBase::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) { @@ -55,15 +52,8 @@ bool CallableCustomMethodPointerBase::compare_less(const CallableCustom *p_a, co return a->comp_size < b->comp_size; } - for (uint32_t i = 0; i < a->comp_size; i++) { - if (a->comp_ptr[i] == b->comp_ptr[i]) { - continue; - } - - return a->comp_ptr[i] < b->comp_ptr[i]; - } - - return false; + // See note in compare_equal(). + return memcmp(a->comp_ptr, b->comp_ptr, a->comp_size * 4) < 0; } CallableCustom::CompareEqualFunc CallableCustomMethodPointerBase::get_compare_equal_func() const { diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index cc4a29164d..100cb8e6a2 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -53,7 +53,7 @@ MethodDefinition D_METHODP(const char *p_name, const char *const **p_args, uint3 #endif ClassDB::APIType ClassDB::current_api = API_CORE; -HashMap<ClassDB::APIType, uint64_t> ClassDB::api_hashes_cache; +HashMap<ClassDB::APIType, uint32_t> ClassDB::api_hashes_cache; void ClassDB::set_current_api(APIType p_api) { DEV_ASSERT(!api_hashes_cache.has(p_api)); // This API type may not be suitable for caching of hash if it can change later. @@ -144,7 +144,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_COND_V_MSG(!ti, StringName(), "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_NULL_V_MSG(ti, StringName(), "Cannot get class '" + String(p_class) + "'."); return ti->inherits; } @@ -159,11 +159,11 @@ ClassDB::APIType ClassDB::get_api_type(const StringName &p_class) { ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_COND_V_MSG(!ti, API_NONE, "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_NULL_V_MSG(ti, API_NONE, "Cannot get class '" + String(p_class) + "'."); return ti->api; } -uint64_t ClassDB::get_api_hash(APIType p_api) { +uint32_t ClassDB::get_api_hash(APIType p_api) { OBJTYPE_RLOCK; #ifdef DEBUG_METHODS_ENABLED @@ -180,7 +180,7 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { for (const StringName &E : class_list) { ClassInfo *t = classes.getptr(E); - ERR_FAIL_COND_V_MSG(!t, 0, "Cannot get class '" + String(E) + "'."); + ERR_FAIL_NULL_V_MSG(t, 0, "Cannot get class '" + String(E) + "'."); if (t->api != p_api || !t->exposed) { continue; } @@ -278,7 +278,7 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { for (const StringName &F : snames) { PropertySetGet *psg = t->property_setget.getptr(F); - ERR_FAIL_COND_V(!psg, 0); + ERR_FAIL_NULL_V(psg, 0); hash = hash_murmur3_one_64(F.hash(), hash); hash = hash_murmur3_one_64(psg->setter.hash(), hash); @@ -336,9 +336,9 @@ Object *ClassDB::instantiate(const StringName &p_class) { ti = classes.getptr(compat_classes[p_class]); } } - ERR_FAIL_COND_V_MSG(!ti, nullptr, "Cannot get class '" + String(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_COND_V_MSG(!ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated."); + ERR_FAIL_NULL_V_MSG(ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated."); } #ifdef TOOLS_ENABLED if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { @@ -354,7 +354,7 @@ Object *ClassDB::instantiate(const StringName &p_class) { } void ClassDB::set_object_extension_instance(Object *p_object, const StringName &p_class, GDExtensionClassInstancePtr p_instance) { - ERR_FAIL_COND(!p_object); + ERR_FAIL_NULL(p_object); ClassInfo *ti; { OBJTYPE_RLOCK; @@ -364,9 +364,9 @@ void ClassDB::set_object_extension_instance(Object *p_object, const StringName & ti = classes.getptr(compat_classes[p_class]); } } - ERR_FAIL_COND_MSG(!ti, "Cannot get class '" + String(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_COND_MSG(!ti->gdextension, "Class '" + String(p_class) + "' has no native extension."); + ERR_FAIL_NULL_MSG(ti->gdextension, "Class '" + String(p_class) + "' has no native extension."); } p_object->_extension = ti->gdextension; @@ -377,7 +377,7 @@ bool ClassDB::can_instantiate(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'."); #ifdef TOOLS_ENABLED if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { return false; @@ -621,7 +621,7 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName ClassInfo *type = classes.getptr(p_class); - ERR_FAIL_COND(!type); + ERR_FAIL_NULL(type); if (type->constant_map.has(p_name)) { ERR_FAIL(); @@ -790,7 +790,7 @@ void ClassDB::set_method_error_return_values(const StringName &p_class, const St #ifdef DEBUG_METHODS_ENABLED ClassInfo *type = classes.getptr(p_class); - ERR_FAIL_COND(!type); + ERR_FAIL_NULL(type); type->method_error_values[p_method] = p_values; #endif @@ -800,7 +800,7 @@ Vector<Error> ClassDB::get_method_error_return_values(const StringName &p_class, #ifdef DEBUG_METHODS_ENABLED ClassInfo *type = classes.getptr(p_class); - ERR_FAIL_COND_V(!type, Vector<Error>()); + ERR_FAIL_NULL_V(type, Vector<Error>()); if (!type->method_error_values.has(p_method)) { return Vector<Error>(); @@ -853,7 +853,7 @@ void ClassDB::add_signal(const StringName &p_class, const MethodInfo &p_signal) OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); - ERR_FAIL_COND(!type); + ERR_FAIL_NULL(type); StringName sname = p_signal.name; @@ -872,7 +872,7 @@ void ClassDB::get_signal_list(const StringName &p_class, List<MethodInfo> *p_sig OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); - ERR_FAIL_COND(!type); + ERR_FAIL_NULL(type); ClassInfo *check = type; @@ -926,7 +926,7 @@ bool ClassDB::get_signal(const StringName &p_class, const StringName &p_signal, void ClassDB::add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix, int p_indent_depth) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); - ERR_FAIL_COND(!type); + ERR_FAIL_NULL(type); String prefix = p_prefix; if (p_indent_depth > 0) { @@ -939,7 +939,7 @@ void ClassDB::add_property_group(const StringName &p_class, const String &p_name void ClassDB::add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix, int p_indent_depth) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); - ERR_FAIL_COND(!type); + ERR_FAIL_NULL(type); String prefix = p_prefix; if (p_indent_depth > 0) { @@ -956,7 +956,7 @@ void ClassDB::add_property_array_count(const StringName &p_class, const String & void ClassDB::add_property_array(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); - ERR_FAIL_COND(!type); + ERR_FAIL_NULL(type); type->property_list.push_back(PropertyInfo(Variant::NIL, p_path, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, p_array_element_prefix)); } @@ -967,14 +967,14 @@ void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinf ClassInfo *type = classes.getptr(p_class); lock.read_unlock(); - ERR_FAIL_COND(!type); + ERR_FAIL_NULL(type); MethodBind *mb_set = nullptr; if (p_setter) { mb_set = get_method(p_class, p_setter); #ifdef DEBUG_METHODS_ENABLED - ERR_FAIL_COND_MSG(!mb_set, "Invalid setter '" + p_class + "::" + p_setter + "' for property '" + p_pinfo.name + "'."); + ERR_FAIL_NULL_MSG(mb_set, "Invalid setter '" + p_class + "::" + p_setter + "' for property '" + p_pinfo.name + "'."); int exp_args = 1 + (p_index >= 0 ? 1 : 0); ERR_FAIL_COND_MSG(mb_set->get_argument_count() != exp_args, "Invalid function for setter '" + p_class + "::" + p_setter + " for property '" + p_pinfo.name + "'."); @@ -986,7 +986,7 @@ 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_COND_MSG(!mb_get, "Invalid getter '" + p_class + "::" + p_getter + "' for property '" + p_pinfo.name + "'."); + ERR_FAIL_NULL_MSG(mb_get, "Invalid getter '" + p_class + "::" + p_getter + "' for property '" + p_pinfo.name + "'."); int exp_args = 0 + (p_index >= 0 ? 1 : 0); ERR_FAIL_COND_MSG(mb_get->get_argument_count() != exp_args, "Invalid function for getter '" + p_class + "::" + p_getter + "' for property: '" + p_pinfo.name + "'."); @@ -1031,7 +1031,7 @@ void ClassDB::add_linked_property(const StringName &p_class, const String &p_pro #ifdef TOOLS_ENABLED OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); - ERR_FAIL_COND(!type); + ERR_FAIL_NULL(type); ERR_FAIL_COND(!type->property_map.has(p_property)); ERR_FAIL_COND(!type->property_map.has(p_linked_property)); @@ -1306,7 +1306,7 @@ void ClassDB::set_method_flags(const StringName &p_class, const StringName &p_me OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; - ERR_FAIL_COND(!check); + ERR_FAIL_NULL(check); ERR_FAIL_COND(!check->method_map.has(p_method)); check->method_map[p_method]->set_hint_flags(p_flags); } @@ -1374,7 +1374,7 @@ MethodBind *ClassDB::_bind_vararg_method(MethodBind *p_bind, const StringName &p ClassInfo *type = classes.getptr(instance_type); if (!type) { memdelete(bind); - ERR_FAIL_COND_V(!type, nullptr); + ERR_FAIL_NULL_V(type, nullptr); } if (p_compatibility) { @@ -1406,7 +1406,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, bool p_ #endif OBJTYPE_WLOCK; - ERR_FAIL_COND_V(!p_bind, nullptr); + ERR_FAIL_NULL_V(p_bind, nullptr); p_bind->set_name(mdname); String instance_type = p_bind->get_instance_class(); @@ -1532,7 +1532,7 @@ bool ClassDB::is_class_enabled(const StringName &p_class) { } } - ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'."); return !ti->disabled; } @@ -1540,7 +1540,7 @@ bool ClassDB::is_class_exposed(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); - ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'."); return ti->exposed; } @@ -1661,19 +1661,34 @@ void ClassDB::register_extension_class(ObjectGDExtension *p_extension) { c.name = p_extension->class_name; c.is_virtual = p_extension->is_virtual; if (!p_extension->is_abstract) { - c.creation_func = parent->creation_func; + // Find the closest ancestor which is either non-abstract or native (or both). + ClassInfo *concrete_ancestor = parent; + while (concrete_ancestor->creation_func == nullptr && + concrete_ancestor->inherits_ptr != nullptr && + 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)); + c.creation_func = concrete_ancestor->creation_func; } c.inherits = parent->name; c.class_ptr = parent->class_ptr; c.inherits_ptr = parent; - c.exposed = true; + c.exposed = p_extension->is_exposed; + if (c.exposed) { + // The parent classes should be exposed if it has an exposed child class. + while (parent && !parent->exposed) { + parent->exposed = true; + parent = classes.getptr(parent->name); + } + } classes[p_extension->class_name] = c; } void ClassDB::unregister_extension_class(const StringName &p_class) { ClassInfo *c = classes.getptr(p_class); - ERR_FAIL_COND_MSG(!c, "Class " + p_class + "does not exist"); + ERR_FAIL_NULL_MSG(c, "Class '" + String(p_class) + "' does not exist."); for (KeyValue<StringName, MethodBind *> &F : c->method_map) { memdelete(F.value); } diff --git a/core/object/class_db.h b/core/object/class_db.h index ce64336a45..b112cddf38 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -155,7 +155,7 @@ public: #endif static APIType current_api; - static HashMap<APIType, uint64_t> api_hashes_cache; + static HashMap<APIType, uint32_t> api_hashes_cache; static void _add_class2(const StringName &p_class, const StringName &p_inherits); @@ -187,9 +187,10 @@ public: template <class T> static void register_class(bool p_virtual = false) { GLOBAL_LOCK_FUNCTION; + static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); - ERR_FAIL_COND(!t); + ERR_FAIL_NULL(t); t->creation_func = &creator<T>; t->exposed = true; t->is_virtual = p_virtual; @@ -201,15 +202,31 @@ public: template <class T> static void register_abstract_class() { GLOBAL_LOCK_FUNCTION; + static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); - ERR_FAIL_COND(!t); + ERR_FAIL_NULL(t); t->exposed = true; t->class_ptr = T::get_class_ptr_static(); t->api = current_api; //nothing } + template <class T> + static void register_internal_class() { + GLOBAL_LOCK_FUNCTION; + static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS."); + T::initialize_class(); + ClassInfo *t = classes.getptr(T::get_class_static()); + ERR_FAIL_NULL(t); + t->creation_func = &creator<T>; + t->exposed = false; + t->is_virtual = false; + t->class_ptr = T::get_class_ptr_static(); + t->api = current_api; + T::register_custom_data_to_otdb(); + } + static void register_extension_class(ObjectGDExtension *p_extension); static void unregister_extension_class(const StringName &p_class); @@ -221,9 +238,10 @@ public: template <class T> static void register_custom_instance_class() { GLOBAL_LOCK_FUNCTION; + static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); - ERR_FAIL_COND(!t); + ERR_FAIL_NULL(t); t->creation_func = &_create_ptr_func<T>; t->exposed = true; t->class_ptr = T::get_class_ptr_static(); @@ -246,7 +264,7 @@ public: static APIType get_api_type(const StringName &p_class); - static uint64_t get_api_hash(APIType p_api); + static uint32_t get_api_hash(APIType p_api); template <typename> struct member_function_traits; @@ -329,7 +347,7 @@ public: GLOBAL_LOCK_FUNCTION; MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant); - ERR_FAIL_COND_V(!bind, nullptr); + ERR_FAIL_NULL_V(bind, nullptr); if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) { bind->set_return_type_is_raw_object_ptr(true); @@ -342,7 +360,7 @@ public: GLOBAL_LOCK_FUNCTION; MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant); - ERR_FAIL_COND_V(!bind, nullptr); + ERR_FAIL_NULL_V(bind, nullptr); if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) { bind->set_return_type_is_raw_object_ptr(true); @@ -480,6 +498,10 @@ _FORCE_INLINE_ Vector<Error> errarray(P... p_args) { if (m_class::_class_is_enabled) { \ ::ClassDB::register_abstract_class<m_class>(); \ } +#define GDREGISTER_INTERNAL_CLASS(m_class) \ + if (m_class::_class_is_enabled) { \ + ::ClassDB::register_internal_class<m_class>(); \ + } #define GDREGISTER_NATIVE_STRUCT(m_class, m_code) ClassDB::register_native_struct(#m_class, m_code, sizeof(m_class)) diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py index 5be9650b32..38682d6d92 100644 --- a/core/object/make_virtuals.py +++ b/core/object/make_virtuals.py @@ -160,6 +160,8 @@ def run(target, source, env): #ifndef GDVIRTUAL_GEN_H #define GDVIRTUAL_GEN_H +#include "core/object/script_instance.h" + """ diff --git a/core/object/object.cpp b/core/object/object.cpp index 4d19a2c75b..3fd7fe36e0 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -485,20 +485,22 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons if (_extension) { const ObjectGDExtension *current_extension = _extension; while (current_extension) { - p_list->push_back(PropertyInfo(Variant::NIL, current_extension->class_name, PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); + p_list->push_back(PropertyInfo(Variant::NIL, current_extension->class_name, PROPERTY_HINT_NONE, current_extension->class_name, PROPERTY_USAGE_CATEGORY)); + ClassDB::get_property_list(current_extension->class_name, p_list, true, this); - current_extension = current_extension->parent; - } - } - if (_extension && _extension->get_property_list) { - uint32_t pcount; - const GDExtensionPropertyInfo *pinfo = _extension->get_property_list(_extension_instance, &pcount); - for (uint32_t i = 0; i < pcount; i++) { - p_list->push_back(PropertyInfo(pinfo[i])); - } - if (_extension->free_property_list) { - _extension->free_property_list(_extension_instance, pinfo); + if (current_extension->get_property_list) { + uint32_t pcount; + const GDExtensionPropertyInfo *pinfo = current_extension->get_property_list(_extension_instance, &pcount); + for (uint32_t i = 0; i < pcount; i++) { + p_list->push_back(PropertyInfo(pinfo[i])); + } + if (current_extension->free_property_list) { + current_extension->free_property_list(_extension_instance, pinfo); + } + } + + current_extension = current_extension->parent; } } @@ -524,6 +526,31 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons void Object::validate_property(PropertyInfo &p_property) const { _validate_propertyv(p_property); + + if (_extension && _extension->validate_property) { + // GDExtension uses a StringName rather than a String for property name. + StringName prop_name = p_property.name; + GDExtensionPropertyInfo gdext_prop = { + (GDExtensionVariantType)p_property.type, + &prop_name, + &p_property.class_name, + (uint32_t)p_property.hint, + &p_property.hint_string, + p_property.usage, + }; + if (_extension->validate_property(_extension_instance, &gdext_prop)) { + p_property.type = (Variant::Type)gdext_prop.type; + p_property.name = *reinterpret_cast<StringName *>(gdext_prop.name); + p_property.class_name = *reinterpret_cast<StringName *>(gdext_prop.class_name); + p_property.hint = (PropertyHint)gdext_prop.hint; + p_property.hint_string = *reinterpret_cast<String *>(gdext_prop.hint_string); + p_property.usage = gdext_prop.usage; + }; + } + + if (script_instance) { // Call it last to allow user altering already validated properties. + script_instance->validate_property(p_property); + } } bool Object::property_can_revert(const StringName &p_name) const { @@ -793,14 +820,30 @@ Variant Object::call_const(const StringName &p_method, const Variant **p_args, i } void Object::notification(int p_notification, bool p_reversed) { - _notificationv(p_notification, p_reversed); + if (p_reversed) { + if (script_instance) { + script_instance->notification(p_notification, p_reversed); + } + } else { + _notificationv(p_notification, p_reversed); + } - if (script_instance) { - script_instance->notification(p_notification); + if (_extension) { + if (_extension->notification2) { + _extension->notification2(_extension_instance, p_notification, static_cast<GDExtensionBool>(p_reversed)); +#ifndef DISABLE_DEPRECATED + } else if (_extension->notification) { + _extension->notification(_extension_instance, p_notification); +#endif // DISABLE_DEPRECATED + } } - if (_extension && _extension->notification) { - _extension->notification(_extension_instance, p_notification); + if (p_reversed) { + _notificationv(p_notification, p_reversed); + } else { + if (script_instance) { + script_instance->notification(p_notification, p_reversed); + } } } @@ -824,7 +867,7 @@ String Object::to_string() { void Object::set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance) { //this function is not meant to be used in any of these ways ERR_FAIL_COND(p_script.is_null()); - ERR_FAIL_COND(!p_instance); + ERR_FAIL_NULL(p_instance); ERR_FAIL_COND(script_instance != nullptr || !script.is_null()); script = p_script; @@ -1257,7 +1300,7 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui ERR_FAIL_COND_V_MSG(p_callable.is_null(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is null."); Object *target_object = p_callable.get_object(); - ERR_FAIL_COND_V_MSG(!target_object, ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "' to callable '" + p_callable + "': the callable object is null."); + ERR_FAIL_NULL_V_MSG(target_object, ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "' to callable '" + p_callable + "': the callable object is null."); SignalData *s = signal_map.getptr(p_signal); if (!s) { @@ -1342,7 +1385,7 @@ bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable, ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, "Cannot disconnect from '" + p_signal + "': the provided callable is null."); Object *target_object = p_callable.get_object(); - ERR_FAIL_COND_V_MSG(!target_object, false, "Cannot disconnect '" + p_signal + "' from callable '" + p_callable + "': the callable object is null."); + ERR_FAIL_NULL_V_MSG(target_object, false, "Cannot disconnect '" + p_signal + "' from callable '" + p_callable + "': the callable object is null."); SignalData *s = signal_map.getptr(p_signal); if (!s) { @@ -1350,7 +1393,7 @@ bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable, (!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(!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 + "'."); @@ -1602,6 +1645,8 @@ void Object::_bind_methods() { plget.return_val.hint_string = "Dictionary"; BIND_OBJ_CORE_METHOD(plget); + BIND_OBJ_CORE_METHOD(MethodInfo(Variant::NIL, "_validate_property", PropertyInfo(Variant::DICTIONARY, "property"))); + BIND_OBJ_CORE_METHOD(MethodInfo(Variant::BOOL, "_property_can_revert", PropertyInfo(Variant::STRING_NAME, "property"))); MethodInfo mipgr("_property_get_revert", PropertyInfo(Variant::STRING_NAME, "property")); mipgr.return_val.name = "Variant"; diff --git a/core/object/object.h b/core/object/object.h index 318dbf98de..3a698f7526 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -315,13 +315,18 @@ struct ObjectGDExtension { bool editor_class = false; bool is_virtual = false; bool is_abstract = false; + bool is_exposed = true; GDExtensionClassSet set; GDExtensionClassGet get; GDExtensionClassGetPropertyList get_property_list; GDExtensionClassFreePropertyList free_property_list; GDExtensionClassPropertyCanRevert property_can_revert; GDExtensionClassPropertyGetRevert property_get_revert; + GDExtensionClassValidateProperty validate_property; +#ifndef DISABLE_DEPRECATED GDExtensionClassNotification notification; +#endif // DISABLE_DEPRECATED + GDExtensionClassNotification2 notification2; GDExtensionClassToString to_string; GDExtensionClassReference reference; GDExtensionClassReference unreference; @@ -382,6 +387,7 @@ private: friend class ::ClassDB; \ \ public: \ + typedef m_class self_type; \ static constexpr bool _class_is_enabled = !bool(GD_IS_DEFINED(ClassDB_Disable_##m_class)) && m_inherits::_class_is_enabled; \ virtual String get_class() const override { \ if (_get_extension()) { \ @@ -485,7 +491,7 @@ protected: if (!p_reversed) { \ m_inherits::_get_property_listv(p_list, p_reversed); \ } \ - p_list->push_back(PropertyInfo(Variant::NIL, get_class_static(), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); \ + p_list->push_back(PropertyInfo(Variant::NIL, get_class_static(), PROPERTY_HINT_NONE, get_class_static(), PROPERTY_USAGE_CATEGORY)); \ if (!_is_gpl_reversed()) { \ ::ClassDB::get_property_list(#m_class, p_list, true, this); \ } \ @@ -557,6 +563,8 @@ class ScriptInstance; class Object { public: + typedef Object self_type; + enum ConnectFlags { CONNECT_DEFERRED = 1, CONNECT_PERSIST = 2, // hint for scene to save this connection diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h index 3386514706..228373d662 100644 --- a/core/object/ref_counted.h +++ b/core/object/ref_counted.h @@ -71,7 +71,7 @@ class Ref { } void ref_pointer(T *p_ref) { - ERR_FAIL_COND(!p_ref); + ERR_FAIL_NULL(p_ref); if (p_ref->init_ref()) { reference = p_ref; diff --git a/core/object/script_instance.cpp b/core/object/script_instance.cpp new file mode 100644 index 0000000000..303b127db1 --- /dev/null +++ b/core/object/script_instance.cpp @@ -0,0 +1,71 @@ +/**************************************************************************/ +/* script_instance.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 "script_instance.h" + +#include "core/object/script_language.h" + +Variant ScriptInstance::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + return callp(p_method, p_args, p_argcount, r_error); +} + +void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state) { + List<PropertyInfo> pinfo; + get_property_list(&pinfo); + for (const PropertyInfo &E : pinfo) { + if (E.usage & PROPERTY_USAGE_STORAGE) { + Pair<StringName, Variant> p; + p.first = E.name; + if (get(p.first, p.second)) { + state.push_back(p); + } + } + } +} + +void ScriptInstance::property_set_fallback(const StringName &, const Variant &, bool *r_valid) { + if (r_valid) { + *r_valid = false; + } +} + +Variant ScriptInstance::property_get_fallback(const StringName &, bool *r_valid) { + if (r_valid) { + *r_valid = false; + } + return Variant(); +} + +const Variant ScriptInstance::get_rpc_config() const { + return get_script()->get_rpc_config(); +} + +ScriptInstance::~ScriptInstance() { +} diff --git a/core/object/script_instance.h b/core/object/script_instance.h new file mode 100644 index 0000000000..df978a25ea --- /dev/null +++ b/core/object/script_instance.h @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* script_instance.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 SCRIPT_INSTANCE_H +#define SCRIPT_INSTANCE_H + +#include "core/object/ref_counted.h" + +class Script; +class ScriptLanguage; + +class ScriptInstance { +public: + virtual bool set(const StringName &p_name, const Variant &p_value) = 0; + virtual bool get(const StringName &p_name, Variant &r_ret) const = 0; + virtual void get_property_list(List<PropertyInfo> *p_properties) const = 0; + virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const = 0; + virtual void validate_property(PropertyInfo &p_property) const = 0; + + virtual bool property_can_revert(const StringName &p_name) const = 0; + virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const = 0; + + virtual Object *get_owner() { return nullptr; } + virtual void get_property_state(List<Pair<StringName, Variant>> &state); + + virtual void get_method_list(List<MethodInfo> *p_list) const = 0; + virtual bool has_method(const StringName &p_method) const = 0; + + virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = 0; + + template <typename... VarArgs> + Variant call(const StringName &p_method, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + Callable::CallError cerr; + return callp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), cerr); + } + + virtual Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); // implement if language supports const functions + virtual void notification(int p_notification, bool p_reversed = false) = 0; + virtual String to_string(bool *r_valid) { + if (r_valid) { + *r_valid = false; + } + return String(); + } + + //this is used by script languages that keep a reference counter of their own + //you can make make Ref<> not die when it reaches zero, so deleting the reference + //depends entirely from the script + + virtual void refcount_incremented() {} + virtual bool refcount_decremented() { return true; } //return true if it can die + + virtual Ref<Script> get_script() const = 0; + + virtual bool is_placeholder() const { return false; } + + virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid); + virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid); + + virtual const Variant get_rpc_config() const; + + virtual ScriptLanguage *get_language() = 0; + virtual ~ScriptInstance(); +}; + +#endif // SCRIPT_INSTANCE_H diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index a8b0e426ae..abf2b7b054 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -388,40 +388,6 @@ String ScriptServer::get_global_class_cache_file_path() { //////////////////// -Variant ScriptInstance::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - return callp(p_method, p_args, p_argcount, r_error); -} - -void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state) { - List<PropertyInfo> pinfo; - get_property_list(&pinfo); - for (const PropertyInfo &E : pinfo) { - if (E.usage & PROPERTY_USAGE_STORAGE) { - Pair<StringName, Variant> p; - p.first = E.name; - if (get(p.first, p.second)) { - state.push_back(p); - } - } - } -} - -void ScriptInstance::property_set_fallback(const StringName &, const Variant &, bool *r_valid) { - if (r_valid) { - *r_valid = false; - } -} - -Variant ScriptInstance::property_get_fallback(const StringName &, bool *r_valid) { - if (r_valid) { - *r_valid = false; - } - return Variant(); -} - -ScriptInstance::~ScriptInstance() { -} - ScriptCodeCompletionCache *ScriptCodeCompletionCache::singleton = nullptr; ScriptCodeCompletionCache::ScriptCodeCompletionCache() { singleton = this; @@ -482,7 +448,6 @@ TypedArray<int> ScriptLanguage::CodeCompletionOption::get_option_characteristics } charac.push_back(matches.size()); charac.push_back((matches[0].first == 0) ? 0 : 1); - charac.push_back(location); const char32_t *target_char = &p_base[0]; int bad_case = 0; for (const Pair<int, int> &match_segment : matches) { @@ -494,6 +459,7 @@ TypedArray<int> ScriptLanguage::CodeCompletionOption::get_option_characteristics } } charac.push_back(bad_case); + charac.push_back(location); charac.push_back(matches[0].first); last_matches = matches; return charac; diff --git a/core/object/script_language.h b/core/object/script_language.h index 3ea6a6e4c3..e0c4d650dd 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -33,6 +33,7 @@ #include "core/doc_data.h" #include "core/io/resource.h" +#include "core/object/script_instance.h" #include "core/templates/pair.h" #include "core/templates/rb_map.h" #include "core/templates/safe_refcount.h" @@ -101,7 +102,6 @@ public: static bool are_languages_finished() { return languages_finished.is_set(); } }; -class ScriptInstance; class PlaceHolderScriptInstance; class Script : public Resource { @@ -141,6 +141,7 @@ public: #ifdef TOOLS_ENABLED virtual Vector<DocData::ClassDoc> get_documentation() const = 0; + virtual String get_class_icon_path() const = 0; virtual PropertyInfo get_class_category() const; #endif // TOOLS_ENABLED @@ -173,64 +174,6 @@ public: Script() {} }; -class ScriptInstance { -public: - virtual bool set(const StringName &p_name, const Variant &p_value) = 0; - virtual bool get(const StringName &p_name, Variant &r_ret) const = 0; - virtual void get_property_list(List<PropertyInfo> *p_properties) const = 0; - virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const = 0; - - virtual bool property_can_revert(const StringName &p_name) const = 0; - virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const = 0; - - virtual Object *get_owner() { return nullptr; } - virtual void get_property_state(List<Pair<StringName, Variant>> &state); - - virtual void get_method_list(List<MethodInfo> *p_list) const = 0; - virtual bool has_method(const StringName &p_method) const = 0; - - virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = 0; - - template <typename... VarArgs> - Variant call(const StringName &p_method, VarArgs... p_args) { - Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. - const Variant *argptrs[sizeof...(p_args) + 1]; - for (uint32_t i = 0; i < sizeof...(p_args); i++) { - argptrs[i] = &args[i]; - } - Callable::CallError cerr; - return callp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), cerr); - } - - virtual Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); // implement if language supports const functions - virtual void notification(int p_notification) = 0; - virtual String to_string(bool *r_valid) { - if (r_valid) { - *r_valid = false; - } - return String(); - } - - //this is used by script languages that keep a reference counter of their own - //you can make make Ref<> not die when it reaches zero, so deleting the reference - //depends entirely from the script - - virtual void refcount_incremented() {} - virtual bool refcount_decremented() { return true; } //return true if it can die - - virtual Ref<Script> get_script() const = 0; - - virtual bool is_placeholder() const { return false; } - - virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid); - virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid); - - virtual const Variant get_rpc_config() const { return get_script()->get_rpc_config(); } - - virtual ScriptLanguage *get_language() = 0; - virtual ~ScriptInstance(); -}; - class ScriptCodeCompletionCache { static ScriptCodeCompletionCache *singleton; @@ -342,14 +285,16 @@ public: Vector<Pair<int, int>> matches; Vector<Pair<int, int>> last_matches = { { -1, -1 } }; // This value correspond to an impossible match int location = LOCATION_OTHER; + String theme_color_name; CodeCompletionOption() {} - CodeCompletionOption(const String &p_text, CodeCompletionKind p_kind, int p_location = LOCATION_OTHER) { + CodeCompletionOption(const String &p_text, CodeCompletionKind p_kind, int p_location = LOCATION_OTHER, const String &p_theme_color_name = "") { display = p_text; insert_text = p_text; kind = p_kind; location = p_location; + theme_color_name = p_theme_color_name; } TypedArray<int> get_option_characteristics(const String &p_base); @@ -462,6 +407,7 @@ public: virtual bool get(const StringName &p_name, Variant &r_ret) const override; virtual void get_property_list(List<PropertyInfo> *p_properties) const override; 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; }; @@ -473,7 +419,7 @@ public: r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; return Variant(); } - virtual void notification(int p_notification) override {} + virtual void notification(int p_notification, bool p_reversed = false) override {} virtual Ref<Script> get_script() const override { return script; } diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp index 0df9d58334..bf8bac476a 100644 --- a/core/object/script_language_extension.cpp +++ b/core/object/script_language_extension.cpp @@ -52,6 +52,7 @@ void ScriptExtension::_bind_methods() { GDVIRTUAL_BIND(_reload, "keep_state"); GDVIRTUAL_BIND(_get_documentation); + GDVIRTUAL_BIND(_get_class_icon_path); GDVIRTUAL_BIND(_has_method, "method"); GDVIRTUAL_BIND(_get_method_info, "method"); diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h index 1a0ec29479..e06f005320 100644 --- a/core/object/script_language_extension.h +++ b/core/object/script_language_extension.h @@ -77,6 +77,7 @@ public: EXBIND1R(Error, reload, bool) GDVIRTUAL0RC(TypedArray<Dictionary>, _get_documentation) + GDVIRTUAL0RC(String, _get_class_icon_path) #ifdef TOOLS_ENABLED virtual Vector<DocData::ClassDoc> get_documentation() const override { TypedArray<Dictionary> doc; @@ -89,6 +90,12 @@ public: return class_doc; } + + virtual String get_class_icon_path() const override { + String ret; + GDVIRTUAL_CALL(_get_class_icon_path, ret); + return ret; + } #endif // TOOLS_ENABLED EXBIND1RC(bool, has_method, const StringName &) @@ -623,7 +630,12 @@ VARIANT_ENUM_CAST(ScriptLanguageExtension::CodeCompletionLocation) class ScriptInstanceExtension : public ScriptInstance { public: - const GDExtensionScriptInstanceInfo *native_info; + const GDExtensionScriptInstanceInfo2 *native_info; + bool free_native_info = false; + struct { + GDExtensionClassNotification notification_func; + } deprecated_native_info; + GDExtensionScriptInstanceDataPtr instance = nullptr; // There should not be warnings on explicit casts. @@ -675,6 +687,28 @@ public: } return Variant::NIL; } + virtual void validate_property(PropertyInfo &p_property) const override { + if (native_info->validate_property_func) { + // GDExtension uses a StringName rather than a String for property name. + StringName prop_name = p_property.name; + GDExtensionPropertyInfo gdext_prop = { + (GDExtensionVariantType)p_property.type, + &prop_name, + &p_property.class_name, + (uint32_t)p_property.hint, + &p_property.hint_string, + p_property.usage, + }; + if (native_info->validate_property_func(instance, &gdext_prop)) { + p_property.type = (Variant::Type)gdext_prop.type; + p_property.name = *reinterpret_cast<StringName *>(gdext_prop.name); + p_property.class_name = *reinterpret_cast<StringName *>(gdext_prop.class_name); + p_property.hint = (PropertyHint)gdext_prop.hint; + p_property.hint_string = *reinterpret_cast<String *>(gdext_prop.hint_string); + p_property.usage = gdext_prop.usage; + } + } + } virtual bool property_can_revert(const StringName &p_name) const override { if (native_info->property_can_revert_func) { @@ -736,11 +770,16 @@ public: return ret; } - virtual void notification(int p_notification) override { + virtual void notification(int p_notification, bool p_reversed = false) override { if (native_info->notification_func) { - native_info->notification_func(instance, p_notification); + native_info->notification_func(instance, p_notification, p_reversed); +#ifndef DISABLE_DEPRECATED + } else if (deprecated_native_info.notification_func) { + deprecated_native_info.notification_func(instance, p_notification); +#endif // DISABLE_DEPRECATED } } + virtual String to_string(bool *r_valid) override { if (native_info->to_string_func) { GDExtensionBool valid; @@ -812,6 +851,9 @@ public: if (native_info->free_func) { native_info->free_func(instance); } + if (free_native_info) { + memfree(const_cast<GDExtensionScriptInstanceInfo2 *>(native_info)); + } } #if defined(__GNUC__) && !defined(__clang__) diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index f04961c760..b0f9501985 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -178,7 +178,7 @@ void UndoRedo::add_undo_method(const Callable &p_callable) { } void UndoRedo::add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value) { - ERR_FAIL_COND(p_object == nullptr); + ERR_FAIL_NULL(p_object); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); Operation do_op; @@ -194,7 +194,7 @@ void UndoRedo::add_do_property(Object *p_object, const StringName &p_property, c } void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value) { - ERR_FAIL_COND(p_object == nullptr); + ERR_FAIL_NULL(p_object); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); @@ -217,7 +217,7 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property, } void UndoRedo::add_do_reference(Object *p_object) { - ERR_FAIL_COND(p_object == nullptr); + ERR_FAIL_NULL(p_object); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); Operation do_op; @@ -231,7 +231,7 @@ void UndoRedo::add_do_reference(Object *p_object) { } void UndoRedo::add_undo_reference(Object *p_object) { - ERR_FAIL_COND(p_object == nullptr); + ERR_FAIL_NULL(p_object); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp index 5ec3e1a1a8..cfc3ac3bcf 100644 --- a/core/object/worker_thread_pool.cpp +++ b/core/object/worker_thread_pool.cpp @@ -34,8 +34,8 @@ #include "core/os/thread_safe.h" void WorkerThreadPool::Task::free_template_userdata() { - ERR_FAIL_COND(!template_userdata); - ERR_FAIL_COND(native_func_userdata == nullptr); + ERR_FAIL_NULL(template_userdata); + ERR_FAIL_NULL(native_func_userdata); BaseTemplateUserdata *btu = (BaseTemplateUserdata *)native_func_userdata; memdelete(btu); } diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h index 9fe8497eaf..d4d9387765 100644 --- a/core/object/worker_thread_pool.h +++ b/core/object/worker_thread_pool.h @@ -202,25 +202,4 @@ public: ~WorkerThreadPool(); }; -template <typename F> -static _FORCE_INLINE_ void for_range(int i_begin, int i_end, bool parallel, String name, F f) { - if (!parallel) { - for (int i = i_begin; i < i_end; i++) { - f(i); - } - return; - } - - auto wrapper = [&](int i, void *unused) { - f(i + i_begin); - }; - - WorkerThreadPool *wtp = WorkerThreadPool::get_singleton(); - WorkerThreadPool::GroupID gid = wtp->add_template_group_task( - &wrapper, &decltype(wrapper)::operator(), nullptr, - i_end - i_begin, -1, - true, name); - wtp->wait_for_group_task_completion(gid); -} - #endif // WORKER_THREAD_POOL_H diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp index 1a51624030..6078882839 100644 --- a/core/os/keyboard.cpp +++ b/core/os/keyboard.cpp @@ -146,6 +146,7 @@ static const _KeyCodeText _keycodes[] = { {Key::FAVORITES ,"Favorites"}, {Key::SEARCH ,"Search"}, {Key::STANDBY ,"StandBy"}, + {Key::OPENURL ,"OpenURL"}, {Key::LAUNCHMAIL ,"LaunchMail"}, {Key::LAUNCHMEDIA ,"LaunchMedia"}, {Key::LAUNCH0 ,"Launch0"}, @@ -238,6 +239,8 @@ static const _KeyCodeText _keycodes[] = { {Key::BAR ,"Bar"}, {Key::BRACERIGHT ,"BraceRight"}, {Key::ASCIITILDE ,"AsciiTilde"}, + {Key::YEN ,"Yen"}, + {Key::SECTION ,"Section"}, {Key::NONE ,nullptr} /* clang-format on */ }; diff --git a/core/os/keyboard.h b/core/os/keyboard.h index cf276dc49f..785972d31d 100644 --- a/core/os/keyboard.h +++ b/core/os/keyboard.h @@ -33,6 +33,8 @@ #include "core/string/ustring.h" +// Keep the values in this enum in sync with `_keycodes` in `keyboard.cpp`, +// and the bindings in `core_constants.cpp`. enum class Key { NONE = 0, // Special key: The strategy here is similar to the one used by toolkits, diff --git a/core/os/main_loop.h b/core/os/main_loop.h index 90cad009b1..b45eb38aeb 100644 --- a/core/os/main_loop.h +++ b/core/os/main_loop.h @@ -34,7 +34,6 @@ #include "core/input/input_event.h" #include "core/object/gdvirtual.gen.inc" #include "core/object/ref_counted.h" -#include "core/object/script_language.h" class MainLoop : public Object { GDCLASS(MainLoop, Object); diff --git a/core/os/memory.cpp b/core/os/memory.cpp index 0d15b8dcf5..5f6216a5f1 100644 --- a/core/os/memory.cpp +++ b/core/os/memory.cpp @@ -74,7 +74,7 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) { void *mem = malloc(p_bytes + (prepad ? PAD_ALIGN : 0)); - ERR_FAIL_COND_V(!mem, nullptr); + ERR_FAIL_NULL_V(mem, nullptr); alloc_count.increment(); @@ -127,7 +127,7 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) { *s = p_bytes; mem = (uint8_t *)realloc(mem, p_bytes + PAD_ALIGN); - ERR_FAIL_COND_V(!mem, nullptr); + ERR_FAIL_NULL_V(mem, nullptr); s = (uint64_t *)mem; @@ -145,7 +145,7 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) { } void Memory::free_static(void *p_ptr, bool p_pad_align) { - ERR_FAIL_COND(p_ptr == nullptr); + ERR_FAIL_NULL(p_ptr); uint8_t *mem = (uint8_t *)p_ptr; diff --git a/core/os/memory.h b/core/os/memory.h index 45019894b1..a0524b0ea2 100644 --- a/core/os/memory.h +++ b/core/os/memory.h @@ -144,7 +144,7 @@ T *memnew_arr_template(size_t p_elements) { size_t len = sizeof(T) * p_elements; uint64_t *mem = (uint64_t *)Memory::alloc_static(len, true); T *failptr = nullptr; //get rid of a warning - ERR_FAIL_COND_V(!mem, failptr); + ERR_FAIL_NULL_V(mem, failptr); *(mem - 1) = p_elements; if (!std::is_trivially_constructible<T>::value) { diff --git a/core/os/os.h b/core/os/os.h index 965dc1f912..cc5ebe1bc8 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -328,6 +328,10 @@ public: virtual PreferredTextureFormat get_preferred_texture_format() const; + // Load GDExtensions specific to this platform. + // This is invoked by the GDExtensionManager after loading GDExtensions specified by the project. + virtual void load_platform_gdextensions() const {} + OS(); virtual ~OS(); }; diff --git a/core/os/pool_allocator.cpp b/core/os/pool_allocator.cpp index c23196efdb..acbaed4ce8 100644 --- a/core/os/pool_allocator.cpp +++ b/core/os/pool_allocator.cpp @@ -305,7 +305,7 @@ Error PoolAllocator::resize(ID p_mem, int p_new_size) { if (!e) { mt_unlock(); - ERR_FAIL_COND_V(!e, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(e, ERR_INVALID_PARAMETER); } if (needs_locking && e->lock) { @@ -431,7 +431,7 @@ bool PoolAllocator::is_locked(ID p_mem) const { const void *PoolAllocator::get(ID p_mem) const { if (!needs_locking) { const Entry *e = get_entry(p_mem); - ERR_FAIL_COND_V(!e, nullptr); + ERR_FAIL_NULL_V(e, nullptr); return &pool[e->pos]; } @@ -440,7 +440,7 @@ const void *PoolAllocator::get(ID p_mem) const { if (!e) { mt_unlock(); - ERR_FAIL_COND_V(!e, nullptr); + ERR_FAIL_NULL_V(e, nullptr); } if (e->lock == 0) { mt_unlock(); @@ -463,7 +463,7 @@ const void *PoolAllocator::get(ID p_mem) const { void *PoolAllocator::get(ID p_mem) { if (!needs_locking) { Entry *e = get_entry(p_mem); - ERR_FAIL_COND_V(!e, nullptr); + ERR_FAIL_NULL_V(e, nullptr); return &pool[e->pos]; } @@ -472,7 +472,7 @@ void *PoolAllocator::get(ID p_mem) { if (!e) { mt_unlock(); - ERR_FAIL_COND_V(!e, nullptr); + ERR_FAIL_NULL_V(e, nullptr); } if (e->lock == 0) { mt_unlock(); @@ -500,7 +500,7 @@ void PoolAllocator::unlock(ID p_mem) { Entry *e = get_entry(p_mem); if (!e) { mt_unlock(); - ERR_FAIL_COND(!e); + ERR_FAIL_NULL(e); } if (e->lock == 0) { mt_unlock(); @@ -540,7 +540,7 @@ void PoolAllocator::create_pool(void *p_mem, int p_size, int p_max_entries) { PoolAllocator::PoolAllocator(int p_size, bool p_needs_locking, int p_max_entries) { mem_ptr = memalloc(p_size); - ERR_FAIL_COND(!mem_ptr); + ERR_FAIL_NULL(mem_ptr); align = 1; create_pool(mem_ptr, p_size, p_max_entries); needs_locking = p_needs_locking; diff --git a/core/string/locales.h b/core/string/locales.h index 8a7efb4fd1..840fca65a7 100644 --- a/core/string/locales.h +++ b/core/string/locales.h @@ -1057,8 +1057,8 @@ static const char *script_list[][2] = { { "Hangul", "Hang" }, { "Han", "Hani" }, { "Hanunoo", "Hano" }, - { "Simplified", "Hans" }, - { "Traditional", "Hant" }, + { "Simplified Han", "Hans" }, + { "Traditional Han", "Hant" }, { "Hatran", "Hatr" }, { "Hebrew", "Hebr" }, { "Hiragana", "Hira" }, @@ -1110,7 +1110,7 @@ static const char *script_list[][2] = { { "Mro", "Mroo" }, { "Meitei Mayek", "Mtei" }, { "Multani", "Mult" }, - { "Myanmar (Burmese)", "Mymr" }, + { "Myanmar / Burmese", "Mymr" }, { "Nag Mundari", "Nagm" }, { "Nandinagari", "Nand" }, { "Old North Arabian", "Narb" }, diff --git a/core/string/node_path.cpp b/core/string/node_path.cpp index af7c18741d..32e4564c5e 100644 --- a/core/string/node_path.cpp +++ b/core/string/node_path.cpp @@ -73,7 +73,7 @@ int NodePath::get_name_count() const { } StringName NodePath::get_name(int p_idx) const { - ERR_FAIL_COND_V(!data, StringName()); + ERR_FAIL_NULL_V(data, StringName()); ERR_FAIL_INDEX_V(p_idx, data->path.size(), StringName()); return data->path[p_idx]; } @@ -87,7 +87,7 @@ int NodePath::get_subname_count() const { } StringName NodePath::get_subname(int p_idx) const { - ERR_FAIL_COND_V(!data, StringName()); + ERR_FAIL_NULL_V(data, StringName()); ERR_FAIL_INDEX_V(p_idx, data->subpath.size(), StringName()); return data->subpath[p_idx]; } @@ -200,7 +200,7 @@ Vector<StringName> NodePath::get_subnames() const { } StringName NodePath::get_concatenated_names() const { - ERR_FAIL_COND_V(!data, StringName()); + ERR_FAIL_NULL_V(data, StringName()); if (!data->concatenated_path) { int pc = data->path.size(); @@ -215,7 +215,7 @@ StringName NodePath::get_concatenated_names() const { } StringName NodePath::get_concatenated_subnames() const { - ERR_FAIL_COND_V(!data, StringName()); + ERR_FAIL_NULL_V(data, StringName()); if (!data->concatenated_subpath) { int spc = data->subpath.size(); diff --git a/core/string/print_string.cpp b/core/string/print_string.cpp index dcdde3c175..e3614be359 100644 --- a/core/string/print_string.cpp +++ b/core/string/print_string.cpp @@ -65,7 +65,7 @@ void remove_print_handler(const PrintHandlerList *p_handler) { //OS::get_singleton()->print("print handler list is %p\n",print_handler_list); _global_unlock(); - ERR_FAIL_COND(l == nullptr); + ERR_FAIL_NULL(l); } void __print_line(String p_string) { diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp index 6099fea13f..4402e44ad4 100644 --- a/core/string/string_name.cpp +++ b/core/string/string_name.cpp @@ -390,7 +390,7 @@ StringName::StringName(const String &p_name, bool p_static) { StringName StringName::search(const char *p_name) { ERR_FAIL_COND_V(!configured, StringName()); - ERR_FAIL_COND_V(!p_name, StringName()); + ERR_FAIL_NULL_V(p_name, StringName()); if (!p_name[0]) { return StringName(); } @@ -426,7 +426,7 @@ StringName StringName::search(const char *p_name) { StringName StringName::search(const char32_t *p_name) { ERR_FAIL_COND_V(!configured, StringName()); - ERR_FAIL_COND_V(!p_name, StringName()); + ERR_FAIL_NULL_V(p_name, StringName()); if (!p_name[0]) { return StringName(); } diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 3ca2e5ccdf..02380c92bb 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -82,6 +82,15 @@ void Translation::_set_messages(const Dictionary &p_messages) { void Translation::set_locale(const String &p_locale) { locale = TranslationServer::get_singleton()->standardize_locale(p_locale); + if (Thread::is_main_thread()) { + _notify_translation_changed_if_applies(); + } else { + // Avoid calling non-thread-safe functions here. + callable_mp(this, &Translation::_notify_translation_changed_if_applies).call_deferred(); + } +} + +void Translation::_notify_translation_changed_if_applies() { if (OS::get_singleton()->get_main_loop() && TranslationServer::get_singleton()->get_loaded_locales().has(get_locale())) { OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); } diff --git a/core/string/translation.h b/core/string/translation.h index 01d239f81c..3f9dbcc476 100644 --- a/core/string/translation.h +++ b/core/string/translation.h @@ -33,7 +33,6 @@ #include "core/io/resource.h" #include "core/object/gdvirtual.gen.inc" -#include "core/object/script_language.h" class Translation : public Resource { GDCLASS(Translation, Resource); @@ -47,6 +46,8 @@ class Translation : public Resource { virtual Dictionary _get_messages() const; virtual void _set_messages(const Dictionary &p_messages); + void _notify_translation_changed_if_applies(); + protected: static void _bind_methods(); diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 12e6423724..f6a17cf1a7 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -1493,9 +1493,9 @@ String String::num(double p_num, int p_decimals) { if (p_decimals < 0) { p_decimals = 14; - const double abs_num = ABS(p_num); + const double abs_num = Math::abs(p_num); if (abs_num > 10) { - // We want to align the digits to the above sane default, so we only + // We want to align the digits to the above reasonable default, so we only // need to subtract log10 for numbers with a positive power of ten. p_decimals -= (int)floor(log10(abs_num)); } @@ -1750,7 +1750,7 @@ Vector<uint8_t> String::hex_decode() const { void String::print_unicode_error(const String &p_message, bool p_critical) const { if (p_critical) { - print_error(vformat("Unicode parsing error, some characters were replaced with � (U+FFFD): %s", p_message)); + print_error(vformat(U"Unicode parsing error, some characters were replaced with � (U+FFFD): %s", p_message)); } else { print_error(vformat("Unicode parsing error: %s", p_message)); } @@ -3635,6 +3635,23 @@ String String::repeat(int p_count) const { return new_string; } +String String::reverse() const { + int len = length(); + if (len <= 1) { + return *this; + } + String new_string; + new_string.resize(len + 1); + + const char32_t *src = ptr(); + char32_t *dst = new_string.ptrw(); + for (int i = 0; i < len; i++) { + dst[i] = src[len - i - 1]; + } + dst[len] = _null; + return new_string; +} + String String::left(int p_len) const { if (p_len < 0) { p_len = length() + p_len; @@ -3648,7 +3665,9 @@ String String::left(int p_len) const { return *this; } - return substr(0, p_len); + String s; + s.copy_from_unchecked(&get_data()[0], p_len); + return s; } String String::right(int p_len) const { @@ -3664,7 +3683,9 @@ String String::right(int p_len) const { return *this; } - return substr(length() - p_len); + String s; + s.copy_from_unchecked(&get_data()[length() - p_len], p_len); + return s; } char32_t String::unicode_at(int p_idx) const { @@ -4869,8 +4890,8 @@ String String::sprintf(const Array &values, bool *error) const { } double value = values[value_index]; - bool is_negative = (value < 0); - String str = String::num(ABS(value), min_decimals); + bool is_negative = signbit(value); + String str = String::num(Math::abs(value), min_decimals); const bool is_finite = Math::is_finite(value); // Pad decimals out. @@ -4932,7 +4953,7 @@ String String::sprintf(const Array &values, bool *error) const { String str = "("; for (int i = 0; i < count; i++) { double val = vec[i]; - String number_str = String::num(ABS(val), min_decimals); + String number_str = String::num(Math::abs(val), min_decimals); const bool is_finite = Math::is_finite(val); // Pad decimals out. diff --git a/core/string/ustring.h b/core/string/ustring.h index 295625395d..f45392eee1 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -305,6 +305,7 @@ public: String replace(const char *p_key, const char *p_with) const; String replacen(const String &p_key, const String &p_with) const; String repeat(int p_count) const; + String reverse() const; String insert(int p_at_pos, const String &p_string) const; String erase(int p_pos, int p_chars = 1) const; String pad_decimals(int p_digits) const; diff --git a/core/templates/command_queue_mt.h b/core/templates/command_queue_mt.h index b7dd1bae34..7e480653ac 100644 --- a/core/templates/command_queue_mt.h +++ b/core/templates/command_queue_mt.h @@ -409,7 +409,7 @@ public: } void wait_and_flush() { - ERR_FAIL_COND(!sync); + ERR_FAIL_NULL(sync); sync->wait(); _flush(); } diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h index e33822fedf..d446c81721 100644 --- a/core/templates/cowdata.h +++ b/core/templates/cowdata.h @@ -286,7 +286,7 @@ Error CowData<T>::resize(int p_size) { if (current_size == 0) { // alloc from scratch uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true); - ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY); + ERR_FAIL_NULL_V(ptr, ERR_OUT_OF_MEMORY); *(ptr - 1) = 0; //size, currently none new (ptr - 2) SafeNumeric<uint32_t>(1); //refcount @@ -294,7 +294,7 @@ Error CowData<T>::resize(int p_size) { } else { uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true); - ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + ERR_FAIL_NULL_V(_ptrnew, ERR_OUT_OF_MEMORY); new (_ptrnew - 2) SafeNumeric<uint32_t>(rc); //refcount _ptr = (T *)(_ptrnew); @@ -324,7 +324,7 @@ Error CowData<T>::resize(int p_size) { if (alloc_size != current_alloc_size) { uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true); - ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + ERR_FAIL_NULL_V(_ptrnew, ERR_OUT_OF_MEMORY); new (_ptrnew - 2) SafeNumeric<uint32_t>(rc); //refcount _ptr = (T *)(_ptrnew); diff --git a/core/templates/list.h b/core/templates/list.h index 809d7e1667..6393b942ff 100644 --- a/core/templates/list.h +++ b/core/templates/list.h @@ -219,7 +219,7 @@ private: int size_cache = 0; bool erase(const Element *p_I) { - ERR_FAIL_COND_V(!p_I, false); + ERR_FAIL_NULL_V(p_I, false); ERR_FAIL_COND_V(p_I->data != this, false); if (first == p_I) { diff --git a/core/templates/paged_array.h b/core/templates/paged_array.h index 45b90869b9..863e3eef11 100644 --- a/core/templates/paged_array.h +++ b/core/templates/paged_array.h @@ -112,7 +112,7 @@ public: } void configure(uint32_t p_page_size) { - ERR_FAIL_COND(page_pool != nullptr); //sanity check + ERR_FAIL_COND(page_pool != nullptr); // Safety check. ERR_FAIL_COND(p_page_size == 0); page_size = nearest_power_of_2_templated(p_page_size); } @@ -185,7 +185,7 @@ public: uint32_t new_page_count = page_count + 1; if (unlikely(new_page_count > max_pages_used)) { - ERR_FAIL_COND(page_pool == nullptr); //sanity check + ERR_FAIL_NULL(page_pool); // Safety check. _grow_page_array(); //keep out of inline } @@ -352,7 +352,7 @@ public: } void set_page_pool(PagedArrayPool<T> *p_page_pool) { - ERR_FAIL_COND(max_pages_used > 0); //sanity check + ERR_FAIL_COND(max_pages_used > 0); // Safety check. page_pool = p_page_pool; page_size_mask = page_pool->get_page_size_mask(); diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h index e2ffabdaf0..e6c62ebf43 100644 --- a/core/templates/rid_owner.h +++ b/core/templates/rid_owner.h @@ -211,12 +211,12 @@ public: } void initialize_rid(RID p_rid) { T *mem = get_or_null(p_rid, true); - ERR_FAIL_COND(!mem); + ERR_FAIL_NULL(mem); memnew_placement(mem, T); } void initialize_rid(RID p_rid, const T &p_value) { T *mem = get_or_null(p_rid, true); - ERR_FAIL_COND(!mem); + ERR_FAIL_NULL(mem); memnew_placement(mem, T(p_value)); } @@ -391,7 +391,7 @@ public: _FORCE_INLINE_ void replace(const RID &p_rid, T *p_new_ptr) { T **ptr = alloc.get_or_null(p_rid); - ERR_FAIL_COND(!ptr); + ERR_FAIL_NULL(ptr); *ptr = p_new_ptr; } diff --git a/core/typedefs.h b/core/typedefs.h index 1dcba58188..24c247fd38 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -109,7 +109,7 @@ constexpr T ABS(T m_v) { template <typename T> constexpr const T SIGN(const T m_v) { - return m_v == 0 ? 0.0f : (m_v < 0 ? -1.0f : +1.0f); + return m_v > 0 ? +1.0f : (m_v < 0 ? -1.0f : 0.0f); } template <typename T, typename T2> diff --git a/core/variant/array.cpp b/core/variant/array.cpp index 5a0ded6c01..4c9ee68ab5 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -52,7 +52,7 @@ public: void Array::_ref(const Array &p_from) const { ArrayPrivate *_fp = p_from._p; - ERR_FAIL_COND(!_fp); // should NOT happen. + ERR_FAIL_NULL(_fp); // Should NOT happen. if (_fp == _p) { return; // whatever it is, nothing to do here move along diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp index 630873ec2e..d7034f1c00 100644 --- a/core/variant/callable.cpp +++ b/core/variant/callable.cpp @@ -465,20 +465,20 @@ Error Signal::emit(const Variant **p_arguments, int p_argcount) const { Error Signal::connect(const Callable &p_callable, uint32_t p_flags) { Object *obj = get_object(); - ERR_FAIL_COND_V(!obj, ERR_UNCONFIGURED); + ERR_FAIL_NULL_V(obj, ERR_UNCONFIGURED); return obj->connect(name, p_callable, p_flags); } void Signal::disconnect(const Callable &p_callable) { Object *obj = get_object(); - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->disconnect(name, p_callable); } bool Signal::is_connected(const Callable &p_callable) const { Object *obj = get_object(); - ERR_FAIL_COND_V(!obj, false); + ERR_FAIL_NULL_V(obj, false); return obj->is_connected(name, p_callable); } @@ -500,7 +500,7 @@ Array Signal::get_connections() const { } Signal::Signal(const Object *p_object, const StringName &p_name) { - ERR_FAIL_COND_MSG(p_object == nullptr, "Object argument to Signal constructor must be non-null"); + ERR_FAIL_NULL_MSG(p_object, "Object argument to Signal constructor must be non-null."); object = p_object->get_instance_id(); name = p_name; diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp index e493e50467..a5629d5d39 100644 --- a/core/variant/callable_bind.cpp +++ b/core/variant/callable_bind.cpp @@ -88,7 +88,7 @@ ObjectID CallableCustomBind::get_object() const { } const Callable *CallableCustomBind::get_base_comparator() const { - return &callable; + return callable.get_base_comparator(); } int CallableCustomBind::get_bound_arguments_count() const { @@ -222,7 +222,7 @@ ObjectID CallableCustomUnbind::get_object() const { } const Callable *CallableCustomUnbind::get_base_comparator() const { - return &callable; + return callable.get_base_comparator(); } int CallableCustomUnbind::get_bound_arguments_count() const { diff --git a/core/variant/container_type_validate.h b/core/variant/container_type_validate.h index ffe1dc90a3..0a23c69cb4 100644 --- a/core/variant/container_type_validate.h +++ b/core/variant/container_type_validate.h @@ -113,7 +113,7 @@ struct ContainerTypeValidate { return true; // This is fine, it's null. } Object *object = ObjectDB::get_instance(object_id); - ERR_FAIL_COND_V_MSG(object == nullptr, false, "Attempted to " + String(p_operation) + " an invalid (previously freed?) object instance into a '" + String(where) + "."); + ERR_FAIL_NULL_V_MSG(object, false, "Attempted to " + String(p_operation) + " an invalid (previously freed?) object instance into a '" + String(where) + "."); #else Object *object = p_variant; if (object == nullptr) { diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp index f019273735..2dda9fc1f8 100644 --- a/core/variant/dictionary.cpp +++ b/core/variant/dictionary.cpp @@ -248,7 +248,7 @@ void Dictionary::merge(const Dictionary &p_dictionary, bool p_overwrite) { } void Dictionary::_unref() const { - ERR_FAIL_COND(!_p); + ERR_FAIL_NULL(_p); if (_p->refcount.unref()) { if (_p->read_only) { memdelete(_p->read_only); diff --git a/core/variant/typed_array.h b/core/variant/typed_array.h index 98afc7e717..055c52aa63 100644 --- a/core/variant/typed_array.h +++ b/core/variant/typed_array.h @@ -230,4 +230,7 @@ MAKE_TYPED_ARRAY_INFO(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY) MAKE_TYPED_ARRAY_INFO(Vector<Color>, Variant::PACKED_COLOR_ARRAY) MAKE_TYPED_ARRAY_INFO(IPAddress, Variant::STRING) +#undef MAKE_TYPED_ARRAY +#undef MAKE_TYPED_ARRAY_INFO + #endif // TYPED_ARRAY_H diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index 10a267e5a9..8a0289898d 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -1754,11 +1754,10 @@ String Variant::stringify(int recursion_count) const { case COLOR: return operator Color(); case DICTIONARY: { + ERR_FAIL_COND_V_MSG(recursion_count > MAX_RECURSION, "{ ... }", "Maximum dictionary recursion reached!"); + recursion_count++; + const Dictionary &d = *reinterpret_cast<const Dictionary *>(_data._mem); - if (recursion_count > MAX_RECURSION) { - ERR_PRINT("Maximum dictionary recursion reached!"); - return "{ ... }"; - } // Add leading and trailing space to Dictionary printing. This distinguishes it // from array printing on fonts that have similar-looking {} and [] characters. @@ -1768,7 +1767,6 @@ String Variant::stringify(int recursion_count) const { Vector<_VariantStrPair> pairs; - recursion_count++; for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { _VariantStrPair sp; sp.key = stringify_variant_clean(E->get(), recursion_count); @@ -1787,6 +1785,7 @@ String Variant::stringify(int recursion_count) const { return str; } + // Packed arrays cannot contain recursive structures, the recursion_count increment is not needed. case PACKED_VECTOR2_ARRAY: { return stringify_vector(operator Vector<Vector2>(), recursion_count); } @@ -1815,13 +1814,10 @@ String Variant::stringify(int recursion_count) const { return stringify_vector(operator Vector<double>(), recursion_count); } case ARRAY: { - Array arr = operator Array(); - if (recursion_count > MAX_RECURSION) { - ERR_PRINT("Maximum array recursion reached!"); - return "[...]"; - } + ERR_FAIL_COND_V_MSG(recursion_count > MAX_RECURSION, "[...]", "Maximum array recursion reached!"); + recursion_count++; - return stringify_vector(arr, recursion_count); + return stringify_vector(operator Array(), recursion_count); } case OBJECT: { if (_get_obj().obj) { diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index dad9183216..19ae1b0157 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -561,8 +561,8 @@ static _FORCE_INLINE_ void vc_ptrcall(void (*method)(T *, P...), void *p_base, c } \ static void ptrcall(void *p_base, const void **p_args, void *r_ret, int p_argcount) { \ LocalVector<Variant> vars; \ - vars.resize(p_argcount); \ LocalVector<const Variant *> vars_ptrs; \ + vars.resize(p_argcount); \ vars_ptrs.resize(p_argcount); \ for (int i = 0; i < p_argcount; i++) { \ vars[i] = PtrToArg<Variant>::convert(p_args[i]); \ @@ -571,7 +571,7 @@ static _FORCE_INLINE_ void vc_ptrcall(void (*method)(T *, P...), void *p_base, c Variant base = PtrToArg<m_class>::convert(p_base); \ Variant ret; \ Callable::CallError ce; \ - m_method_ptr(&base, (const Variant **)&vars_ptrs[0], p_argcount, ret, ce); \ + m_method_ptr(&base, vars_ptrs.ptr(), p_argcount, ret, ce); \ if (m_has_return) { \ m_return_type r = ret; \ PtrToArg<m_return_type>::encode(ret, r_ret); \ @@ -617,8 +617,8 @@ static _FORCE_INLINE_ void vc_ptrcall(void (*method)(T *, P...), void *p_base, c } \ static void ptrcall(void *p_base, const void **p_args, void *r_ret, int p_argcount) { \ LocalVector<Variant> vars; \ - vars.resize(p_argcount); \ LocalVector<const Variant *> vars_ptrs; \ + vars.resize(p_argcount); \ vars_ptrs.resize(p_argcount); \ for (int i = 0; i < p_argcount; i++) { \ vars[i] = PtrToArg<Variant>::convert(p_args[i]); \ @@ -627,7 +627,7 @@ static _FORCE_INLINE_ void vc_ptrcall(void (*method)(T *, P...), void *p_base, c Variant base = PtrToArg<m_class>::convert(p_base); \ Variant ret; \ Callable::CallError ce; \ - m_method_ptr(&base, (const Variant **)&vars_ptrs[0], p_argcount, ret, ce); \ + m_method_ptr(&base, vars_ptrs.ptr(), p_argcount, ret, ce); \ } \ static int get_argument_count() { \ return 1; \ @@ -1132,11 +1132,7 @@ static void register_builtin_method(const Vector<String> &p_argnames, const Vect imi.call = T::call; imi.validated_call = T::validated_call; - if (T::is_vararg()) { - imi.ptrcall = nullptr; - } else { - imi.ptrcall = T::ptrcall; - } + imi.ptrcall = T::ptrcall; imi.default_arguments = p_def_args; imi.argument_names = p_argnames; @@ -1263,28 +1259,28 @@ bool Variant::has_builtin_method(Variant::Type p_type, const StringName &p_metho Variant::ValidatedBuiltInMethod Variant::get_validated_builtin_method(Variant::Type p_type, const StringName &p_method) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); - ERR_FAIL_COND_V(!method, nullptr); + ERR_FAIL_NULL_V(method, nullptr); return method->validated_call; } Variant::PTRBuiltInMethod Variant::get_ptr_builtin_method(Variant::Type p_type, const StringName &p_method) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); - ERR_FAIL_COND_V(!method, nullptr); + ERR_FAIL_NULL_V(method, nullptr); return method->ptrcall; } int Variant::get_builtin_method_argument_count(Variant::Type p_type, const StringName &p_method) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, 0); const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); - ERR_FAIL_COND_V(!method, 0); + ERR_FAIL_NULL_V(method, 0); return method->argument_count; } Variant::Type Variant::get_builtin_method_argument_type(Variant::Type p_type, const StringName &p_method, int p_argument) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, Variant::NIL); const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); - ERR_FAIL_COND_V(!method, Variant::NIL); + ERR_FAIL_NULL_V(method, Variant::NIL); ERR_FAIL_INDEX_V(p_argument, method->argument_count, Variant::NIL); return method->get_argument_type(p_argument); } @@ -1292,7 +1288,7 @@ Variant::Type Variant::get_builtin_method_argument_type(Variant::Type p_type, co String Variant::get_builtin_method_argument_name(Variant::Type p_type, const StringName &p_method, int p_argument) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, String()); const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); - ERR_FAIL_COND_V(!method, String()); + ERR_FAIL_NULL_V(method, String()); #ifdef DEBUG_METHODS_ENABLED ERR_FAIL_INDEX_V(p_argument, method->argument_count, String()); return method->argument_names[p_argument]; @@ -1304,14 +1300,14 @@ String Variant::get_builtin_method_argument_name(Variant::Type p_type, const Str Vector<Variant> Variant::get_builtin_method_default_arguments(Variant::Type p_type, const StringName &p_method) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, Vector<Variant>()); const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); - ERR_FAIL_COND_V(!method, Vector<Variant>()); + ERR_FAIL_NULL_V(method, Vector<Variant>()); return method->default_arguments; } bool Variant::has_builtin_method_return_value(Variant::Type p_type, const StringName &p_method) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); - ERR_FAIL_COND_V(!method, false); + ERR_FAIL_NULL_V(method, false); return method->has_return_type; } @@ -1330,35 +1326,35 @@ int Variant::get_builtin_method_count(Variant::Type p_type) { Variant::Type Variant::get_builtin_method_return_type(Variant::Type p_type, const StringName &p_method) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, Variant::NIL); const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); - ERR_FAIL_COND_V(!method, Variant::NIL); + ERR_FAIL_NULL_V(method, Variant::NIL); return method->return_type; } bool Variant::is_builtin_method_const(Variant::Type p_type, const StringName &p_method) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); - ERR_FAIL_COND_V(!method, false); + ERR_FAIL_NULL_V(method, false); return method->is_const; } bool Variant::is_builtin_method_static(Variant::Type p_type, const StringName &p_method) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); - ERR_FAIL_COND_V(!method, false); + ERR_FAIL_NULL_V(method, false); return method->is_static; } bool Variant::is_builtin_method_vararg(Variant::Type p_type, const StringName &p_method) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); - ERR_FAIL_COND_V(!method, false); + ERR_FAIL_NULL_V(method, false); return method->is_vararg; } uint32_t Variant::get_builtin_method_hash(Variant::Type p_type, const StringName &p_method) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, 0); const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); - ERR_FAIL_COND_V(!method, 0); + ERR_FAIL_NULL_V(method, 0); uint32_t hash = hash_murmur3_one_32(method->is_const); hash = hash_murmur3_one_32(method->is_static, hash); hash = hash_murmur3_one_32(method->is_vararg, hash); @@ -1659,6 +1655,7 @@ static void _register_variant_builtin_methods() { bind_string_methodv(replace, static_cast<String (String::*)(const String &, const String &) const>(&String::replace), sarray("what", "forwhat"), varray()); bind_string_method(replacen, sarray("what", "forwhat"), varray()); bind_string_method(repeat, sarray("count"), varray()); + bind_string_method(reverse, sarray(), varray()); bind_string_method(insert, sarray("position", "what"), varray()); bind_string_method(erase, sarray("position", "chars"), varray(1)); bind_string_method(capitalize, sarray(), varray()); diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h index c11f726402..9e6367ab6d 100644 --- a/core/variant/variant_op.h +++ b/core/variant/variant_op.h @@ -284,7 +284,7 @@ public: const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right); if (b == 0) { r_valid = false; - *r_ret = "Module by zero error"; + *r_ret = "Modulo by zero error"; return; } *r_ret = a % b; @@ -307,7 +307,7 @@ public: const Vector2i &b = *VariantGetInternalPtr<Vector2i>::get_ptr(&p_right); if (unlikely(b.x == 0 || b.y == 0)) { r_valid = false; - *r_ret = "Module by zero error"; + *r_ret = "Modulo by zero error"; return; } *r_ret = a % b; @@ -331,7 +331,7 @@ public: const Vector3i &b = *VariantGetInternalPtr<Vector3i>::get_ptr(&p_right); if (unlikely(b.x == 0 || b.y == 0 || b.z == 0)) { r_valid = false; - *r_ret = "Module by zero error"; + *r_ret = "Modulo by zero error"; return; } *r_ret = a % b; @@ -355,7 +355,7 @@ public: const Vector4i &b = *VariantGetInternalPtr<Vector4i>::get_ptr(&p_right); if (unlikely(b.x == 0 || b.y == 0 || b.z == 0 || b.w == 0)) { r_valid = false; - *r_ret = "Module by zero error"; + *r_ret = "Modulo by zero error"; return; } *r_ret = a % b; @@ -1493,7 +1493,7 @@ public: } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { Object *l = right->get_validated_object(); - ERR_FAIL_COND(l == nullptr); + ERR_FAIL_NULL(l); const String &a = *VariantGetInternalPtr<String>::get_ptr(left); bool valid; @@ -1527,7 +1527,7 @@ public: } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { Object *l = right->get_validated_object(); - ERR_FAIL_COND(l == nullptr); + ERR_FAIL_NULL(l); const StringName &a = *VariantGetInternalPtr<StringName>::get_ptr(left); bool valid; diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index 3320750994..fea1622222 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -32,6 +32,7 @@ #include "core/input/input_event.h" #include "core/io/resource_loader.h" +#include "core/object/script_language.h" #include "core/os/keyboard.h" #include "core/string/string_buffer.h" @@ -1708,7 +1709,7 @@ static String rtos_fix(double p_value) { } } -Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int recursion_count) { +Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count) { switch (p_variant.get_type()) { case Variant::NIL: { p_store_string_func(p_store_string_ud, "null"); @@ -1730,10 +1731,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::STRING: { String str = p_variant; - str = "\"" + str.c_escape_multiline() + "\""; p_store_string_func(p_store_string_ud, str); } break; + + // Math types. case Variant::VECTOR2: { Vector2 v = p_variant; p_store_string_func(p_store_string_ud, "Vector2(" + rtos_fix(v.x) + ", " + rtos_fix(v.y) + ")"); @@ -1745,12 +1747,10 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str case Variant::RECT2: { Rect2 aabb = p_variant; p_store_string_func(p_store_string_ud, "Rect2(" + rtos_fix(aabb.position.x) + ", " + rtos_fix(aabb.position.y) + ", " + rtos_fix(aabb.size.x) + ", " + rtos_fix(aabb.size.y) + ")"); - } break; case Variant::RECT2I: { Rect2i aabb = p_variant; p_store_string_func(p_store_string_ud, "Rect2i(" + itos(aabb.position.x) + ", " + itos(aabb.position.y) + ", " + itos(aabb.size.x) + ", " + itos(aabb.size.y) + ")"); - } break; case Variant::VECTOR3: { Vector3 v = p_variant; @@ -1771,17 +1771,14 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str case Variant::PLANE: { Plane p = p_variant; p_store_string_func(p_store_string_ud, "Plane(" + rtos_fix(p.normal.x) + ", " + rtos_fix(p.normal.y) + ", " + rtos_fix(p.normal.z) + ", " + rtos_fix(p.d) + ")"); - } break; case Variant::AABB: { AABB aabb = p_variant; p_store_string_func(p_store_string_ud, "AABB(" + rtos_fix(aabb.position.x) + ", " + rtos_fix(aabb.position.y) + ", " + rtos_fix(aabb.position.z) + ", " + rtos_fix(aabb.size.x) + ", " + rtos_fix(aabb.size.y) + ", " + rtos_fix(aabb.size.z) + ")"); - } break; case Variant::QUATERNION: { Quaternion quaternion = p_variant; p_store_string_func(p_store_string_ud, "Quaternion(" + rtos_fix(quaternion.x) + ", " + rtos_fix(quaternion.y) + ", " + rtos_fix(quaternion.z) + ", " + rtos_fix(quaternion.w) + ")"); - } break; case Variant::TRANSFORM2D: { String s = "Transform2D("; @@ -1796,7 +1793,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, s + ")"); - } break; case Variant::BASIS: { String s = "Basis("; @@ -1811,7 +1807,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, s + ")"); - } break; case Variant::TRANSFORM3D: { String s = "Transform3D("; @@ -1845,30 +1840,23 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, s + ")"); } break; - // misc types + // Misc types. case Variant::COLOR: { Color c = p_variant; p_store_string_func(p_store_string_ud, "Color(" + rtos_fix(c.r) + ", " + rtos_fix(c.g) + ", " + rtos_fix(c.b) + ", " + rtos_fix(c.a) + ")"); - } break; case Variant::STRING_NAME: { String str = p_variant; - str = "&\"" + str.c_escape() + "\""; p_store_string_func(p_store_string_ud, str); - } break; case Variant::NODE_PATH: { String str = p_variant; - str = "NodePath(\"" + str.c_escape() + "\")"; p_store_string_func(p_store_string_ud, str); - } break; - case Variant::RID: { RID rid = p_variant; - if (rid == RID()) { p_store_string_func(p_store_string_ud, "RID()"); } else { @@ -1885,6 +1873,13 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::OBJECT: { + if (unlikely(p_recursion_count > MAX_RECURSION)) { + ERR_PRINT("Max recursion reached"); + p_store_string_func(p_store_string_ud, "null"); + return OK; + } + p_recursion_count++; + Object *obj = p_variant.get_validated_object(); if (!obj) { @@ -1934,21 +1929,20 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, "\"" + E.name + "\":"); - write(obj->get(E.name), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud); + write(obj->get(E.name), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); } } p_store_string_func(p_store_string_ud, ")\n"); - } break; case Variant::DICTIONARY: { Dictionary dict = p_variant; - if (recursion_count > MAX_RECURSION) { + if (unlikely(p_recursion_count > MAX_RECURSION)) { ERR_PRINT("Max recursion reached"); p_store_string_func(p_store_string_ud, "{}"); } else { - recursion_count++; + p_recursion_count++; List<Variant> keys; dict.get_key_list(&keys); @@ -1961,9 +1955,9 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, "{\n"); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, recursion_count); + write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); p_store_string_func(p_store_string_ud, ": "); - write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, recursion_count); + write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); if (E->next()) { p_store_string_func(p_store_string_ud, ",\n"); } else { @@ -1973,7 +1967,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, "}"); } - } break; case Variant::ARRAY: { @@ -2009,11 +2002,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, "]("); } - if (recursion_count > MAX_RECURSION) { + if (unlikely(p_recursion_count > MAX_RECURSION)) { ERR_PRINT("Max recursion reached"); p_store_string_func(p_store_string_ud, "[]"); } else { - recursion_count++; + p_recursion_count++; p_store_string_func(p_store_string_ud, "["); int len = array.size(); @@ -2021,7 +2014,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i > 0) { p_store_string_func(p_store_string_ud, ", "); } - write(array[i], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, recursion_count); + write(array[i], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); } p_store_string_func(p_store_string_ud, "]"); @@ -2030,7 +2023,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (array.get_typed_builtin() != Variant::NIL) { p_store_string_func(p_store_string_ud, ")"); } - } break; case Variant::PACKED_BYTE_ARRAY: { @@ -2048,7 +2040,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, ")"); - } break; case Variant::PACKED_INT32_ARRAY: { p_store_string_func(p_store_string_ud, "PackedInt32Array("); @@ -2065,7 +2056,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, ")"); - } break; case Variant::PACKED_INT64_ARRAY: { p_store_string_func(p_store_string_ud, "PackedInt64Array("); @@ -2082,7 +2072,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, ")"); - } break; case Variant::PACKED_FLOAT32_ARRAY: { p_store_string_func(p_store_string_ud, "PackedFloat32Array("); @@ -2098,7 +2087,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, ")"); - } break; case Variant::PACKED_FLOAT64_ARRAY: { p_store_string_func(p_store_string_ud, "PackedFloat64Array("); @@ -2114,7 +2102,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, ")"); - } break; case Variant::PACKED_STRING_ARRAY: { p_store_string_func(p_store_string_ud, "PackedStringArray("); @@ -2130,7 +2117,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, ")"); - } break; case Variant::PACKED_VECTOR2_ARRAY: { p_store_string_func(p_store_string_ud, "PackedVector2Array("); @@ -2146,7 +2132,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, ")"); - } break; case Variant::PACKED_VECTOR3_ARRAY: { p_store_string_func(p_store_string_ud, "PackedVector3Array("); @@ -2162,7 +2147,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, ")"); - } break; case Variant::PACKED_COLOR_ARRAY: { p_store_string_func(p_store_string_ud, "PackedColorArray("); @@ -2178,8 +2162,8 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, ")"); - } break; + default: { ERR_PRINT("Unknown variant type"); return ERR_BUG; diff --git a/core/variant/variant_parser.h b/core/variant/variant_parser.h index 88be99e551..8505fff739 100644 --- a/core/variant/variant_parser.h +++ b/core/variant/variant_parser.h @@ -160,7 +160,7 @@ public: typedef Error (*StoreStringFunc)(void *ud, const String &p_string); typedef String (*EncodeResourceFunc)(void *ud, const Ref<Resource> &p_resource); - static Error write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int recursion_count = 0); + static Error write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count = 0); static Error write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func = nullptr, void *p_encode_res_ud = nullptr); }; diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp index 30fb5d0e9f..05f7abf32c 100644 --- a/core/variant/variant_setget.cpp +++ b/core/variant/variant_setget.cpp @@ -318,7 +318,7 @@ Variant Variant::get_named(const StringName &p_member, bool &r_valid) const { #ifdef DEBUG_ENABLED #define NULL_TEST(m_key) \ - ERR_FAIL_COND(!m_key) + ERR_FAIL_NULL(m_key) #else @@ -1068,7 +1068,7 @@ struct VariantKeyedSetGetObject { } static uint32_t ptr_has(const void *base, const void *key) { const Object *obj = PtrToArg<Object *>::convert(base); - ERR_FAIL_COND_V(!obj, false); + ERR_FAIL_NULL_V(obj, false); bool valid; obj->getvar(PtrToArg<Variant>::convert(key), &valid); return valid; @@ -1245,7 +1245,7 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const { } } else if (type == OBJECT) { Object *obj = get_validated_object(); - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->get_property_list(p_list); } else { diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 545825011a..0a63749ac5 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "variant.h" +#include "variant_utility.h" #include "core/core_string_names.h" #include "core/io/marshalls.h" @@ -40,755 +40,868 @@ #include "core/variant/binder_common.h" #include "core/variant/variant_parser.h" -struct VariantUtilityFunctions { - // Math +// Math +double VariantUtilityFunctions::sin(double arg) { + return Math::sin(arg); +} - static inline double sin(double arg) { - return Math::sin(arg); - } +double VariantUtilityFunctions::cos(double arg) { + return Math::cos(arg); +} - static inline double cos(double arg) { - return Math::cos(arg); - } +double VariantUtilityFunctions::tan(double arg) { + return Math::tan(arg); +} - static inline double tan(double arg) { - return Math::tan(arg); - } +double VariantUtilityFunctions::sinh(double arg) { + return Math::sinh(arg); +} - static inline double sinh(double arg) { - return Math::sinh(arg); - } +double VariantUtilityFunctions::cosh(double arg) { + return Math::cosh(arg); +} - static inline double cosh(double arg) { - return Math::cosh(arg); - } +double VariantUtilityFunctions::tanh(double arg) { + return Math::tanh(arg); +} - static inline double tanh(double arg) { - return Math::tanh(arg); - } +double VariantUtilityFunctions::asin(double arg) { + return Math::asin(arg); +} - static inline double asin(double arg) { - return Math::asin(arg); - } +double VariantUtilityFunctions::acos(double arg) { + return Math::acos(arg); +} - static inline double acos(double arg) { - return Math::acos(arg); - } +double VariantUtilityFunctions::atan(double arg) { + return Math::atan(arg); +} - static inline double atan(double arg) { - return Math::atan(arg); - } +double VariantUtilityFunctions::atan2(double y, double x) { + return Math::atan2(y, x); +} - static inline double atan2(double y, double x) { - return Math::atan2(y, x); - } +double VariantUtilityFunctions::asinh(double arg) { + return Math::asinh(arg); +} - static inline double sqrt(double x) { - return Math::sqrt(x); - } +double VariantUtilityFunctions::acosh(double arg) { + return Math::acosh(arg); +} - static inline double fmod(double b, double r) { - return Math::fmod(b, r); - } +double VariantUtilityFunctions::atanh(double arg) { + return Math::atanh(arg); +} - static inline double fposmod(double b, double r) { - return Math::fposmod(b, r); - } +double VariantUtilityFunctions::sqrt(double x) { + return Math::sqrt(x); +} - static inline int64_t posmod(int64_t b, int64_t r) { - return Math::posmod(b, r); - } +double VariantUtilityFunctions::fmod(double b, double r) { + return Math::fmod(b, r); +} - static inline Variant floor(Variant x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return VariantInternalAccessor<int64_t>::get(&x); - } break; - case Variant::FLOAT: { - return Math::floor(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).floor(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).floor(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).floor(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +double VariantUtilityFunctions::fposmod(double b, double r) { + return Math::fposmod(b, r); +} + +int64_t VariantUtilityFunctions::posmod(int64_t b, int64_t r) { + return Math::posmod(b, r); +} + +Variant VariantUtilityFunctions::floor(Variant x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return VariantInternalAccessor<int64_t>::get(&x); + } break; + case Variant::FLOAT: { + return Math::floor(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).floor(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).floor(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).floor(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double floorf(double x) { - return Math::floor(x); - } +double VariantUtilityFunctions::floorf(double x) { + return Math::floor(x); +} - static inline int64_t floori(double x) { - return int64_t(Math::floor(x)); - } +int64_t VariantUtilityFunctions::floori(double x) { + return int64_t(Math::floor(x)); +} - static inline Variant ceil(Variant x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return VariantInternalAccessor<int64_t>::get(&x); - } break; - case Variant::FLOAT: { - return Math::ceil(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).ceil(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).ceil(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).ceil(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::ceil(Variant x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return VariantInternalAccessor<int64_t>::get(&x); + } break; + case Variant::FLOAT: { + return Math::ceil(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).ceil(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).ceil(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).ceil(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double ceilf(double x) { - return Math::ceil(x); - } +double VariantUtilityFunctions::ceilf(double x) { + return Math::ceil(x); +} - static inline int64_t ceili(double x) { - return int64_t(Math::ceil(x)); - } +int64_t VariantUtilityFunctions::ceili(double x) { + return int64_t(Math::ceil(x)); +} - static inline Variant round(Variant x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return VariantInternalAccessor<int64_t>::get(&x); - } break; - case Variant::FLOAT: { - return Math::round(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).round(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).round(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).round(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::round(Variant x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return VariantInternalAccessor<int64_t>::get(&x); + } break; + case Variant::FLOAT: { + return Math::round(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).round(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).round(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).round(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double roundf(double x) { - return Math::round(x); - } +double VariantUtilityFunctions::roundf(double x) { + return Math::round(x); +} - static inline int64_t roundi(double x) { - return int64_t(Math::round(x)); - } +int64_t VariantUtilityFunctions::roundi(double x) { + return int64_t(Math::round(x)); +} - static inline Variant abs(const Variant &x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return ABS(VariantInternalAccessor<int64_t>::get(&x)); - } break; - case Variant::FLOAT: { - return Math::absd(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).abs(); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor<Vector2i>::get(&x).abs(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).abs(); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor<Vector3i>::get(&x).abs(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).abs(); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor<Vector4i>::get(&x).abs(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::abs(const Variant &x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return ABS(VariantInternalAccessor<int64_t>::get(&x)); + } break; + case Variant::FLOAT: { + return Math::absd(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).abs(); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x).abs(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).abs(); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x).abs(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).abs(); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x).abs(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double absf(double x) { - return Math::absd(x); - } +double VariantUtilityFunctions::absf(double x) { + return Math::absd(x); +} - static inline int64_t absi(int64_t x) { - return ABS(x); - } +int64_t VariantUtilityFunctions::absi(int64_t x) { + return ABS(x); +} - static inline Variant sign(const Variant &x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return SIGN(VariantInternalAccessor<int64_t>::get(&x)); - } break; - case Variant::FLOAT: { - return SIGN(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).sign(); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor<Vector2i>::get(&x).sign(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).sign(); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor<Vector3i>::get(&x).sign(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).sign(); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor<Vector4i>::get(&x).sign(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::sign(const Variant &x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return SIGN(VariantInternalAccessor<int64_t>::get(&x)); + } break; + case Variant::FLOAT: { + return SIGN(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).sign(); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x).sign(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).sign(); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x).sign(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).sign(); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x).sign(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double signf(double x) { - return SIGN(x); - } +double VariantUtilityFunctions::signf(double x) { + return SIGN(x); +} - static inline int64_t signi(int64_t x) { - return SIGN(x); - } +int64_t VariantUtilityFunctions::signi(int64_t x) { + return SIGN(x); +} - static inline double pow(double x, double y) { - return Math::pow(x, y); - } +double VariantUtilityFunctions::pow(double x, double y) { + return Math::pow(x, y); +} - static inline double log(double x) { - return Math::log(x); - } +double VariantUtilityFunctions::log(double x) { + return Math::log(x); +} - static inline double exp(double x) { - return Math::exp(x); - } +double VariantUtilityFunctions::exp(double x) { + return Math::exp(x); +} - static inline bool is_nan(double x) { - return Math::is_nan(x); - } +bool VariantUtilityFunctions::is_nan(double x) { + return Math::is_nan(x); +} - static inline bool is_inf(double x) { - return Math::is_inf(x); - } +bool VariantUtilityFunctions::is_inf(double x) { + return Math::is_inf(x); +} - static inline bool is_equal_approx(double x, double y) { - return Math::is_equal_approx(x, y); - } +bool VariantUtilityFunctions::is_equal_approx(double x, double y) { + return Math::is_equal_approx(x, y); +} - static inline bool is_zero_approx(double x) { - return Math::is_zero_approx(x); - } +bool VariantUtilityFunctions::is_zero_approx(double x) { + return Math::is_zero_approx(x); +} - static inline bool is_finite(double x) { - return Math::is_finite(x); - } +bool VariantUtilityFunctions::is_finite(double x) { + return Math::is_finite(x); +} - static inline double ease(float x, float curve) { - return Math::ease(x, curve); - } +double VariantUtilityFunctions::ease(float x, float curve) { + return Math::ease(x, curve); +} - static inline int step_decimals(float step) { - return Math::step_decimals(step); - } +int VariantUtilityFunctions::step_decimals(float step) { + return Math::step_decimals(step); +} - static inline Variant snapped(const Variant &x, const Variant &step, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - if (x.get_type() != step.get_type() && !((x.get_type() == Variant::INT && step.get_type() == Variant::FLOAT) || (x.get_type() == Variant::FLOAT && step.get_type() == Variant::INT))) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; +Variant VariantUtilityFunctions::snapped(const Variant &x, const Variant &step, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + if (x.get_type() != step.get_type() && !((x.get_type() == Variant::INT && step.get_type() == Variant::FLOAT) || (x.get_type() == Variant::FLOAT && step.get_type() == Variant::INT))) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + return Variant(); + } + + switch (step.get_type()) { + case Variant::INT: { + return snappedi(x, VariantInternalAccessor<int64_t>::get(&step)); + } break; + case Variant::FLOAT: { + return snappedf(x, VariantInternalAccessor<double>::get(&step)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).snapped(VariantInternalAccessor<Vector2>::get(&step)); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x).snapped(VariantInternalAccessor<Vector2i>::get(&step)); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).snapped(VariantInternalAccessor<Vector3>::get(&step)); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x).snapped(VariantInternalAccessor<Vector3i>::get(&step)); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).snapped(VariantInternalAccessor<Vector4>::get(&step)); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x).snapped(VariantInternalAccessor<Vector4i>::get(&step)); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; return Variant(); } - - switch (step.get_type()) { - case Variant::INT: { - return snappedi(x, VariantInternalAccessor<int64_t>::get(&step)); - } break; - case Variant::FLOAT: { - return snappedf(x, VariantInternalAccessor<double>::get(&step)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).snapped(VariantInternalAccessor<Vector2>::get(&step)); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor<Vector2i>::get(&x).snapped(VariantInternalAccessor<Vector2i>::get(&step)); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).snapped(VariantInternalAccessor<Vector3>::get(&step)); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor<Vector3i>::get(&x).snapped(VariantInternalAccessor<Vector3i>::get(&step)); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).snapped(VariantInternalAccessor<Vector4>::get(&step)); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor<Vector4i>::get(&x).snapped(VariantInternalAccessor<Vector4i>::get(&step)); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } - } } +} - static inline double snappedf(double x, double step) { - return Math::snapped(x, step); - } +double VariantUtilityFunctions::snappedf(double x, double step) { + return Math::snapped(x, step); +} - static inline int64_t snappedi(double x, int64_t step) { - return Math::snapped(x, step); - } +int64_t VariantUtilityFunctions::snappedi(double x, int64_t step) { + return Math::snapped(x, step); +} - static inline Variant lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - if (from.get_type() != to.get_type()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = from.get_type(); - r_error.argument = 1; +Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + if (from.get_type() != to.get_type()) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = from.get_type(); + r_error.argument = 1; + return Variant(); + } + + switch (from.get_type()) { + case Variant::INT: { + return lerpf(VariantInternalAccessor<int64_t>::get(&from), to, weight); + } break; + case Variant::FLOAT: { + return lerpf(VariantInternalAccessor<double>::get(&from), to, weight); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&from).lerp(VariantInternalAccessor<Vector2>::get(&to), weight); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&from).lerp(VariantInternalAccessor<Vector3>::get(&to), weight); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&from).lerp(VariantInternalAccessor<Vector4>::get(&to), weight); + } break; + case Variant::QUATERNION: { + return VariantInternalAccessor<Quaternion>::get(&from).slerp(VariantInternalAccessor<Quaternion>::get(&to), weight); + } break; + case Variant::BASIS: { + return VariantInternalAccessor<Basis>::get(&from).slerp(VariantInternalAccessor<Basis>::get(&to), weight); + } break; + case Variant::COLOR: { + return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; return Variant(); } - - switch (from.get_type()) { - case Variant::INT: { - return lerpf(VariantInternalAccessor<int64_t>::get(&from), to, weight); - } break; - case Variant::FLOAT: { - return lerpf(VariantInternalAccessor<double>::get(&from), to, weight); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&from).lerp(VariantInternalAccessor<Vector2>::get(&to), weight); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&from).lerp(VariantInternalAccessor<Vector3>::get(&to), weight); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&from).lerp(VariantInternalAccessor<Vector4>::get(&to), weight); - } break; - case Variant::QUATERNION: { - return VariantInternalAccessor<Quaternion>::get(&from).slerp(VariantInternalAccessor<Quaternion>::get(&to), weight); - } break; - case Variant::BASIS: { - return VariantInternalAccessor<Basis>::get(&from).slerp(VariantInternalAccessor<Basis>::get(&to), weight); - } break; - case Variant::COLOR: { - return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } - } } +} - static inline double lerpf(double from, double to, double weight) { - return Math::lerp(from, to, weight); - } +double VariantUtilityFunctions::lerpf(double from, double to, double weight) { + return Math::lerp(from, to, weight); +} - static inline double cubic_interpolate(double from, double to, double pre, double post, double weight) { - return Math::cubic_interpolate(from, to, pre, post, weight); - } +double VariantUtilityFunctions::cubic_interpolate(double from, double to, double pre, double post, double weight) { + return Math::cubic_interpolate(from, to, pre, post, weight); +} - static inline double cubic_interpolate_angle(double from, double to, double pre, double post, double weight) { - return Math::cubic_interpolate_angle(from, to, pre, post, weight); - } +double VariantUtilityFunctions::cubic_interpolate_angle(double from, double to, double pre, double post, double weight) { + return Math::cubic_interpolate_angle(from, to, pre, post, weight); +} - static inline double cubic_interpolate_in_time(double from, double to, double pre, double post, double weight, - double to_t, double pre_t, double post_t) { - return Math::cubic_interpolate_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); - } +double VariantUtilityFunctions::cubic_interpolate_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t) { + return Math::cubic_interpolate_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); +} - static inline double cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight, - double to_t, double pre_t, double post_t) { - return Math::cubic_interpolate_angle_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); - } +double VariantUtilityFunctions::cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t) { + return Math::cubic_interpolate_angle_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); +} - static inline double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { - return Math::bezier_interpolate(p_start, p_control_1, p_control_2, p_end, p_t); - } +double VariantUtilityFunctions::bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { + return Math::bezier_interpolate(p_start, p_control_1, p_control_2, p_end, p_t); +} - static inline double bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { - return Math::bezier_derivative(p_start, p_control_1, p_control_2, p_end, p_t); - } +double VariantUtilityFunctions::bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { + return Math::bezier_derivative(p_start, p_control_1, p_control_2, p_end, p_t); +} - static inline double lerp_angle(double from, double to, double weight) { - return Math::lerp_angle(from, to, weight); - } +double VariantUtilityFunctions::lerp_angle(double from, double to, double weight) { + return Math::lerp_angle(from, to, weight); +} - static inline double inverse_lerp(double from, double to, double weight) { - return Math::inverse_lerp(from, to, weight); - } +double VariantUtilityFunctions::inverse_lerp(double from, double to, double weight) { + return Math::inverse_lerp(from, to, weight); +} - static inline double remap(double value, double istart, double istop, double ostart, double ostop) { - return Math::remap(value, istart, istop, ostart, ostop); - } +double VariantUtilityFunctions::remap(double value, double istart, double istop, double ostart, double ostop) { + return Math::remap(value, istart, istop, ostart, ostop); +} - static inline double smoothstep(double from, double to, double val) { - return Math::smoothstep(from, to, val); - } +double VariantUtilityFunctions::smoothstep(double from, double to, double val) { + return Math::smoothstep(from, to, val); +} - static inline double move_toward(double from, double to, double delta) { - return Math::move_toward(from, to, delta); - } +double VariantUtilityFunctions::move_toward(double from, double to, double delta) { + return Math::move_toward(from, to, delta); +} - static inline double deg_to_rad(double angle_deg) { - return Math::deg_to_rad(angle_deg); - } +double VariantUtilityFunctions::deg_to_rad(double angle_deg) { + return Math::deg_to_rad(angle_deg); +} - static inline double rad_to_deg(double angle_rad) { - return Math::rad_to_deg(angle_rad); - } +double VariantUtilityFunctions::rad_to_deg(double angle_rad) { + return Math::rad_to_deg(angle_rad); +} - static inline double linear_to_db(double linear) { - return Math::linear_to_db(linear); - } +double VariantUtilityFunctions::linear_to_db(double linear) { + return Math::linear_to_db(linear); +} - static inline double db_to_linear(double db) { - return Math::db_to_linear(db); +double VariantUtilityFunctions::db_to_linear(double db) { + return Math::db_to_linear(db); +} + +Variant VariantUtilityFunctions::wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error) { + Variant::Type x_type = p_x.get_type(); + if (x_type != Variant::INT && x_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = x_type; + return Variant(); } - static inline Variant wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error) { - Variant::Type x_type = p_x.get_type(); - if (x_type != Variant::INT && x_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = x_type; - return Variant(); - } + Variant::Type min_type = p_min.get_type(); + if (min_type != Variant::INT && min_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = x_type; + return Variant(); + } - Variant::Type min_type = p_min.get_type(); - if (min_type != Variant::INT && min_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; - r_error.expected = x_type; - return Variant(); - } + Variant::Type max_type = p_max.get_type(); + if (max_type != Variant::INT && max_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 2; + r_error.expected = x_type; + return Variant(); + } - Variant::Type max_type = p_max.get_type(); - if (max_type != Variant::INT && max_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 2; - r_error.expected = x_type; - return Variant(); - } + Variant value; - Variant value; - - switch (x_type) { - case Variant::INT: { - if (x_type != min_type || x_type != max_type) { - value = wrapf((double)p_x, (double)p_min, (double)p_max); - } else { - value = wrapi((int)p_x, (int)p_min, (int)p_max); - } - } break; - case Variant::FLOAT: { + switch (x_type) { + case Variant::INT: { + if (x_type != min_type || x_type != max_type) { value = wrapf((double)p_x, (double)p_min, (double)p_max); - } break; - default: - break; - } - - r_error.error = Callable::CallError::CALL_OK; - return value; + } else { + value = wrapi((int)p_x, (int)p_min, (int)p_max); + } + } break; + case Variant::FLOAT: { + value = wrapf((double)p_x, (double)p_min, (double)p_max); + } break; + default: + break; } - static inline int64_t wrapi(int64_t value, int64_t min, int64_t max) { - return Math::wrapi(value, min, max); - } + r_error.error = Callable::CallError::CALL_OK; + return value; +} - static inline double wrapf(double value, double min, double max) { - return Math::wrapf(value, min, max); - } +int64_t VariantUtilityFunctions::wrapi(int64_t value, int64_t min, int64_t max) { + return Math::wrapi(value, min, max); +} + +double VariantUtilityFunctions::wrapf(double value, double min, double max) { + return Math::wrapf(value, min, max); +} - static inline double pingpong(double value, double length) { - return Math::pingpong(value, length); +double VariantUtilityFunctions::pingpong(double value, double length) { + return Math::pingpong(value, length); +} + +Variant VariantUtilityFunctions::max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (p_argcount < 2) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.expected = 2; + return Variant(); } + Variant base = *p_args[0]; + Variant ret; - static inline Variant max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.expected = 2; + for (int i = 0; i < p_argcount; i++) { + Variant::Type arg_type = p_args[i]->get_type(); + if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = Variant::FLOAT; + r_error.argument = i; return Variant(); } - Variant base = *p_args[0]; - Variant ret; - - for (int i = 0; i < p_argcount; i++) { - Variant::Type arg_type = p_args[i]->get_type(); - if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = Variant::FLOAT; - r_error.argument = i; - return Variant(); - } - if (i == 0) { - continue; - } - bool valid; - Variant::evaluate(Variant::OP_LESS, base, *p_args[i], ret, valid); - if (!valid) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = base.get_type(); - r_error.argument = i; - return Variant(); - } - if (ret.booleanize()) { - base = *p_args[i]; - } + if (i == 0) { + continue; } - r_error.error = Callable::CallError::CALL_OK; - return base; - } - - static inline double maxf(double x, double y) { - return MAX(x, y); - } - - static inline int64_t maxi(int64_t x, int64_t y) { - return MAX(x, y); - } - - static inline Variant min(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.expected = 2; + bool valid; + Variant::evaluate(Variant::OP_LESS, base, *p_args[i], ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = base.get_type(); + r_error.argument = i; return Variant(); } - Variant base = *p_args[0]; - Variant ret; - - for (int i = 0; i < p_argcount; i++) { - Variant::Type arg_type = p_args[i]->get_type(); - if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = Variant::FLOAT; - r_error.argument = i; - return Variant(); - } - if (i == 0) { - continue; - } - bool valid; - Variant::evaluate(Variant::OP_GREATER, base, *p_args[i], ret, valid); - if (!valid) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = base.get_type(); - r_error.argument = i; - return Variant(); - } - if (ret.booleanize()) { - base = *p_args[i]; - } + if (ret.booleanize()) { + base = *p_args[i]; } - r_error.error = Callable::CallError::CALL_OK; - return base; - } - - static inline double minf(double x, double y) { - return MIN(x, y); } + r_error.error = Callable::CallError::CALL_OK; + return base; +} - static inline int64_t mini(int64_t x, int64_t y) { - return MIN(x, y); - } +double VariantUtilityFunctions::maxf(double x, double y) { + return MAX(x, y); +} - static inline Variant clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error) { - Variant value = x; +int64_t VariantUtilityFunctions::maxi(int64_t x, int64_t y) { + return MAX(x, y); +} - Variant ret; +Variant VariantUtilityFunctions::min(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (p_argcount < 2) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.expected = 2; + return Variant(); + } + Variant base = *p_args[0]; + Variant ret; - bool valid; - Variant::evaluate(Variant::OP_LESS, value, min, ret, valid); - if (!valid) { + for (int i = 0; i < p_argcount; i++) { + Variant::Type arg_type = p_args[i]->get_type(); + if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = value.get_type(); - r_error.argument = 1; + r_error.expected = Variant::FLOAT; + r_error.argument = i; return Variant(); } - if (ret.booleanize()) { - value = min; + if (i == 0) { + continue; } - Variant::evaluate(Variant::OP_GREATER, value, max, ret, valid); + bool valid; + Variant::evaluate(Variant::OP_GREATER, base, *p_args[i], ret, valid); if (!valid) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = value.get_type(); - r_error.argument = 2; + r_error.expected = base.get_type(); + r_error.argument = i; return Variant(); } if (ret.booleanize()) { - value = max; + base = *p_args[i]; } + } + r_error.error = Callable::CallError::CALL_OK; + return base; +} - r_error.error = Callable::CallError::CALL_OK; +double VariantUtilityFunctions::minf(double x, double y) { + return MIN(x, y); +} - return value; - } +int64_t VariantUtilityFunctions::mini(int64_t x, int64_t y) { + return MIN(x, y); +} - static inline double clampf(double x, double min, double max) { - return CLAMP(x, min, max); - } +Variant VariantUtilityFunctions::clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error) { + Variant value = x; - static inline int64_t clampi(int64_t x, int64_t min, int64_t max) { - return CLAMP(x, min, max); - } + Variant ret; - static inline int64_t nearest_po2(int64_t x) { - return nearest_power_of_2_templated(uint64_t(x)); + bool valid; + Variant::evaluate(Variant::OP_LESS, value, min, ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = value.get_type(); + r_error.argument = 1; + return Variant(); + } + if (ret.booleanize()) { + value = min; + } + Variant::evaluate(Variant::OP_GREATER, value, max, ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = value.get_type(); + r_error.argument = 2; + return Variant(); + } + if (ret.booleanize()) { + value = max; } - // Random + r_error.error = Callable::CallError::CALL_OK; - static inline void randomize() { - Math::randomize(); - } + return value; +} - static inline int64_t randi() { - return Math::rand(); - } +double VariantUtilityFunctions::clampf(double x, double min, double max) { + return CLAMP(x, min, max); +} - static inline double randf() { - return Math::randf(); - } +int64_t VariantUtilityFunctions::clampi(int64_t x, int64_t min, int64_t max) { + return CLAMP(x, min, max); +} - static inline double randfn(double mean, double deviation) { - return Math::randfn(mean, deviation); - } +int64_t VariantUtilityFunctions::nearest_po2(int64_t x) { + return nearest_power_of_2_templated(uint64_t(x)); +} - static inline int64_t randi_range(int64_t from, int64_t to) { - return Math::random((int32_t)from, (int32_t)to); - } +// Random - static inline double randf_range(double from, double to) { - return Math::random(from, to); - } +void VariantUtilityFunctions::randomize() { + Math::randomize(); +} - static inline void seed(int64_t s) { - return Math::seed(s); - } +int64_t VariantUtilityFunctions::randi() { + return Math::rand(); +} - static inline PackedInt64Array rand_from_seed(int64_t seed) { - uint64_t s = seed; - PackedInt64Array arr; - arr.resize(2); - arr.write[0] = Math::rand_from_seed(&s); - arr.write[1] = s; - return arr; - } +double VariantUtilityFunctions::randf() { + return Math::randf(); +} - // Utility +double VariantUtilityFunctions::randfn(double mean, double deviation) { + return Math::randfn(mean, deviation); +} - static inline Variant weakref(const Variant &obj, Callable::CallError &r_error) { - if (obj.get_type() == Variant::OBJECT) { - r_error.error = Callable::CallError::CALL_OK; - if (obj.is_ref_counted()) { - Ref<WeakRef> wref = memnew(WeakRef); - Ref<RefCounted> r = obj; - if (r.is_valid()) { - wref->set_ref(r); - } - return wref; - } else { - Ref<WeakRef> wref = memnew(WeakRef); - Object *o = obj.get_validated_object(); - if (o) { - wref->set_obj(o); - } - return wref; - } - } else if (obj.get_type() == Variant::NIL) { - r_error.error = Callable::CallError::CALL_OK; +int64_t VariantUtilityFunctions::randi_range(int64_t from, int64_t to) { + return Math::random((int32_t)from, (int32_t)to); +} + +double VariantUtilityFunctions::randf_range(double from, double to) { + return Math::random(from, to); +} + +void VariantUtilityFunctions::seed(int64_t s) { + return Math::seed(s); +} + +PackedInt64Array VariantUtilityFunctions::rand_from_seed(int64_t seed) { + uint64_t s = seed; + PackedInt64Array arr; + arr.resize(2); + arr.write[0] = Math::rand_from_seed(&s); + arr.write[1] = s; + return arr; +} + +// Utility + +Variant VariantUtilityFunctions::weakref(const Variant &obj, Callable::CallError &r_error) { + if (obj.get_type() == Variant::OBJECT) { + r_error.error = Callable::CallError::CALL_OK; + if (obj.is_ref_counted()) { Ref<WeakRef> wref = memnew(WeakRef); + Ref<RefCounted> r = obj; + if (r.is_valid()) { + wref->set_ref(r); + } return wref; } else { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - return Variant(); + Ref<WeakRef> wref = memnew(WeakRef); + Object *o = obj.get_validated_object(); + if (o) { + wref->set_obj(o); + } + return wref; } + } else if (obj.get_type() == Variant::NIL) { + r_error.error = Callable::CallError::CALL_OK; + Ref<WeakRef> wref = memnew(WeakRef); + return wref; + } else { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + return Variant(); } +} - static inline int64_t _typeof(const Variant &obj) { - return obj.get_type(); +int64_t VariantUtilityFunctions::_typeof(const Variant &obj) { + return obj.get_type(); +} + +Variant VariantUtilityFunctions::type_convert(const Variant &p_variant, const Variant::Type p_type) { + switch (p_type) { + case Variant::Type::NIL: + return Variant(); + case Variant::Type::BOOL: + return p_variant.operator bool(); + case Variant::Type::INT: + return p_variant.operator int64_t(); + case Variant::Type::FLOAT: + return p_variant.operator double(); + case Variant::Type::STRING: + return p_variant.operator String(); + case Variant::Type::VECTOR2: + return p_variant.operator Vector2(); + case Variant::Type::VECTOR2I: + return p_variant.operator Vector2i(); + case Variant::Type::RECT2: + return p_variant.operator Rect2(); + case Variant::Type::RECT2I: + return p_variant.operator Rect2i(); + case Variant::Type::VECTOR3: + return p_variant.operator Vector3(); + case Variant::Type::VECTOR3I: + return p_variant.operator Vector3i(); + case Variant::Type::TRANSFORM2D: + return p_variant.operator Transform2D(); + case Variant::Type::VECTOR4: + return p_variant.operator Vector4(); + case Variant::Type::VECTOR4I: + return p_variant.operator Vector4i(); + case Variant::Type::PLANE: + return p_variant.operator Plane(); + case Variant::Type::QUATERNION: + return p_variant.operator Quaternion(); + case Variant::Type::AABB: + return p_variant.operator ::AABB(); + case Variant::Type::BASIS: + return p_variant.operator Basis(); + case Variant::Type::TRANSFORM3D: + return p_variant.operator Transform3D(); + case Variant::Type::PROJECTION: + return p_variant.operator Projection(); + case Variant::Type::COLOR: + return p_variant.operator Color(); + case Variant::Type::STRING_NAME: + return p_variant.operator StringName(); + case Variant::Type::NODE_PATH: + return p_variant.operator NodePath(); + case Variant::Type::RID: + return p_variant.operator ::RID(); + case Variant::Type::OBJECT: + return p_variant.operator Object *(); + case Variant::Type::CALLABLE: + return p_variant.operator Callable(); + case Variant::Type::SIGNAL: + return p_variant.operator Signal(); + case Variant::Type::DICTIONARY: + return p_variant.operator Dictionary(); + case Variant::Type::ARRAY: + return p_variant.operator Array(); + case Variant::Type::PACKED_BYTE_ARRAY: + return p_variant.operator PackedByteArray(); + case Variant::Type::PACKED_INT32_ARRAY: + return p_variant.operator PackedInt32Array(); + case Variant::Type::PACKED_INT64_ARRAY: + return p_variant.operator PackedInt64Array(); + case Variant::Type::PACKED_FLOAT32_ARRAY: + return p_variant.operator PackedFloat32Array(); + case Variant::Type::PACKED_FLOAT64_ARRAY: + return p_variant.operator PackedFloat64Array(); + case Variant::Type::PACKED_STRING_ARRAY: + return p_variant.operator PackedStringArray(); + case Variant::Type::PACKED_VECTOR2_ARRAY: + return p_variant.operator PackedVector2Array(); + case Variant::Type::PACKED_VECTOR3_ARRAY: + return p_variant.operator PackedVector3Array(); + case Variant::Type::PACKED_COLOR_ARRAY: + return p_variant.operator PackedColorArray(); + case Variant::Type::VARIANT_MAX: + ERR_PRINT("Invalid type argument to type_convert(), use the TYPE_* constants. Returning the unconverted Variant."); + } + return p_variant; +} + +String VariantUtilityFunctions::str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + return String(); } + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - static inline String str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - return String(); + if (i == 0) { + s = os; + } else { + s += os; } - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + } - if (i == 0) { - s = os; - } else { - s += os; - } - } + r_error.error = Callable::CallError::CALL_OK; - r_error.error = Callable::CallError::CALL_OK; + return s; +} - return s; +String VariantUtilityFunctions::error_string(Error error) { + if (error < 0 || error >= ERR_MAX) { + return String("(invalid error code)"); } - static inline String error_string(Error error) { - if (error < 0 || error >= ERR_MAX) { - return String("(invalid error code)"); - } + return String(error_names[error]); +} - return String(error_names[error]); +void VariantUtilityFunctions::print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + s = os; + } else { + s += os; + } } - static inline void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + print_line(s); + r_error.error = Callable::CallError::CALL_OK; +} - if (i == 0) { - s = os; - } else { - s += os; - } - } +void VariantUtilityFunctions::print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - print_line(s); - r_error.error = Callable::CallError::CALL_OK; + if (i == 0) { + s = os; + } else { + s += os; + } } - static inline void print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + print_line_rich(s); + r_error.error = Callable::CallError::CALL_OK; +} + +#undef print_verbose + +void VariantUtilityFunctions::print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (OS::get_singleton()->is_stdout_verbose()) { String s; for (int i = 0; i < p_arg_count; i++) { String os = p_args[i]->operator String(); @@ -800,247 +913,227 @@ struct VariantUtilityFunctions { } } - print_line_rich(s); - r_error.error = Callable::CallError::CALL_OK; + // No need to use `print_verbose()` as this call already only happens + // when verbose mode is enabled. This avoids performing string argument concatenation + // when not needed. + print_line(s); } -#undef print_verbose + r_error.error = Callable::CallError::CALL_OK; +} - static inline void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (OS::get_singleton()->is_stdout_verbose()) { - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); - - if (i == 0) { - s = os; - } else { - s += os; - } - } +void VariantUtilityFunctions::printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - // No need to use `print_verbose()` as this call already only happens - // when verbose mode is enabled. This avoids performing string argument concatenation - // when not needed. - print_line(s); + if (i == 0) { + s = os; + } else { + s += os; } - - r_error.error = Callable::CallError::CALL_OK; } - static inline void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + print_error(s); + r_error.error = Callable::CallError::CALL_OK; +} - if (i == 0) { - s = os; - } else { - s += os; - } +void VariantUtilityFunctions::printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + if (i) { + s += "\t"; } - - print_error(s); - r_error.error = Callable::CallError::CALL_OK; + s += p_args[i]->operator String(); } - static inline void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - if (i) { - s += "\t"; - } - s += p_args[i]->operator String(); - } - - print_line(s); - r_error.error = Callable::CallError::CALL_OK; - } + print_line(s); + r_error.error = Callable::CallError::CALL_OK; +} - static inline void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - if (i) { - s += " "; - } - s += p_args[i]->operator String(); +void VariantUtilityFunctions::prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + if (i) { + s += " "; } - - print_line(s); - r_error.error = Callable::CallError::CALL_OK; + s += p_args[i]->operator String(); } - static inline void printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + print_line(s); + r_error.error = Callable::CallError::CALL_OK; +} - if (i == 0) { - s = os; - } else { - s += os; - } +void VariantUtilityFunctions::printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + s = os; + } else { + s += os; } + } - OS::get_singleton()->print("%s", s.utf8().get_data()); - r_error.error = Callable::CallError::CALL_OK; + OS::get_singleton()->print("%s", s.utf8().get_data()); + r_error.error = Callable::CallError::CALL_OK; +} + +void VariantUtilityFunctions::push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; } + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - static inline void push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; + if (i == 0) { + s = os; + } else { + s += os; } - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + } - if (i == 0) { - s = os; - } else { - s += os; - } - } + ERR_PRINT(s); + r_error.error = Callable::CallError::CALL_OK; +} - ERR_PRINT(s); - r_error.error = Callable::CallError::CALL_OK; +void VariantUtilityFunctions::push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; } + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - static inline void push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; + if (i == 0) { + s = os; + } else { + s += os; } - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + } - if (i == 0) { - s = os; - } else { - s += os; - } - } + WARN_PRINT(s); + r_error.error = Callable::CallError::CALL_OK; +} - WARN_PRINT(s); - r_error.error = Callable::CallError::CALL_OK; - } +String VariantUtilityFunctions::var_to_str(const Variant &p_var) { + String vars; + VariantWriter::write_to_string(p_var, vars); + return vars; +} - static inline String var_to_str(const Variant &p_var) { - String vars; - VariantWriter::write_to_string(p_var, vars); - return vars; - } +Variant VariantUtilityFunctions::str_to_var(const String &p_var) { + VariantParser::StreamString ss; + ss.s = p_var; - static inline Variant str_to_var(const String &p_var) { - VariantParser::StreamString ss; - ss.s = p_var; + String errs; + int line; + Variant ret; + (void)VariantParser::parse(&ss, ret, errs, line); - String errs; - int line; - Variant ret; - (void)VariantParser::parse(&ss, ret, errs, line); + return ret; +} - return ret; +PackedByteArray VariantUtilityFunctions::var_to_bytes(const Variant &p_var) { + int len; + Error err = encode_variant(p_var, nullptr, len, false); + if (err != OK) { + return PackedByteArray(); } - static inline PackedByteArray var_to_bytes(const Variant &p_var) { - int len; - Error err = encode_variant(p_var, nullptr, len, false); + PackedByteArray barr; + barr.resize(len); + { + uint8_t *w = barr.ptrw(); + err = encode_variant(p_var, w, len, false); if (err != OK) { return PackedByteArray(); } + } - PackedByteArray barr; - barr.resize(len); - { - uint8_t *w = barr.ptrw(); - err = encode_variant(p_var, w, len, false); - if (err != OK) { - return PackedByteArray(); - } - } + return barr; +} - return barr; +PackedByteArray VariantUtilityFunctions::var_to_bytes_with_objects(const Variant &p_var) { + int len; + Error err = encode_variant(p_var, nullptr, len, true); + if (err != OK) { + return PackedByteArray(); } - static inline PackedByteArray var_to_bytes_with_objects(const Variant &p_var) { - int len; - Error err = encode_variant(p_var, nullptr, len, true); + PackedByteArray barr; + barr.resize(len); + { + uint8_t *w = barr.ptrw(); + err = encode_variant(p_var, w, len, true); if (err != OK) { return PackedByteArray(); } - - PackedByteArray barr; - barr.resize(len); - { - uint8_t *w = barr.ptrw(); - err = encode_variant(p_var, w, len, true); - if (err != OK) { - return PackedByteArray(); - } - } - - return barr; } - static inline Variant bytes_to_var(const PackedByteArray &p_arr) { - Variant ret; - { - const uint8_t *r = p_arr.ptr(); - Error err = decode_variant(ret, r, p_arr.size(), nullptr, false); - if (err != OK) { - return Variant(); - } + return barr; +} + +Variant VariantUtilityFunctions::bytes_to_var(const PackedByteArray &p_arr) { + Variant ret; + { + const uint8_t *r = p_arr.ptr(); + Error err = decode_variant(ret, r, p_arr.size(), nullptr, false); + if (err != OK) { + return Variant(); } - return ret; } + return ret; +} - static inline Variant bytes_to_var_with_objects(const PackedByteArray &p_arr) { - Variant ret; - { - const uint8_t *r = p_arr.ptr(); - Error err = decode_variant(ret, r, p_arr.size(), nullptr, true); - if (err != OK) { - return Variant(); - } +Variant VariantUtilityFunctions::bytes_to_var_with_objects(const PackedByteArray &p_arr) { + Variant ret; + { + const uint8_t *r = p_arr.ptr(); + Error err = decode_variant(ret, r, p_arr.size(), nullptr, true); + if (err != OK) { + return Variant(); } - return ret; } + return ret; +} - static inline int64_t hash(const Variant &p_arr) { - return p_arr.hash(); - } +int64_t VariantUtilityFunctions::hash(const Variant &p_arr) { + return p_arr.hash(); +} - static inline Object *instance_from_id(int64_t p_id) { - ObjectID id = ObjectID((uint64_t)p_id); - Object *ret = ObjectDB::get_instance(id); - return ret; - } +Object *VariantUtilityFunctions::instance_from_id(int64_t p_id) { + ObjectID id = ObjectID((uint64_t)p_id); + Object *ret = ObjectDB::get_instance(id); + return ret; +} - static inline bool is_instance_id_valid(int64_t p_id) { - return ObjectDB::get_instance(ObjectID((uint64_t)p_id)) != nullptr; - } +bool VariantUtilityFunctions::is_instance_id_valid(int64_t p_id) { + return ObjectDB::get_instance(ObjectID((uint64_t)p_id)) != nullptr; +} - static inline bool is_instance_valid(const Variant &p_instance) { - if (p_instance.get_type() != Variant::OBJECT) { - return false; - } - return p_instance.get_validated_object() != nullptr; +bool VariantUtilityFunctions::is_instance_valid(const Variant &p_instance) { + if (p_instance.get_type() != Variant::OBJECT) { + return false; } + return p_instance.get_validated_object() != nullptr; +} - static inline uint64_t rid_allocate_id() { - return RID_AllocBase::_gen_id(); - } +uint64_t VariantUtilityFunctions::rid_allocate_id() { + return RID_AllocBase::_gen_id(); +} - static inline RID rid_from_int64(uint64_t p_base) { - return RID::from_uint64(p_base); - } +RID VariantUtilityFunctions::rid_from_int64(uint64_t p_base) { + return RID::from_uint64(p_base); +} - static inline bool is_same(const Variant &p_a, const Variant &p_b) { - return p_a.identity_compare(p_b); - } -}; +bool VariantUtilityFunctions::is_same(const Variant &p_a, const Variant &p_b) { + return p_a.identity_compare(p_b); +} #ifdef DEBUG_METHODS_ENABLED #define VCALLR *ret = p_func(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...) @@ -1505,6 +1598,10 @@ void Variant::_register_variant_utility_functions() { FUNCBINDR(atan2, sarray("y", "x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(asinh, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(acosh, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(atanh, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); + FUNCBINDR(sqrt, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH); FUNCBINDR(fmod, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH); FUNCBINDR(fposmod, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH); @@ -1602,6 +1699,7 @@ void Variant::_register_variant_utility_functions() { FUNCBINDVR(weakref, sarray("obj"), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDR(_typeof, sarray("variable"), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDR(type_convert, sarray("variant", "type"), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGS(str, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDR(error_string, sarray("error"), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(print, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); @@ -1775,7 +1873,7 @@ bool Variant::is_utility_function_vararg(const StringName &p_name) { uint32_t Variant::get_utility_function_hash(const StringName &p_name) { const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); - ERR_FAIL_COND_V(!bfi, 0); + ERR_FAIL_NULL_V(bfi, 0); uint32_t hash = hash_murmur3_one_32(bfi->is_vararg); hash = hash_murmur3_one_32(bfi->returns_value, hash); diff --git a/core/variant/variant_utility.h b/core/variant/variant_utility.h new file mode 100644 index 0000000000..66883fb140 --- /dev/null +++ b/core/variant/variant_utility.h @@ -0,0 +1,156 @@ +/**************************************************************************/ +/* variant_utility.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 VARIANT_UTILITY_H +#define VARIANT_UTILITY_H + +#include "variant.h" + +struct VariantUtilityFunctions { + // Math + static double sin(double arg); + static double cos(double arg); + static double tan(double arg); + static double sinh(double arg); + static double cosh(double arg); + static double tanh(double arg); + static double asin(double arg); + static double acos(double arg); + static double atan(double arg); + static double atan2(double y, double x); + static double asinh(double arg); + static double acosh(double arg); + static double atanh(double arg); + static double sqrt(double x); + static double fmod(double b, double r); + static double fposmod(double b, double r); + static int64_t posmod(int64_t b, int64_t r); + static Variant floor(Variant x, Callable::CallError &r_error); + static double floorf(double x); + static int64_t floori(double x); + static Variant ceil(Variant x, Callable::CallError &r_error); + static double ceilf(double x); + static int64_t ceili(double x); + static Variant round(Variant x, Callable::CallError &r_error); + static double roundf(double x); + static int64_t roundi(double x); + static Variant abs(const Variant &x, Callable::CallError &r_error); + static double absf(double x); + static int64_t absi(int64_t x); + static Variant sign(const Variant &x, Callable::CallError &r_error); + static double signf(double x); + static int64_t signi(int64_t x); + static double pow(double x, double y); + static double log(double x); + static double exp(double x); + static bool is_nan(double x); + static bool is_inf(double x); + static bool is_equal_approx(double x, double y); + static bool is_zero_approx(double x); + static bool is_finite(double x); + static double ease(float x, float curve); + static int step_decimals(float step); + static Variant snapped(const Variant &x, const Variant &step, Callable::CallError &r_error); + static double snappedf(double x, double step); + static int64_t snappedi(double x, int64_t step); + static Variant lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error); + static double lerpf(double from, double to, double weight); + static double cubic_interpolate(double from, double to, double pre, double post, double weight); + static double cubic_interpolate_angle(double from, double to, double pre, double post, double weight); + static double cubic_interpolate_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t); + static double cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t); + static double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t); + static double bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t); + static double lerp_angle(double from, double to, double weight); + static double inverse_lerp(double from, double to, double weight); + static double remap(double value, double istart, double istop, double ostart, double ostop); + static double smoothstep(double from, double to, double val); + static double move_toward(double from, double to, double delta); + static double deg_to_rad(double angle_deg); + static double rad_to_deg(double angle_rad); + static double linear_to_db(double linear); + static double db_to_linear(double db); + static Variant wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error); + static int64_t wrapi(int64_t value, int64_t min, int64_t max); + static double wrapf(double value, double min, double max); + static double pingpong(double value, double length); + static Variant max(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + static double maxf(double x, double y); + static int64_t maxi(int64_t x, int64_t y); + static Variant min(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + static double minf(double x, double y); + static int64_t mini(int64_t x, int64_t y); + static Variant clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error); + static double clampf(double x, double min, double max); + static int64_t clampi(int64_t x, int64_t min, int64_t max); + static int64_t nearest_po2(int64_t x); + // Random + static void randomize(); + static int64_t randi(); + static double randf(); + static double randfn(double mean, double deviation); + static int64_t randi_range(int64_t from, int64_t to); + static double randf_range(double from, double to); + static void seed(int64_t s); + static PackedInt64Array rand_from_seed(int64_t seed); + // Utility + static Variant weakref(const Variant &obj, Callable::CallError &r_error); + static int64_t _typeof(const Variant &obj); + static Variant type_convert(const Variant &p_variant, const Variant::Type p_type); + static String str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static String error_string(Error error); + static void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); +#undef print_verbose + static void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static String var_to_str(const Variant &p_var); + static Variant str_to_var(const String &p_var); + static PackedByteArray var_to_bytes(const Variant &p_var); + static PackedByteArray var_to_bytes_with_objects(const Variant &p_var); + static Variant bytes_to_var(const PackedByteArray &p_arr); + static Variant bytes_to_var_with_objects(const PackedByteArray &p_arr); + static int64_t hash(const Variant &p_arr); + static Object *instance_from_id(int64_t p_id); + static bool is_instance_id_valid(int64_t p_id); + static bool is_instance_valid(const Variant &p_instance); + static uint64_t rid_allocate_id(); + static RID rid_from_int64(uint64_t p_base); + static bool is_same(const Variant &p_a, const Variant &p_b); +}; + +#endif // VARIANT_UTILITY_H |