summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/config/engine.cpp4
-rw-r--r--core/config/engine.h2
-rw-r--r--core/config/project_settings.cpp38
-rw-r--r--core/config/project_settings.h6
-rw-r--r--core/core_bind.cpp19
-rw-r--r--core/core_bind.h2
-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/remote_debugger.cpp109
-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/gdextension.cpp62
-rw-r--r--core/extension/gdextension.h8
-rw-r--r--core/extension/gdextension_interface.cpp26
-rw-r--r--core/extension/gdextension_interface.h49
-rw-r--r--core/input/input.cpp12
-rw-r--r--core/input/input.h4
-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.h9
-rw-r--r--core/io/file_access_pack.h9
-rw-r--r--core/io/file_access_zip.h9
-rw-r--r--core/io/image.cpp10
-rw-r--r--core/io/image.h2
-rw-r--r--core/io/remote_filesystem_client.cpp2
-rw-r--r--core/io/remote_filesystem_client.h4
-rw-r--r--core/io/resource_loader.cpp4
-rw-r--r--core/math/a_star_grid_2d.cpp34
-rw-r--r--core/math/a_star_grid_2d.h3
-rw-r--r--core/math/delaunay_3d.h13
-rw-r--r--core/object/callable_method_pointer.cpp22
-rw-r--r--core/object/class_db.cpp4
-rw-r--r--core/object/class_db.h4
-rw-r--r--core/object/object.cpp24
-rw-r--r--core/object/worker_thread_pool.h21
-rw-r--r--core/string/locales.h6
-rw-r--r--core/string/translation.cpp9
-rw-r--r--core/string/translation.h2
-rw-r--r--core/string/ustring.cpp19
-rw-r--r--core/string/ustring.h1
-rw-r--r--core/variant/variant_call.cpp1
-rw-r--r--core/variant/variant_op.h8
-rw-r--r--core/variant/variant_utility.cpp1571
-rw-r--r--core/variant/variant_utility.h153
53 files changed, 1663 insertions, 963 deletions
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 7fdea7d1aa..6727c58fd1 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -239,6 +239,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..73d40d50ae 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -67,6 +67,7 @@ private:
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;
@@ -156,6 +157,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..1bfb745662 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://");
@@ -283,6 +283,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 +325,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
}
}
+ _queue_changed();
return true;
}
@@ -424,6 +426,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 +1243,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 +1280,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 +1349,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);
diff --git a/core/config/project_settings.h b/core/config/project_settings.h
index b89e6694b0..dba4aa6822 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;
@@ -114,6 +117,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);
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/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/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index b7471d7c82..b4d6fa4174 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();
@@ -369,22 +417,33 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
msg.push_back(error_str);
ERR_FAIL_COND(!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);
@@ -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/gdextension.cpp b/core/extension/gdextension.cpp
index 73526fae3e..1e4cd81034 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -363,6 +363,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 +375,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) {
@@ -482,6 +485,13 @@ void GDExtension::close_library() {
ERR_FAIL_COND(library == nullptr);
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;
}
@@ -542,6 +552,7 @@ void GDExtension::initialize_gdextensions() {
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 +614,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 +648,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) {
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index 77ec458d30..5a4dd3d5f5 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;
@@ -54,6 +57,7 @@ class GDExtension : public Resource {
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 +79,10 @@ 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; }
+#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..b4541de8fe 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);
@@ -1043,6 +1048,25 @@ static GDExtensionScriptInstancePtr gdextension_script_instance_create(const GDE
return reinterpret_cast<GDExtensionScriptInstancePtr>(script_instance_extension);
}
+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);
@@ -1167,6 +1191,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);
@@ -1210,6 +1235,7 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(ref_get_object);
REGISTER_INTERFACE_FUNC(ref_set_object);
REGISTER_INTERFACE_FUNC(script_instance_create);
+ 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..cfc21473d6 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -1526,6 +1526,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 */
/**
@@ -2107,6 +2126,19 @@ typedef void (*GDExtensionInterfaceRefSetObject)(GDExtensionRefPtr p_ref, GDExte
*/
typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate)(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data);
+/**
+ * @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 */
/**
@@ -2212,6 +2244,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/input/input.cpp b/core/input/input.cpp
index d481acf005..39f1acf623 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);
@@ -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 {
@@ -1499,6 +1502,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..c63a4e52e3 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -149,6 +149,7 @@ private:
HatMask last_hat = HatMask::CENTER;
int mapping = -1;
int hat_current = 0;
+ Dictionary info;
};
VelocityTrack mouse_velocity_track;
@@ -276,7 +277,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 +333,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/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.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.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..a5fea09113 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -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::_dds_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_dds_from_buffer", "buffer"), &Image::load_dds_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));
@@ -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_dds_from_buffer(const Vector<uint8_t> &p_array) {
+ ERR_FAIL_NULL_V_MSG(
+ _dds_mem_loader_func,
+ ERR_UNAVAILABLE,
+ "The DDS module isn't enabled. Recompile the Godot editor or export template binary with the `module_dds_enabled=yes` SCons option.");
+ return _load_from_buffer(p_array, _dds_mem_loader_func);
+}
+
void Image::convert_rg_to_ra_rgba8() {
ERR_FAIL_COND(format != FORMAT_RGBA8);
ERR_FAIL_COND(!data.size());
diff --git a/core/io/image.h b/core/io/image.h
index f877b00ee6..f68543ba24 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 _dds_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_dds_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/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_loader.cpp b/core/io/resource_loader.cpp
index 1fe662b1fa..df0253349c 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -275,10 +275,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) {
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..dd5f9d0575 100644
--- a/core/math/a_star_grid_2d.h
+++ b/core/math/a_star_grid_2d.h
@@ -177,6 +177,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/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..c8c50fb957 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.
@@ -163,7 +163,7 @@ ClassDB::APIType ClassDB::get_api_type(const StringName &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
diff --git a/core/object/class_db.h b/core/object/class_db.h
index ce64336a45..3aae3b452e 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);
@@ -246,7 +246,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;
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 4d19a2c75b..4ae0ecdefd 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -486,19 +486,21 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
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));
+
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;
}
}
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/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/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..ca8b460312 100644
--- a/core/string/translation.h
+++ b/core/string/translation.h
@@ -47,6 +47,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..80ca51573c 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -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;
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/variant/variant_call.cpp b/core/variant/variant_call.cpp
index dad9183216..ccf9b82022 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1659,6 +1659,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..fc1f7828a2 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;
diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index 545825011a..4f6bcb58b3 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,772 @@
#include "core/variant/binder_common.h"
#include "core/variant/variant_parser.h"
-struct VariantUtilityFunctions {
- // Math
-
- static inline double sin(double arg) {
- return Math::sin(arg);
- }
+// Math
+double VariantUtilityFunctions::sin(double arg) {
+ return Math::sin(arg);
+}
- static inline double cos(double arg) {
- return Math::cos(arg);
- }
+double VariantUtilityFunctions::cos(double arg) {
+ return Math::cos(arg);
+}
- static inline double tan(double arg) {
- return Math::tan(arg);
- }
+double VariantUtilityFunctions::tan(double arg) {
+ return Math::tan(arg);
+}
- static inline double sinh(double arg) {
- return Math::sinh(arg);
- }
+double VariantUtilityFunctions::sinh(double arg) {
+ return Math::sinh(arg);
+}
- static inline double cosh(double arg) {
- return Math::cosh(arg);
- }
+double VariantUtilityFunctions::cosh(double arg) {
+ return Math::cosh(arg);
+}
- static inline double tanh(double arg) {
- return Math::tanh(arg);
- }
+double VariantUtilityFunctions::tanh(double arg) {
+ return Math::tanh(arg);
+}
- static inline double asin(double arg) {
- return Math::asin(arg);
- }
+double VariantUtilityFunctions::asin(double arg) {
+ return Math::asin(arg);
+}
- static inline double acos(double arg) {
- return Math::acos(arg);
- }
+double VariantUtilityFunctions::acos(double arg) {
+ return Math::acos(arg);
+}
- static inline double atan(double arg) {
- return Math::atan(arg);
- }
+double VariantUtilityFunctions::atan(double arg) {
+ return Math::atan(arg);
+}
- static inline double atan2(double y, double x) {
- return Math::atan2(y, x);
- }
+double VariantUtilityFunctions::atan2(double y, double x) {
+ return Math::atan2(y, x);
+}
- static inline double sqrt(double x) {
- return Math::sqrt(x);
- }
+double VariantUtilityFunctions::sqrt(double x) {
+ return Math::sqrt(x);
+}
- static inline double fmod(double b, double r) {
- return Math::fmod(b, r);
- }
+double VariantUtilityFunctions::fmod(double b, double r) {
+ return Math::fmod(b, r);
+}
- static inline double fposmod(double b, double r) {
- return Math::fposmod(b, r);
- }
+double VariantUtilityFunctions::fposmod(double b, double r) {
+ return Math::fposmod(b, r);
+}
- static inline int64_t posmod(int64_t b, int64_t r) {
- return Math::posmod(b, r);
- }
+int64_t VariantUtilityFunctions::posmod(int64_t b, int64_t r) {
+ return Math::posmod(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();
- }
+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);
+}
+
+double VariantUtilityFunctions::db_to_linear(double db) {
+ return Math::db_to_linear(db);
+}
- static inline double 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);
+}
- static inline double pingpong(double value, double length) {
- return Math::pingpong(value, length);
+double VariantUtilityFunctions::wrapf(double value, double min, double max) {
+ return Math::wrapf(value, min, max);
+}
+
+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();
+}
+
+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 +817,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)...)
diff --git a/core/variant/variant_utility.h b/core/variant/variant_utility.h
new file mode 100644
index 0000000000..78f66987cb
--- /dev/null
+++ b/core/variant/variant_utility.h
@@ -0,0 +1,153 @@
+/**************************************************************************/
+/* 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 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