summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/config/engine.cpp12
-rw-r--r--core/config/engine.h6
-rw-r--r--core/config/project_settings.cpp60
-rw-r--r--core/config/project_settings.h8
-rw-r--r--core/core_bind.cpp19
-rw-r--r--core/core_bind.h2
-rw-r--r--core/core_constants.cpp8
-rw-r--r--core/crypto/crypto_core.cpp2
-rw-r--r--core/crypto/hashing_context.cpp6
-rw-r--r--core/debugger/debugger_marshalls.cpp10
-rw-r--r--core/debugger/debugger_marshalls.h1
-rw-r--r--core/debugger/engine_debugger.cpp8
-rw-r--r--core/debugger/engine_debugger.h8
-rw-r--r--core/debugger/engine_profiler.h4
-rw-r--r--core/debugger/remote_debugger.cpp113
-rw-r--r--core/debugger/remote_debugger.h11
-rw-r--r--core/debugger/script_debugger.cpp22
-rw-r--r--core/debugger/script_debugger.h23
-rw-r--r--core/doc_data.h44
-rw-r--r--core/error/error_macros.h15
-rw-r--r--core/extension/extension_api_dump.cpp3
-rw-r--r--core/extension/gdextension.cpp126
-rw-r--r--core/extension/gdextension.h19
-rw-r--r--core/extension/gdextension_interface.cpp105
-rw-r--r--core/extension/gdextension_interface.h184
-rw-r--r--core/extension/gdextension_manager.cpp2
-rw-r--r--core/input/godotcontrollerdb.txt3
-rw-r--r--core/input/input.cpp88
-rw-r--r--core/input/input.h7
-rw-r--r--core/input/input_builders.py1
-rw-r--r--core/input/input_event.cpp22
-rw-r--r--core/io/compression.cpp2
-rw-r--r--core/io/file_access.cpp74
-rw-r--r--core/io/file_access.h34
-rw-r--r--core/io/file_access_compressed.cpp32
-rw-r--r--core/io/file_access_compressed.h9
-rw-r--r--core/io/file_access_encrypted.cpp41
-rw-r--r--core/io/file_access_encrypted.h9
-rw-r--r--core/io/file_access_memory.cpp16
-rw-r--r--core/io/file_access_memory.h9
-rw-r--r--core/io/file_access_pack.h9
-rw-r--r--core/io/file_access_zip.cpp4
-rw-r--r--core/io/file_access_zip.h9
-rw-r--r--core/io/image.cpp26
-rw-r--r--core/io/image.h2
-rw-r--r--core/io/ip.cpp2
-rw-r--r--core/io/marshalls.cpp4
-rw-r--r--core/io/packed_data_container.cpp6
-rw-r--r--core/io/packet_peer.h1
-rw-r--r--core/io/pck_packer.cpp4
-rw-r--r--core/io/remote_filesystem_client.cpp2
-rw-r--r--core/io/remote_filesystem_client.h4
-rw-r--r--core/io/resource.cpp5
-rw-r--r--core/io/resource.h2
-rw-r--r--core/io/resource_format_binary.cpp1
-rw-r--r--core/io/resource_loader.cpp7
-rw-r--r--core/io/resource_loader.h1
-rw-r--r--core/io/resource_saver.cpp2
-rw-r--r--core/io/resource_saver.h1
-rw-r--r--core/io/stream_peer.h1
-rw-r--r--core/io/stream_peer_gzip.cpp4
-rw-r--r--core/io/xml_parser.cpp4
-rw-r--r--core/io/zip_io.cpp14
-rw-r--r--core/math/a_star.h1
-rw-r--r--core/math/a_star_grid_2d.cpp34
-rw-r--r--core/math/a_star_grid_2d.h4
-rw-r--r--core/math/delaunay_3d.h13
-rw-r--r--core/math/disjoint_set.h2
-rw-r--r--core/math/dynamic_bvh.h2
-rw-r--r--core/math/math_funcs.h11
-rw-r--r--core/object/callable_method_pointer.cpp22
-rw-r--r--core/object/class_db.cpp79
-rw-r--r--core/object/class_db.h36
-rw-r--r--core/object/make_virtuals.py2
-rw-r--r--core/object/object.cpp87
-rw-r--r--core/object/object.h10
-rw-r--r--core/object/ref_counted.h2
-rw-r--r--core/object/script_instance.cpp71
-rw-r--r--core/object/script_instance.h98
-rw-r--r--core/object/script_language.cpp36
-rw-r--r--core/object/script_language.h68
-rw-r--r--core/object/script_language_extension.cpp1
-rw-r--r--core/object/script_language_extension.h48
-rw-r--r--core/object/undo_redo.cpp8
-rw-r--r--core/object/worker_thread_pool.cpp4
-rw-r--r--core/object/worker_thread_pool.h21
-rw-r--r--core/os/keyboard.cpp3
-rw-r--r--core/os/keyboard.h2
-rw-r--r--core/os/main_loop.h1
-rw-r--r--core/os/memory.cpp6
-rw-r--r--core/os/memory.h2
-rw-r--r--core/os/os.h4
-rw-r--r--core/os/pool_allocator.cpp14
-rw-r--r--core/string/locales.h6
-rw-r--r--core/string/node_path.cpp8
-rw-r--r--core/string/print_string.cpp2
-rw-r--r--core/string/string_name.cpp4
-rw-r--r--core/string/translation.cpp9
-rw-r--r--core/string/translation.h3
-rw-r--r--core/string/ustring.cpp37
-rw-r--r--core/string/ustring.h1
-rw-r--r--core/templates/command_queue_mt.h2
-rw-r--r--core/templates/cowdata.h6
-rw-r--r--core/templates/list.h2
-rw-r--r--core/templates/paged_array.h6
-rw-r--r--core/templates/rid_owner.h6
-rw-r--r--core/typedefs.h2
-rw-r--r--core/variant/array.cpp2
-rw-r--r--core/variant/callable.cpp8
-rw-r--r--core/variant/callable_bind.cpp4
-rw-r--r--core/variant/container_type_validate.h2
-rw-r--r--core/variant/dictionary.cpp2
-rw-r--r--core/variant/typed_array.h3
-rw-r--r--core/variant/variant.cpp18
-rw-r--r--core/variant/variant_call.cpp39
-rw-r--r--core/variant/variant_op.h12
-rw-r--r--core/variant/variant_parser.cpp58
-rw-r--r--core/variant/variant_parser.h2
-rw-r--r--core/variant/variant_setget.cpp6
-rw-r--r--core/variant/variant_utility.cpp1672
-rw-r--r--core/variant/variant_utility.h156
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