summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/SCsub2
-rw-r--r--core/config/engine.cpp65
-rw-r--r--core/config/engine.h41
-rw-r--r--core/config/project_settings.cpp43
-rw-r--r--core/config/project_settings.h5
-rw-r--r--core/core_bind.cpp1927
-rw-r--r--core/core_bind.h297
-rw-r--r--core/core_constants.cpp73
-rw-r--r--core/crypto/aes_context.h6
-rw-r--r--core/crypto/crypto.h11
-rw-r--r--core/crypto/crypto_core.h2
-rw-r--r--core/crypto/hashing_context.h6
-rw-r--r--core/debugger/debugger_marshalls.cpp10
-rw-r--r--core/debugger/debugger_marshalls.h16
-rw-r--r--core/debugger/engine_debugger.cpp2
-rw-r--r--core/debugger/engine_debugger.h12
-rw-r--r--core/debugger/local_debugger.cpp20
-rw-r--r--core/debugger/remote_debugger.cpp39
-rw-r--r--core/debugger/remote_debugger_peer.cpp2
-rw-r--r--core/debugger/remote_debugger_peer.h4
-rw-r--r--core/doc_data.cpp18
-rw-r--r--core/doc_data.h14
-rw-r--r--core/error/error_list.cpp85
-rw-r--r--core/error/error_list.h8
-rw-r--r--core/extension/SCsub7
-rw-r--r--core/extension/extension_api_dump.cpp869
-rw-r--r--core/extension/extension_api_dump.h45
-rw-r--r--core/extension/gdnative_interface.cpp1012
-rw-r--r--core/extension/gdnative_interface.h479
-rw-r--r--core/extension/native_extension.cpp440
-rw-r--r--core/extension/native_extension.h99
-rw-r--r--core/extension/native_extension_manager.cpp149
-rw-r--r--core/extension/native_extension_manager.h74
-rw-r--r--core/input/gamecontrollerdb.txt104
-rw-r--r--core/input/godotcontrollerdb.txt2
-rw-r--r--core/input/input.cpp195
-rw-r--r--core/input/input.h60
-rw-r--r--core/input/input_enums.h126
-rw-r--r--core/input/input_event.cpp216
-rw-r--r--core/input/input_event.h143
-rw-r--r--core/input/input_map.cpp128
-rw-r--r--core/input/input_map.h4
-rw-r--r--core/input/shortcut.cpp76
-rw-r--r--core/input/shortcut.h54
-rw-r--r--core/io/compression.cpp8
-rw-r--r--core/io/config_file.cpp12
-rw-r--r--core/io/config_file.h8
-rw-r--r--core/io/dir_access.cpp (renamed from core/os/dir_access.cpp)23
-rw-r--r--core/io/dir_access.h (renamed from core/os/dir_access.h)0
-rw-r--r--core/io/dtls_server.cpp2
-rw-r--r--core/io/dtls_server.h4
-rw-r--r--core/io/file_access.cpp (renamed from core/os/file_access.cpp)50
-rw-r--r--core/io/file_access.h (renamed from core/os/file_access.h)12
-rw-r--r--core/io/file_access_compressed.h2
-rw-r--r--core/io/file_access_encrypted.cpp1
-rw-r--r--core/io/file_access_encrypted.h2
-rw-r--r--core/io/file_access_memory.cpp3
-rw-r--r--core/io/file_access_memory.h2
-rw-r--r--core/io/file_access_network.cpp2
-rw-r--r--core/io/file_access_network.h2
-rw-r--r--core/io/file_access_pack.cpp6
-rw-r--r--core/io/file_access_pack.h6
-rw-r--r--core/io/file_access_zip.cpp2
-rw-r--r--core/io/http_client.cpp711
-rw-r--r--core/io/http_client.h89
-rw-r--r--core/io/http_client_tcp.cpp687
-rw-r--r--core/io/http_client_tcp.h93
-rw-r--r--core/io/image.cpp61
-rw-r--r--core/io/image.h13
-rw-r--r--core/io/image_loader.cpp6
-rw-r--r--core/io/image_loader.h2
-rw-r--r--core/io/ip.cpp83
-rw-r--r--core/io/json.cpp86
-rw-r--r--core/io/json.h40
-rw-r--r--core/io/logger.cpp9
-rw-r--r--core/io/logger.h2
-rw-r--r--core/io/marshalls.cpp572
-rw-r--r--core/io/marshalls.h40
-rw-r--r--core/io/multiplayer_api.cpp1254
-rw-r--r--core/io/net_socket.h4
-rw-r--r--core/io/packed_data_container.cpp18
-rw-r--r--core/io/packed_data_container.h4
-rw-r--r--core/io/packet_peer.cpp2
-rw-r--r--core/io/packet_peer.h4
-rw-r--r--core/io/packet_peer_dtls.cpp2
-rw-r--r--core/io/pck_packer.cpp2
-rw-r--r--core/io/pck_packer.h6
-rw-r--r--core/io/resource.cpp111
-rw-r--r--core/io/resource.h22
-rw-r--r--core/io/resource_format_binary.cpp281
-rw-r--r--core/io/resource_format_binary.h19
-rw-r--r--core/io/resource_importer.cpp42
-rw-r--r--core/io/resource_importer.h19
-rw-r--r--core/io/resource_loader.cpp228
-rw-r--r--core/io/resource_loader.h26
-rw-r--r--core/io/resource_saver.cpp58
-rw-r--r--core/io/resource_saver.h15
-rw-r--r--core/io/resource_uid.cpp262
-rw-r--r--core/io/resource_uid.h89
-rw-r--r--core/io/stream_peer.cpp6
-rw-r--r--core/io/stream_peer.h8
-rw-r--r--core/io/tcp_server.cpp1
-rw-r--r--core/io/tcp_server.h4
-rw-r--r--core/io/translation_loader_po.cpp2
-rw-r--r--core/io/translation_loader_po.h2
-rw-r--r--core/io/udp_server.cpp1
-rw-r--r--core/io/udp_server.h4
-rw-r--r--core/io/xml_parser.h9
-rw-r--r--core/io/zip_io.h2
-rw-r--r--core/math/a_star.cpp38
-rw-r--r--core/math/a_star.h18
-rw-r--r--core/math/aabb.cpp2
-rw-r--r--core/math/basis.cpp83
-rw-r--r--core/math/basis.h32
-rw-r--r--core/math/camera_matrix.cpp18
-rw-r--r--core/math/camera_matrix.h10
-rw-r--r--core/math/color.cpp16
-rw-r--r--core/math/color.h1
-rw-r--r--core/math/convex_hull.cpp28
-rw-r--r--core/math/convex_hull.h2
-rw-r--r--core/math/delaunay_3d.h5
-rw-r--r--core/math/dynamic_bvh.cpp2
-rw-r--r--core/math/expression.cpp6
-rw-r--r--core/math/expression.h6
-rw-r--r--core/math/face3.cpp6
-rw-r--r--core/math/face3.h10
-rw-r--r--core/math/geometry_2d.h36
-rw-r--r--core/math/geometry_3d.h2
-rw-r--r--core/math/math_defs.h22
-rw-r--r--core/math/math_fieldwise.cpp8
-rw-r--r--core/math/math_funcs.cpp10
-rw-r--r--core/math/math_funcs.h1
-rw-r--r--core/math/plane.cpp2
-rw-r--r--core/math/quaternion.cpp (renamed from core/math/quat.cpp)75
-rw-r--r--core/math/quaternion.h (renamed from core/math/quat.h)113
-rw-r--r--core/math/quick_hull.cpp36
-rw-r--r--core/math/random_number_generator.h6
-rw-r--r--core/math/rect2.cpp8
-rw-r--r--core/math/rect2.h4
-rw-r--r--core/math/transform_2d.cpp23
-rw-r--r--core/math/transform_2d.h4
-rw-r--r--core/math/transform_3d.cpp (renamed from core/math/transform.cpp)132
-rw-r--r--core/math/transform_3d.h (renamed from core/math/transform.h)143
-rw-r--r--core/math/triangle_mesh.cpp14
-rw-r--r--core/math/triangle_mesh.h12
-rw-r--r--core/math/triangulate.h2
-rw-r--r--core/math/vector2.cpp32
-rw-r--r--core/math/vector2.h48
-rw-r--r--core/math/vector3.cpp34
-rw-r--r--core/math/vector3.h62
-rw-r--r--core/math/vector3i.cpp13
-rw-r--r--core/math/vector3i.h51
-rw-r--r--core/multiplayer/SCsub5
-rw-r--r--core/multiplayer/multiplayer.h80
-rw-r--r--core/multiplayer/multiplayer_api.cpp668
-rw-r--r--core/multiplayer/multiplayer_api.h (renamed from core/io/multiplayer_api.h)130
-rw-r--r--core/multiplayer/multiplayer_peer.cpp (renamed from core/io/networked_multiplayer_peer.cpp)55
-rw-r--r--core/multiplayer/multiplayer_peer.h (renamed from core/io/networked_multiplayer_peer.h)24
-rw-r--r--core/multiplayer/multiplayer_replicator.cpp791
-rw-r--r--core/multiplayer/multiplayer_replicator.h138
-rw-r--r--core/multiplayer/rpc_manager.cpp525
-rw-r--r--core/multiplayer/rpc_manager.h89
-rw-r--r--core/object/SCsub5
-rw-r--r--core/object/class_db.cpp267
-rw-r--r--core/object/class_db.h121
-rw-r--r--core/object/make_virtuals.py161
-rw-r--r--core/object/method_bind.cpp29
-rw-r--r--core/object/method_bind.h3
-rw-r--r--core/object/object.cpp236
-rw-r--r--core/object/object.h197
-rw-r--r--core/object/object_id.h2
-rw-r--r--core/object/ref_counted.cpp (renamed from core/object/reference.cpp)45
-rw-r--r--core/object/ref_counted.h (renamed from core/object/reference.h)42
-rw-r--r--core/object/script_language.cpp70
-rw-r--r--core/object/script_language.h64
-rw-r--r--core/object/undo_redo.cpp70
-rw-r--r--core/object/undo_redo.h8
-rw-r--r--core/os/keyboard.h47
-rw-r--r--core/os/main_loop.cpp33
-rw-r--r--core/os/main_loop.h12
-rw-r--r--core/os/memory.h16
-rw-r--r--core/os/midi_driver.cpp10
-rw-r--r--core/os/os.cpp67
-rw-r--r--core/os/os.h46
-rw-r--r--core/os/pool_allocator.h2
-rw-r--r--core/os/time.cpp433
-rw-r--r--core/os/time.h109
-rw-r--r--core/register_core_types.cpp289
-rw-r--r--core/register_core_types.h1
-rw-r--r--core/string/node_path.cpp17
-rw-r--r--core/string/optimized_translation.cpp6
-rw-r--r--core/string/string_name.cpp108
-rw-r--r--core/string/string_name.h36
-rw-r--r--core/string/translation.cpp270
-rw-r--r--core/string/translation.h27
-rw-r--r--core/string/translation_po.cpp27
-rw-r--r--core/string/ustring.cpp334
-rw-r--r--core/string/ustring.h16
-rw-r--r--core/templates/bin_sorted_array.h181
-rw-r--r--core/templates/command_queue_mt.cpp29
-rw-r--r--core/templates/command_queue_mt.h163
-rw-r--r--core/templates/cowdata.h18
-rw-r--r--core/templates/hash_map.h6
-rw-r--r--core/templates/hashfuncs.h4
-rw-r--r--core/templates/list.h77
-rw-r--r--core/templates/local_vector.h4
-rw-r--r--core/templates/map.h135
-rw-r--r--core/templates/oa_hash_map.h4
-rw-r--r--core/templates/paged_allocator.h5
-rw-r--r--core/templates/pair.h40
-rw-r--r--core/templates/rid_owner.h57
-rw-r--r--core/templates/safe_list.h375
-rw-r--r--core/templates/set.h84
-rw-r--r--core/templates/thread_work_pool.h2
-rw-r--r--core/templates/vector.h66
-rw-r--r--core/typedefs.h7
-rw-r--r--core/variant/array.cpp37
-rw-r--r--core/variant/array.h3
-rw-r--r--core/variant/binder_common.h19
-rw-r--r--core/variant/callable.cpp10
-rw-r--r--core/variant/callable.h1
-rw-r--r--core/variant/callable_bind.cpp3
-rw-r--r--core/variant/dictionary.cpp59
-rw-r--r--core/variant/method_ptrcall.h25
-rw-r--r--core/variant/native_ptr.h130
-rw-r--r--core/variant/type_info.h33
-rw-r--r--core/variant/typed_array.h18
-rw-r--r--core/variant/variant.cpp280
-rw-r--r--core/variant/variant.h46
-rw-r--r--core/variant/variant_call.cpp165
-rw-r--r--core/variant/variant_construct.cpp568
-rw-r--r--core/variant/variant_construct.h572
-rw-r--r--core/variant/variant_destruct.cpp78
-rw-r--r--core/variant/variant_destruct.h76
-rw-r--r--core/variant/variant_internal.h110
-rw-r--r--core/variant/variant_op.cpp1421
-rw-r--r--core/variant/variant_op.h1416
-rw-r--r--core/variant/variant_parser.cpp154
-rw-r--r--core/variant/variant_parser.h8
-rw-r--r--core/variant/variant_setget.cpp335
-rw-r--r--core/variant/variant_setget.h332
-rw-r--r--core/variant/variant_utility.cpp62
242 files changed, 17504 insertions, 8759 deletions
diff --git a/core/SCsub b/core/SCsub
index e3ba46be02..14dfa3487f 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -183,9 +183,11 @@ SConscript("os/SCsub")
SConscript("math/SCsub")
SConscript("crypto/SCsub")
SConscript("io/SCsub")
+SConscript("multiplayer/SCsub")
SConscript("debugger/SCsub")
SConscript("input/SCsub")
SConscript("variant/SCsub")
+SConscript("extension/SCsub")
SConscript("object/SCsub")
SConscript("templates/SCsub")
SConscript("string/SCsub")
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index c43e32868c..d8fbb50a75 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -37,23 +37,23 @@
#include "core/version.h"
#include "core/version_hash.gen.h"
-void Engine::set_iterations_per_second(int p_ips) {
+void Engine::set_physics_ticks_per_second(int p_ips) {
ERR_FAIL_COND_MSG(p_ips <= 0, "Engine iterations per second must be greater than 0.");
ips = p_ips;
}
-int Engine::get_iterations_per_second() const {
+int Engine::get_physics_ticks_per_second() const {
return ips;
}
-void Engine::set_physics_jitter_fix(float p_threshold) {
+void Engine::set_physics_jitter_fix(double p_threshold) {
if (p_threshold < 0) {
p_threshold = 0;
}
physics_jitter_fix = p_threshold;
}
-float Engine::get_physics_jitter_fix() const {
+double Engine::get_physics_jitter_fix() const {
return physics_jitter_fix;
}
@@ -77,11 +77,11 @@ uint32_t Engine::get_frame_delay() const {
return _frame_delay;
}
-void Engine::set_time_scale(float p_scale) {
+void Engine::set_time_scale(double p_scale) {
_time_scale = p_scale;
}
-float Engine::get_time_scale() const {
+double Engine::get_time_scale() const {
return _time_scale;
}
@@ -190,24 +190,56 @@ bool Engine::is_validation_layers_enabled() const {
return use_validation_layers;
}
+void Engine::set_print_error_messages(bool p_enabled) {
+ _print_error_enabled = p_enabled;
+}
+
+bool Engine::is_printing_error_messages() const {
+ return _print_error_enabled;
+}
+
void Engine::add_singleton(const Singleton &p_singleton) {
+ ERR_FAIL_COND_MSG(singleton_ptrs.has(p_singleton.name), "Can't register singleton that already exists: " + String(p_singleton.name));
singletons.push_back(p_singleton);
singleton_ptrs[p_singleton.name] = p_singleton.ptr;
}
-Object *Engine::get_singleton_object(const String &p_name) const {
+Object *Engine::get_singleton_object(const StringName &p_name) const {
const Map<StringName, Object *>::Element *E = singleton_ptrs.find(p_name);
- ERR_FAIL_COND_V_MSG(!E, nullptr, "Failed to retrieve non-existent singleton '" + p_name + "'.");
+ ERR_FAIL_COND_V_MSG(!E, nullptr, "Failed to retrieve non-existent singleton '" + String(p_name) + "'.");
return E->get();
}
-bool Engine::has_singleton(const String &p_name) const {
+bool Engine::is_singleton_user_created(const StringName &p_name) const {
+ ERR_FAIL_COND_V(!singleton_ptrs.has(p_name), false);
+
+ for (const Singleton &E : singletons) {
+ if (E.name == p_name && E.user_created) {
+ return true;
+ }
+ }
+
+ return false;
+}
+void Engine::remove_singleton(const StringName &p_name) {
+ ERR_FAIL_COND(!singleton_ptrs.has(p_name));
+
+ for (List<Singleton>::Element *E = singletons.front(); E; E = E->next()) {
+ if (E->get().name == p_name) {
+ singletons.erase(E);
+ singleton_ptrs.erase(p_name);
+ return;
+ }
+ }
+}
+
+bool Engine::has_singleton(const StringName &p_name) const {
return singleton_ptrs.has(p_name);
}
void Engine::get_singletons(List<Singleton> *p_singletons) {
- for (List<Singleton>::Element *E = singletons.front(); E; E = E->next()) {
- p_singletons->push_back(E->get());
+ for (const Singleton &E : singletons) {
+ p_singletons->push_back(E);
}
}
@@ -228,13 +260,14 @@ Engine::Engine() {
singleton = this;
}
-Engine::Singleton::Singleton(const StringName &p_name, Object *p_ptr) :
+Engine::Singleton::Singleton(const StringName &p_name, Object *p_ptr, const StringName &p_class_name) :
name(p_name),
- ptr(p_ptr) {
+ ptr(p_ptr),
+ class_name(p_class_name) {
#ifdef DEBUG_ENABLED
- Reference *ref = Object::cast_to<Reference>(p_ptr);
- if (ref && !ref->is_referenced()) {
- WARN_PRINT("You must use Ref<> to ensure the lifetime of a Reference object intended to be used as a singleton.");
+ RefCounted *rc = Object::cast_to<RefCounted>(p_ptr);
+ if (rc && !rc->is_referenced()) {
+ WARN_PRINT("You must use Ref<> to ensure the lifetime of a RefCounted object intended to be used as a singleton.");
}
#endif
}
diff --git a/core/config/engine.h b/core/config/engine.h
index 276da1c7ea..ae33acede2 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -41,7 +41,9 @@ public:
struct Singleton {
StringName name;
Object *ptr;
- Singleton(const StringName &p_name = StringName(), Object *p_ptr = nullptr);
+ StringName class_name; //used for binding generation hinting
+ bool user_created = false;
+ Singleton(const StringName &p_name = StringName(), Object *p_ptr = nullptr, const StringName &p_class_name = StringName());
};
private:
@@ -50,15 +52,15 @@ private:
uint64_t frames_drawn = 0;
uint32_t _frame_delay = 0;
uint64_t _frame_ticks = 0;
- float _process_step = 0;
+ double _process_step = 0;
int ips = 60;
- float physics_jitter_fix = 0.5;
- float _fps = 1;
+ double physics_jitter_fix = 0.5;
+ double _fps = 1;
int _target_fps = 0;
- float _time_scale = 1.0;
+ double _time_scale = 1.0;
uint64_t _physics_frames = 0;
- float _physics_interpolation_fraction = 0.0f;
+ double _physics_interpolation_fraction = 0.0f;
bool abort_on_gpu_errors = false;
bool use_validation_layers = false;
@@ -77,16 +79,16 @@ private:
public:
static Engine *get_singleton();
- virtual void set_iterations_per_second(int p_ips);
- virtual int get_iterations_per_second() const;
+ virtual void set_physics_ticks_per_second(int p_ips);
+ virtual int get_physics_ticks_per_second() const;
- void set_physics_jitter_fix(float p_threshold);
- float get_physics_jitter_fix() const;
+ void set_physics_jitter_fix(double p_threshold);
+ double get_physics_jitter_fix() const;
virtual void set_target_fps(int p_fps);
virtual int get_target_fps() const;
- virtual float get_frames_per_second() const { return _fps; }
+ virtual double get_frames_per_second() const { return _fps; }
uint64_t get_frames_drawn();
@@ -94,19 +96,24 @@ public:
uint64_t get_process_frames() const { return _process_frames; }
bool is_in_physics_frame() const { return _in_physics; }
uint64_t get_frame_ticks() const { return _frame_ticks; }
- float get_process_step() const { return _process_step; }
- float get_physics_interpolation_fraction() const { return _physics_interpolation_fraction; }
+ double get_process_step() const { return _process_step; }
+ double get_physics_interpolation_fraction() const { return _physics_interpolation_fraction; }
- void set_time_scale(float p_scale);
- float get_time_scale() const;
+ void set_time_scale(double p_scale);
+ double get_time_scale() const;
+
+ void set_print_error_messages(bool p_enabled);
+ bool is_printing_error_messages() const;
void set_frame_delay(uint32_t p_msec);
uint32_t get_frame_delay() const;
void add_singleton(const Singleton &p_singleton);
void get_singletons(List<Singleton> *p_singletons);
- bool has_singleton(const String &p_name) const;
- Object *get_singleton_object(const String &p_name) const;
+ bool has_singleton(const StringName &p_name) const;
+ Object *get_singleton_object(const StringName &p_name) const;
+ void remove_singleton(const StringName &p_name);
+ bool is_singleton_user_created(const StringName &p_name) const;
#ifdef TOOLS_ENABLED
_FORCE_INLINE_ void set_editor_hint(bool p_enabled) { editor_hint = p_enabled; }
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 53d13f7429..03892d1d4f 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -30,20 +30,18 @@
#include "project_settings.h"
-#include "core/core_bind.h"
+#include "core/core_bind.h" // For Compression enum.
#include "core/core_string_names.h"
#include "core/input/input_map.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/io/file_access_network.h"
#include "core/io/file_access_pack.h"
#include "core/io/marshalls.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "core/variant/variant_parser.h"
-#include <zlib.h>
-
ProjectSettings *ProjectSettings::singleton = nullptr;
ProjectSettings *ProjectSettings::get_singleton() {
@@ -62,7 +60,7 @@ String ProjectSettings::localize_path(const String &p_path) const {
}
if (p_path.begins_with("res://") || p_path.begins_with("user://") ||
- (p_path.is_abs_path() && !p_path.begins_with(resource_path))) {
+ (p_path.is_absolute_path() && !p_path.begins_with(resource_path))) {
return p_path.simplify_path();
}
@@ -248,7 +246,7 @@ struct _VCSort {
String name;
Variant::Type type;
int order;
- int flags;
+ uint32_t flags;
bool operator<(const _VCSort &p_vcs) const { return order == p_vcs.order ? name < p_vcs.name : order < p_vcs.order; }
};
@@ -702,17 +700,14 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str
int count = 0;
for (Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) {
- for (List<String>::Element *F = E->get().front(); F; F = F->next()) {
- count++;
- }
+ count += E->get().size();
}
if (p_custom_features != String()) {
file->store_32(count + 1);
//store how many properties are saved, add one for custom featuers, which must always go first
String key = CoreStringNames::get_singleton()->_custom_features;
- file->store_32(key.length());
- file->store_string(key);
+ file->store_pascal_string(key);
int len;
err = encode_variant(p_custom_features, nullptr, len, false);
@@ -737,8 +732,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str
}
for (Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) {
- for (List<String>::Element *F = E->get().front(); F; F = F->next()) {
- String key = F->get();
+ for (String &key : E->get()) {
if (E->key() != "") {
key = E->key() + "/" + key;
}
@@ -749,8 +743,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str
value = get(key);
}
- file->store_32(key.length());
- file->store_string(key);
+ file->store_pascal_string(key);
int len;
err = encode_variant(value, nullptr, len, true);
@@ -807,8 +800,8 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const Map<Strin
if (E->key() != "") {
file->store_string("[" + E->key() + "]\n\n");
}
- for (List<String>::Element *F = E->get().front(); F; F = F->next()) {
- String key = F->get();
+ for (const String &F : E->get()) {
+ String key = F;
if (E->key() != "") {
key = E->key() + "/" + key;
}
@@ -821,7 +814,7 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const Map<Strin
String vstr;
VariantWriter::write_to_string(value, vstr);
- file->store_string(F->get().property_name_encode() + "=" + vstr + "\n");
+ file->store_string(F.property_name_encode() + "=" + vstr + "\n");
}
}
@@ -935,11 +928,11 @@ Vector<String> ProjectSettings::get_optimizer_presets() const {
ProjectSettings::get_singleton()->get_property_list(&pi);
Vector<String> names;
- for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) {
- if (!E->get().name.begins_with("optimizer_presets/")) {
+ for (const PropertyInfo &E : pi) {
+ if (!E.name.begins_with("optimizer_presets/")) {
continue;
}
- names.push_back(E->get().name.get_slicec('/', 1));
+ names.push_back(E.name.get_slicec('/', 1));
}
names.sort();
@@ -1013,7 +1006,7 @@ bool ProjectSettings::has_custom_feature(const String &p_feature) const {
return custom_features.has(p_feature);
}
-Map<StringName, ProjectSettings::AutoloadInfo> ProjectSettings::get_autoload_list() const {
+OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> ProjectSettings::get_autoload_list() const {
return autoloads;
}
@@ -1102,7 +1095,7 @@ ProjectSettings::ProjectSettings() {
if (Engine::get_singleton()->has_singleton("GodotSharp")) {
extensions.push_back("cs");
}
- extensions.push_back("shader");
+ extensions.push_back("gdshader");
GLOBAL_DEF("editor/run/main_run_args", "");
@@ -1116,6 +1109,8 @@ ProjectSettings::ProjectSettings() {
// Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum.
custom_prop_info["display/window/handheld/orientation"] = PropertyInfo(Variant::INT, "display/window/handheld/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait,Reverse Landscape,Reverse Portrait,Sensor Landscape,Sensor Portrait,Sensor");
+ // Keep the enum values in sync with the `DisplayServer::VSyncMode` enum.
+ custom_prop_info["display/window/vsync/vsync_mode"] = PropertyInfo(Variant::INT, "display/window/vsync/vsync_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Adaptive,Mailbox");
custom_prop_info["rendering/driver/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/driver/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded");
GLOBAL_DEF("physics/2d/run_on_thread", false);
GLOBAL_DEF("physics/3d/run_on_thread", false);
diff --git a/core/config/project_settings.h b/core/config/project_settings.h
index ed8fb19fa0..7e93f26f0d 100644
--- a/core/config/project_settings.h
+++ b/core/config/project_settings.h
@@ -33,6 +33,7 @@
#include "core/object/class_db.h"
#include "core/os/thread_safe.h"
+#include "core/templates/ordered_hash_map.h"
#include "core/templates/set.h"
class ProjectSettings : public Object {
@@ -91,7 +92,7 @@ protected:
Set<String> custom_features;
Map<StringName, StringName> feature_overrides;
- Map<StringName, AutoloadInfo> autoloads;
+ OrderedHashMap<StringName, AutoloadInfo> autoloads;
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -168,7 +169,7 @@ public:
bool has_custom_feature(const String &p_feature) const;
- Map<StringName, AutoloadInfo> get_autoload_list() const;
+ OrderedHashMap<StringName, AutoloadInfo> get_autoload_list() const;
void add_autoload(const AutoloadInfo &p_autoload);
void remove_autoload(const StringName &p_autoload);
bool has_autoload(const StringName &p_autoload) const;
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 05265c41ad..58aecf39aa 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -35,112 +35,96 @@
#include "core/debugger/engine_debugger.h"
#include "core/io/file_access_compressed.h"
#include "core/io/file_access_encrypted.h"
-#include "core/io/json.h"
#include "core/io/marshalls.h"
#include "core/math/geometry_2d.h"
#include "core/math/geometry_3d.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
-/**
- * Time constants borrowed from loc_time.h
- */
-#define EPOCH_YR 1970 /* EPOCH = Jan 1 1970 00:00:00 */
-#define SECS_DAY (24L * 60L * 60L)
-#define LEAPYEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400)))
-#define YEARSIZE(year) (LEAPYEAR(year) ? 366 : 365)
-#define SECOND_KEY "second"
-#define MINUTE_KEY "minute"
-#define HOUR_KEY "hour"
-#define DAY_KEY "day"
-#define MONTH_KEY "month"
-#define YEAR_KEY "year"
-#define WEEKDAY_KEY "weekday"
-#define DST_KEY "dst"
-
-/// Table of number of days in each month (for regular year and leap year)
-static const unsigned int MONTH_DAYS_TABLE[2][12] = {
- { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
- { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
-};
+namespace core_bind {
-////// _ResourceLoader //////
+////// ResourceLoader //////
-_ResourceLoader *_ResourceLoader::singleton = nullptr;
+ResourceLoader *ResourceLoader::singleton = nullptr;
-Error _ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads) {
- return ResourceLoader::load_threaded_request(p_path, p_type_hint, p_use_sub_threads);
+Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads) {
+ return ::ResourceLoader::load_threaded_request(p_path, p_type_hint, p_use_sub_threads);
}
-_ResourceLoader::ThreadLoadStatus _ResourceLoader::load_threaded_get_status(const String &p_path, Array r_progress) {
+ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const String &p_path, Array r_progress) {
float progress = 0;
- ResourceLoader::ThreadLoadStatus tls = ResourceLoader::load_threaded_get_status(p_path, &progress);
+ ::ResourceLoader::ThreadLoadStatus tls = ::ResourceLoader::load_threaded_get_status(p_path, &progress);
r_progress.resize(1);
r_progress[0] = progress;
return (ThreadLoadStatus)tls;
}
-RES _ResourceLoader::load_threaded_get(const String &p_path) {
+RES ResourceLoader::load_threaded_get(const String &p_path) {
Error error;
- RES res = ResourceLoader::load_threaded_get(p_path, &error);
+ RES res = ::ResourceLoader::load_threaded_get(p_path, &error);
return res;
}
-RES _ResourceLoader::load(const String &p_path, const String &p_type_hint, CacheMode p_cache_mode) {
+RES ResourceLoader::load(const String &p_path, const String &p_type_hint, CacheMode p_cache_mode) {
Error err = OK;
- RES ret = ResourceLoader::load(p_path, p_type_hint, ResourceFormatLoader::CacheMode(p_cache_mode), &err);
+ RES ret = ::ResourceLoader::load(p_path, p_type_hint, ResourceFormatLoader::CacheMode(p_cache_mode), &err);
ERR_FAIL_COND_V_MSG(err != OK, ret, "Error loading resource: '" + p_path + "'.");
return ret;
}
-Vector<String> _ResourceLoader::get_recognized_extensions_for_type(const String &p_type) {
+Vector<String> ResourceLoader::get_recognized_extensions_for_type(const String &p_type) {
List<String> exts;
- ResourceLoader::get_recognized_extensions_for_type(p_type, &exts);
+ ::ResourceLoader::get_recognized_extensions_for_type(p_type, &exts);
Vector<String> ret;
- for (List<String>::Element *E = exts.front(); E; E = E->next()) {
- ret.push_back(E->get());
+ for (const String &E : exts) {
+ ret.push_back(E);
}
return ret;
}
-void _ResourceLoader::set_abort_on_missing_resources(bool p_abort) {
- ResourceLoader::set_abort_on_missing_resources(p_abort);
+void ResourceLoader::set_abort_on_missing_resources(bool p_abort) {
+ ::ResourceLoader::set_abort_on_missing_resources(p_abort);
}
-PackedStringArray _ResourceLoader::get_dependencies(const String &p_path) {
+PackedStringArray ResourceLoader::get_dependencies(const String &p_path) {
List<String> deps;
- ResourceLoader::get_dependencies(p_path, &deps);
+ ::ResourceLoader::get_dependencies(p_path, &deps);
PackedStringArray ret;
- for (List<String>::Element *E = deps.front(); E; E = E->next()) {
- ret.push_back(E->get());
+ for (const String &E : deps) {
+ ret.push_back(E);
}
return ret;
}
-bool _ResourceLoader::has_cached(const String &p_path) {
+bool ResourceLoader::has_cached(const String &p_path) {
String local_path = ProjectSettings::get_singleton()->localize_path(p_path);
return ResourceCache::has(local_path);
}
-bool _ResourceLoader::exists(const String &p_path, const String &p_type_hint) {
- return ResourceLoader::exists(p_path, p_type_hint);
+bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) {
+ return ::ResourceLoader::exists(p_path, p_type_hint);
+}
+
+ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) {
+ return ::ResourceLoader::get_resource_uid(p_path);
}
-void _ResourceLoader::_bind_methods() {
- ClassDB::bind_method(D_METHOD("load_threaded_request", "path", "type_hint", "use_sub_threads"), &_ResourceLoader::load_threaded_request, DEFVAL(""), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &_ResourceLoader::load_threaded_get_status, DEFVAL(Array()));
- ClassDB::bind_method(D_METHOD("load_threaded_get", "path"), &_ResourceLoader::load_threaded_get);
+void ResourceLoader::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("load_threaded_request", "path", "type_hint", "use_sub_threads"), &ResourceLoader::load_threaded_request, DEFVAL(""), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &ResourceLoader::load_threaded_get_status, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("load_threaded_get", "path"), &ResourceLoader::load_threaded_get);
- ClassDB::bind_method(D_METHOD("load", "path", "type_hint", "cache_mode"), &_ResourceLoader::load, DEFVAL(""), DEFVAL(CACHE_MODE_REUSE));
- ClassDB::bind_method(D_METHOD("get_recognized_extensions_for_type", "type"), &_ResourceLoader::get_recognized_extensions_for_type);
- ClassDB::bind_method(D_METHOD("set_abort_on_missing_resources", "abort"), &_ResourceLoader::set_abort_on_missing_resources);
- ClassDB::bind_method(D_METHOD("get_dependencies", "path"), &_ResourceLoader::get_dependencies);
- ClassDB::bind_method(D_METHOD("has_cached", "path"), &_ResourceLoader::has_cached);
- ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &_ResourceLoader::exists, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("load", "path", "type_hint", "cache_mode"), &ResourceLoader::load, DEFVAL(""), DEFVAL(CACHE_MODE_REUSE));
+ ClassDB::bind_method(D_METHOD("get_recognized_extensions_for_type", "type"), &ResourceLoader::get_recognized_extensions_for_type);
+ ClassDB::bind_method(D_METHOD("set_abort_on_missing_resources", "abort"), &ResourceLoader::set_abort_on_missing_resources);
+ ClassDB::bind_method(D_METHOD("get_dependencies", "path"), &ResourceLoader::get_dependencies);
+ ClassDB::bind_method(D_METHOD("has_cached", "path"), &ResourceLoader::has_cached);
+ ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &ResourceLoader::exists, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("get_resource_uid", "path"), &ResourceLoader::get_resource_uid);
BIND_ENUM_CONSTANT(THREAD_LOAD_INVALID_RESOURCE);
BIND_ENUM_CONSTANT(THREAD_LOAD_IN_PROGRESS);
@@ -152,29 +136,29 @@ void _ResourceLoader::_bind_methods() {
BIND_ENUM_CONSTANT(CACHE_MODE_REPLACE);
}
-////// _ResourceSaver //////
+////// ResourceSaver //////
-Error _ResourceSaver::save(const String &p_path, const RES &p_resource, SaverFlags p_flags) {
+Error ResourceSaver::save(const String &p_path, const RES &p_resource, SaverFlags p_flags) {
ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, "Can't save empty resource to path '" + String(p_path) + "'.");
- return ResourceSaver::save(p_path, p_resource, p_flags);
+ return ::ResourceSaver::save(p_path, p_resource, p_flags);
}
-Vector<String> _ResourceSaver::get_recognized_extensions(const RES &p_resource) {
+Vector<String> ResourceSaver::get_recognized_extensions(const RES &p_resource) {
ERR_FAIL_COND_V_MSG(p_resource.is_null(), Vector<String>(), "It's not a reference to a valid Resource object.");
List<String> exts;
- ResourceSaver::get_recognized_extensions(p_resource, &exts);
+ ::ResourceSaver::get_recognized_extensions(p_resource, &exts);
Vector<String> ret;
- for (List<String>::Element *E = exts.front(); E; E = E->next()) {
- ret.push_back(E->get());
+ for (const String &E : exts) {
+ ret.push_back(E);
}
return ret;
}
-_ResourceSaver *_ResourceSaver::singleton = nullptr;
+ResourceSaver *ResourceSaver::singleton = nullptr;
-void _ResourceSaver::_bind_methods() {
- ClassDB::bind_method(D_METHOD("save", "path", "resource", "flags"), &_ResourceSaver::save, DEFVAL(0));
- ClassDB::bind_method(D_METHOD("get_recognized_extensions", "type"), &_ResourceSaver::get_recognized_extensions);
+void ResourceSaver::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("save", "path", "resource", "flags"), &ResourceSaver::save, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("get_recognized_extensions", "type"), &ResourceSaver::get_recognized_extensions);
BIND_ENUM_CONSTANT(FLAG_RELATIVE_PATHS);
BIND_ENUM_CONSTANT(FLAG_BUNDLE_RESOURCES);
@@ -185,61 +169,65 @@ void _ResourceSaver::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_REPLACE_SUBRESOURCE_PATHS);
}
-////// _OS //////
+////// OS //////
-PackedStringArray _OS::get_connected_midi_inputs() {
- return OS::get_singleton()->get_connected_midi_inputs();
+PackedStringArray OS::get_connected_midi_inputs() {
+ return ::OS::get_singleton()->get_connected_midi_inputs();
}
-void _OS::open_midi_inputs() {
- OS::get_singleton()->open_midi_inputs();
+void OS::open_midi_inputs() {
+ ::OS::get_singleton()->open_midi_inputs();
}
-void _OS::close_midi_inputs() {
- OS::get_singleton()->close_midi_inputs();
+void OS::close_midi_inputs() {
+ ::OS::get_singleton()->close_midi_inputs();
}
-void _OS::set_use_file_access_save_and_swap(bool p_enable) {
+void OS::set_use_file_access_save_and_swap(bool p_enable) {
FileAccess::set_backup_save(p_enable);
}
-void _OS::set_low_processor_usage_mode(bool p_enabled) {
- OS::get_singleton()->set_low_processor_usage_mode(p_enabled);
+void OS::set_low_processor_usage_mode(bool p_enabled) {
+ ::OS::get_singleton()->set_low_processor_usage_mode(p_enabled);
}
-bool _OS::is_in_low_processor_usage_mode() const {
- return OS::get_singleton()->is_in_low_processor_usage_mode();
+bool OS::is_in_low_processor_usage_mode() const {
+ return ::OS::get_singleton()->is_in_low_processor_usage_mode();
}
-void _OS::set_low_processor_usage_mode_sleep_usec(int p_usec) {
- OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(p_usec);
+void OS::set_low_processor_usage_mode_sleep_usec(int p_usec) {
+ ::OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(p_usec);
}
-int _OS::get_low_processor_usage_mode_sleep_usec() const {
- return OS::get_singleton()->get_low_processor_usage_mode_sleep_usec();
+int OS::get_low_processor_usage_mode_sleep_usec() const {
+ return ::OS::get_singleton()->get_low_processor_usage_mode_sleep_usec();
}
-String _OS::get_executable_path() const {
- return OS::get_singleton()->get_executable_path();
+void OS::alert(const String &p_alert, const String &p_title) {
+ ::OS::get_singleton()->alert(p_alert, p_title);
}
-Error _OS::shell_open(String p_uri) {
+String OS::get_executable_path() const {
+ return ::OS::get_singleton()->get_executable_path();
+}
+
+Error OS::shell_open(String p_uri) {
if (p_uri.begins_with("res://")) {
WARN_PRINT("Attempting to open an URL with the \"res://\" protocol. Use `ProjectSettings.globalize_path()` to convert a Godot-specific path to a system path before opening it with `OS.shell_open()`.");
} else if (p_uri.begins_with("user://")) {
WARN_PRINT("Attempting to open an URL with the \"user://\" protocol. Use `ProjectSettings.globalize_path()` to convert a Godot-specific path to a system path before opening it with `OS.shell_open()`.");
}
- return OS::get_singleton()->shell_open(p_uri);
+ return ::OS::get_singleton()->shell_open(p_uri);
}
-int _OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r_output, bool p_read_stderr) {
+int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r_output, bool p_read_stderr) {
List<String> args;
for (int i = 0; i < p_arguments.size(); i++) {
args.push_back(p_arguments[i]);
}
String pipe;
int exitcode = 0;
- Error err = OS::get_singleton()->execute(p_path, args, &pipe, &exitcode, p_read_stderr);
+ Error err = ::OS::get_singleton()->execute(p_path, args, &pipe, &exitcode, p_read_stderr);
r_output.push_back(pipe);
if (err != OK) {
return -1;
@@ -247,346 +235,147 @@ int _OS::execute(const String &p_path, const Vector<String> &p_arguments, Array
return exitcode;
}
-int _OS::create_process(const String &p_path, const Vector<String> &p_arguments) {
+int OS::create_process(const String &p_path, const Vector<String> &p_arguments) {
List<String> args;
for (int i = 0; i < p_arguments.size(); i++) {
args.push_back(p_arguments[i]);
}
- OS::ProcessID pid = 0;
- Error err = OS::get_singleton()->create_process(p_path, args, &pid);
+ ::OS::ProcessID pid = 0;
+ Error err = ::OS::get_singleton()->create_process(p_path, args, &pid);
if (err != OK) {
return -1;
}
return pid;
}
-Error _OS::kill(int p_pid) {
- return OS::get_singleton()->kill(p_pid);
+Error OS::kill(int p_pid) {
+ return ::OS::get_singleton()->kill(p_pid);
}
-int _OS::get_process_id() const {
- return OS::get_singleton()->get_process_id();
+int OS::get_process_id() const {
+ return ::OS::get_singleton()->get_process_id();
}
-bool _OS::has_environment(const String &p_var) const {
- return OS::get_singleton()->has_environment(p_var);
+bool OS::has_environment(const String &p_var) const {
+ return ::OS::get_singleton()->has_environment(p_var);
}
-String _OS::get_environment(const String &p_var) const {
- return OS::get_singleton()->get_environment(p_var);
+String OS::get_environment(const String &p_var) const {
+ return ::OS::get_singleton()->get_environment(p_var);
}
-bool _OS::set_environment(const String &p_var, const String &p_value) const {
- return OS::get_singleton()->set_environment(p_var, p_value);
+bool OS::set_environment(const String &p_var, const String &p_value) const {
+ return ::OS::get_singleton()->set_environment(p_var, p_value);
}
-String _OS::get_name() const {
- return OS::get_singleton()->get_name();
+String OS::get_name() const {
+ return ::OS::get_singleton()->get_name();
}
-Vector<String> _OS::get_cmdline_args() {
- List<String> cmdline = OS::get_singleton()->get_cmdline_args();
+Vector<String> OS::get_cmdline_args() {
+ List<String> cmdline = ::OS::get_singleton()->get_cmdline_args();
Vector<String> cmdlinev;
- for (List<String>::Element *E = cmdline.front(); E; E = E->next()) {
- cmdlinev.push_back(E->get());
+ for (const String &E : cmdline) {
+ cmdlinev.push_back(E);
}
return cmdlinev;
}
-String _OS::get_locale() const {
- return OS::get_singleton()->get_locale();
+String OS::get_locale() const {
+ return ::OS::get_singleton()->get_locale();
}
-String _OS::get_model_name() const {
- return OS::get_singleton()->get_model_name();
+String OS::get_model_name() const {
+ return ::OS::get_singleton()->get_model_name();
}
-Error _OS::set_thread_name(const String &p_name) {
- return Thread::set_name(p_name);
+Error OS::set_thread_name(const String &p_name) {
+ return ::Thread::set_name(p_name);
}
-Thread::ID _OS::get_thread_caller_id() const {
- return Thread::get_caller_id();
+::Thread::ID OS::get_thread_caller_id() const {
+ return ::Thread::get_caller_id();
};
-bool _OS::has_feature(const String &p_feature) const {
- return OS::get_singleton()->has_feature(p_feature);
-}
-
-uint64_t _OS::get_static_memory_usage() const {
- return OS::get_singleton()->get_static_memory_usage();
+bool OS::has_feature(const String &p_feature) const {
+ return ::OS::get_singleton()->has_feature(p_feature);
}
-uint64_t _OS::get_static_memory_peak_usage() const {
- return OS::get_singleton()->get_static_memory_peak_usage();
-}
-
-/**
- * Get current datetime with consideration for utc and
- * dst
- */
-Dictionary _OS::get_datetime(bool utc) const {
- Dictionary dated = get_date(utc);
- Dictionary timed = get_time(utc);
-
- List<Variant> keys;
- timed.get_key_list(&keys);
-
- for (int i = 0; i < keys.size(); i++) {
- dated[keys[i]] = timed[keys[i]];
- }
-
- return dated;
-}
-
-Dictionary _OS::get_date(bool utc) const {
- OS::Date date = OS::get_singleton()->get_date(utc);
- Dictionary dated;
- dated[YEAR_KEY] = date.year;
- dated[MONTH_KEY] = date.month;
- dated[DAY_KEY] = date.day;
- dated[WEEKDAY_KEY] = date.weekday;
- dated[DST_KEY] = date.dst;
- return dated;
-}
-
-Dictionary _OS::get_time(bool utc) const {
- OS::Time time = OS::get_singleton()->get_time(utc);
- Dictionary timed;
- timed[HOUR_KEY] = time.hour;
- timed[MINUTE_KEY] = time.min;
- timed[SECOND_KEY] = time.sec;
- return timed;
-}
-
-/**
- * Get an epoch time value from a dictionary of time values
- * @p datetime must be populated with the following keys:
- * day, hour, minute, month, second, year. (dst is ignored).
- *
- * You can pass the output from
- * get_datetime_from_unix_time directly into this function
- *
- * @param datetime dictionary of date and time values to convert
- *
- * @return epoch calculated
- */
-int64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const {
- // if datetime is an empty Dictionary throws an error
- ERR_FAIL_COND_V_MSG(datetime.is_empty(), 0, "Invalid datetime Dictionary: Dictionary is empty");
-
- // Bunch of conversion constants
- static const unsigned int SECONDS_PER_MINUTE = 60;
- static const unsigned int MINUTES_PER_HOUR = 60;
- static const unsigned int HOURS_PER_DAY = 24;
- static const unsigned int SECONDS_PER_HOUR = MINUTES_PER_HOUR * SECONDS_PER_MINUTE;
- static const unsigned int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY;
-
- // Get all time values from the dictionary, set to zero if it doesn't exist.
- // Risk incorrect calculation over throwing errors
- unsigned int second = ((datetime.has(SECOND_KEY)) ? static_cast<unsigned int>(datetime[SECOND_KEY]) : 0);
- unsigned int minute = ((datetime.has(MINUTE_KEY)) ? static_cast<unsigned int>(datetime[MINUTE_KEY]) : 0);
- unsigned int hour = ((datetime.has(HOUR_KEY)) ? static_cast<unsigned int>(datetime[HOUR_KEY]) : 0);
- unsigned int day = ((datetime.has(DAY_KEY)) ? static_cast<unsigned int>(datetime[DAY_KEY]) : 1);
- unsigned int month = ((datetime.has(MONTH_KEY)) ? static_cast<unsigned int>(datetime[MONTH_KEY]) : 1);
- unsigned int year = ((datetime.has(YEAR_KEY)) ? static_cast<unsigned int>(datetime[YEAR_KEY]) : 1970);
-
- /// How many days come before each month (0-12)
- static const unsigned short int DAYS_PAST_THIS_YEAR_TABLE[2][13] = {
- /* Normal years. */
- { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
- /* Leap years. */
- { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
- };
-
- ERR_FAIL_COND_V_MSG(second > 59, 0, "Invalid second value of: " + itos(second) + ".");
- ERR_FAIL_COND_V_MSG(minute > 59, 0, "Invalid minute value of: " + itos(minute) + ".");
- ERR_FAIL_COND_V_MSG(hour > 23, 0, "Invalid hour value of: " + itos(hour) + ".");
- ERR_FAIL_COND_V_MSG(year == 0, 0, "Years before 1 AD are not supported. Value passed: " + itos(year) + ".");
- ERR_FAIL_COND_V_MSG(month > 12 || month == 0, 0, "Invalid month value of: " + itos(month) + ".");
- // Do this check after month is tested as valid
- unsigned int days_in_month = MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1];
- ERR_FAIL_COND_V_MSG(day == 0 || day > days_in_month, 0, "Invalid day value of: " + itos(day) + ". It should be comprised between 1 and " + itos(days_in_month) + " for month " + itos(month) + ".");
-
- // Calculate all the seconds from months past in this year
- uint64_t SECONDS_FROM_MONTHS_PAST_THIS_YEAR = DAYS_PAST_THIS_YEAR_TABLE[LEAPYEAR(year)][month - 1] * SECONDS_PER_DAY;
-
- int64_t SECONDS_FROM_YEARS_PAST = 0;
- if (year >= EPOCH_YR) {
- for (unsigned int iyear = EPOCH_YR; iyear < year; iyear++) {
- SECONDS_FROM_YEARS_PAST += YEARSIZE(iyear) * SECONDS_PER_DAY;
- }
- } else {
- for (unsigned int iyear = EPOCH_YR - 1; iyear >= year; iyear--) {
- SECONDS_FROM_YEARS_PAST -= YEARSIZE(iyear) * SECONDS_PER_DAY;
- }
- }
-
- int64_t epoch =
- second +
- minute * SECONDS_PER_MINUTE +
- hour * SECONDS_PER_HOUR +
- // Subtract 1 from day, since the current day isn't over yet
- // and we cannot count all 24 hours.
- (day - 1) * SECONDS_PER_DAY +
- SECONDS_FROM_MONTHS_PAST_THIS_YEAR +
- SECONDS_FROM_YEARS_PAST;
- return epoch;
-}
-
-/**
- * Get a dictionary of time values when given epoch time
- *
- * Dictionary Time values will be a union if values from #get_time
- * and #get_date dictionaries (with the exception of dst =
- * day light standard time, as it cannot be determined from epoch)
- *
- * @param unix_time_val epoch time to convert
- *
- * @return dictionary of date and time values
- */
-Dictionary _OS::get_datetime_from_unix_time(int64_t unix_time_val) const {
- OS::Date date;
- OS::Time time;
-
- long dayclock, dayno;
- int year = EPOCH_YR;
-
- if (unix_time_val >= 0) {
- dayno = unix_time_val / SECS_DAY;
- dayclock = unix_time_val % SECS_DAY;
- /* day 0 was a thursday */
- date.weekday = static_cast<OS::Weekday>((dayno + 4) % 7);
- while (dayno >= YEARSIZE(year)) {
- dayno -= YEARSIZE(year);
- year++;
- }
- } else {
- dayno = (unix_time_val - SECS_DAY + 1) / SECS_DAY;
- dayclock = unix_time_val - dayno * SECS_DAY;
- date.weekday = static_cast<OS::Weekday>(((dayno % 7) + 11) % 7);
- do {
- year--;
- dayno += YEARSIZE(year);
- } while (dayno < 0);
- }
-
- time.sec = dayclock % 60;
- time.min = (dayclock % 3600) / 60;
- time.hour = dayclock / 3600;
- date.year = year;
-
- size_t imonth = 0;
-
- while ((unsigned long)dayno >= MONTH_DAYS_TABLE[LEAPYEAR(year)][imonth]) {
- dayno -= MONTH_DAYS_TABLE[LEAPYEAR(year)][imonth];
- imonth++;
- }
-
- /// Add 1 to month to make sure months are indexed starting at 1
- date.month = static_cast<OS::Month>(imonth + 1);
-
- date.day = dayno + 1;
-
- Dictionary timed;
- timed[HOUR_KEY] = time.hour;
- timed[MINUTE_KEY] = time.min;
- timed[SECOND_KEY] = time.sec;
- timed[YEAR_KEY] = date.year;
- timed[MONTH_KEY] = date.month;
- timed[DAY_KEY] = date.day;
- timed[WEEKDAY_KEY] = date.weekday;
-
- return timed;
+uint64_t OS::get_static_memory_usage() const {
+ return ::OS::get_singleton()->get_static_memory_usage();
}
-Dictionary _OS::get_time_zone_info() const {
- OS::TimeZoneInfo info = OS::get_singleton()->get_time_zone_info();
- Dictionary infod;
- infod["bias"] = info.bias;
- infod["name"] = info.name;
- return infod;
-}
-
-double _OS::get_unix_time() const {
- return OS::get_singleton()->get_unix_time();
+uint64_t OS::get_static_memory_peak_usage() const {
+ return ::OS::get_singleton()->get_static_memory_peak_usage();
}
/** This method uses a signed argument for better error reporting as it's used from the scripting API. */
-void _OS::delay_usec(int p_usec) const {
+void OS::delay_usec(int p_usec) const {
ERR_FAIL_COND_MSG(
p_usec < 0,
vformat("Can't sleep for %d microseconds. The delay provided must be greater than or equal to 0 microseconds.", p_usec));
- OS::get_singleton()->delay_usec(p_usec);
+ ::OS::get_singleton()->delay_usec(p_usec);
}
/** This method uses a signed argument for better error reporting as it's used from the scripting API. */
-void _OS::delay_msec(int p_msec) const {
+void OS::delay_msec(int p_msec) const {
ERR_FAIL_COND_MSG(
p_msec < 0,
vformat("Can't sleep for %d milliseconds. The delay provided must be greater than or equal to 0 milliseconds.", p_msec));
- OS::get_singleton()->delay_usec(int64_t(p_msec) * 1000);
-}
-
-uint32_t _OS::get_ticks_msec() const {
- return OS::get_singleton()->get_ticks_msec();
-}
-
-uint64_t _OS::get_ticks_usec() const {
- return OS::get_singleton()->get_ticks_usec();
+ ::OS::get_singleton()->delay_usec(int64_t(p_msec) * 1000);
}
-bool _OS::can_use_threads() const {
- return OS::get_singleton()->can_use_threads();
+bool OS::can_use_threads() const {
+ return ::OS::get_singleton()->can_use_threads();
}
-bool _OS::is_userfs_persistent() const {
- return OS::get_singleton()->is_userfs_persistent();
+bool OS::is_userfs_persistent() const {
+ return ::OS::get_singleton()->is_userfs_persistent();
}
-int _OS::get_processor_count() const {
- return OS::get_singleton()->get_processor_count();
+int OS::get_processor_count() const {
+ return ::OS::get_singleton()->get_processor_count();
}
-bool _OS::is_stdout_verbose() const {
- return OS::get_singleton()->is_stdout_verbose();
+bool OS::is_stdout_verbose() const {
+ return ::OS::get_singleton()->is_stdout_verbose();
}
-void _OS::dump_memory_to_file(const String &p_file) {
- OS::get_singleton()->dump_memory_to_file(p_file.utf8().get_data());
+void OS::dump_memory_to_file(const String &p_file) {
+ ::OS::get_singleton()->dump_memory_to_file(p_file.utf8().get_data());
}
-struct _OSCoreBindImg {
+struct OSCoreBindImg {
String path;
Size2 size;
int fmt = 0;
ObjectID id;
int vram = 0;
- bool operator<(const _OSCoreBindImg &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
+ bool operator<(const OSCoreBindImg &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
};
-void _OS::print_all_textures_by_size() {
- List<_OSCoreBindImg> imgs;
+void OS::print_all_textures_by_size() {
+ List<OSCoreBindImg> imgs;
uint64_t total = 0;
{
List<Ref<Resource>> rsrc;
ResourceCache::get_cached_resources(&rsrc);
- for (List<Ref<Resource>>::Element *E = rsrc.front(); E; E = E->next()) {
- if (!E->get()->is_class("ImageTexture")) {
+ for (Ref<Resource> &res : rsrc) {
+ if (!res->is_class("Texture")) {
continue;
}
- Size2 size = E->get()->call("get_size");
- int fmt = E->get()->call("get_format");
+ Size2 size = res->call("get_size");
+ int fmt = res->call("get_format");
- _OSCoreBindImg img;
+ OSCoreBindImg img;
img.size = size;
img.fmt = fmt;
- img.path = E->get()->get_path();
+ img.path = res->get_path();
img.vram = Image::get_image_data_size(img.size.width, img.size.height, Image::Format(img.fmt));
- img.id = E->get()->get_instance_id();
+ img.id = res->get_instance_id();
total += img.vram;
imgs.push_back(img);
}
@@ -594,20 +383,34 @@ void _OS::print_all_textures_by_size() {
imgs.sort();
- for (List<_OSCoreBindImg>::Element *E = imgs.front(); E; E = E->next()) {
- total -= E->get().vram;
+ if (imgs.size() == 0) {
+ print_line("No textures seem used in this project.");
+ } else {
+ print_line("Textures currently in use, sorted by VRAM usage:\n"
+ "Path - VRAM usage (Dimensions)");
+ }
+
+ for (const OSCoreBindImg &img : imgs) {
+ print_line(vformat("%s - %s %s",
+ img.path,
+ String::humanize_size(img.vram),
+ img.size));
}
+
+ print_line(vformat("Total VRAM usage: %s.", String::humanize_size(total)));
}
-void _OS::print_resources_by_type(const Vector<String> &p_types) {
- Map<String, int> type_count;
+void OS::print_resources_by_type(const Vector<String> &p_types) {
+ ERR_FAIL_COND_MSG(p_types.size() == 0,
+ "At least one type should be provided to print resources by type.");
+ print_line(vformat("Resources currently in use for the following types: %s", p_types));
+
+ Map<String, int> type_count;
List<Ref<Resource>> resources;
ResourceCache::get_cached_resources(&resources);
- for (List<Ref<Resource>>::Element *E = resources.front(); E; E = E->next()) {
- Ref<Resource> r = E->get();
-
+ for (const Ref<Resource> &r : resources) {
bool found = false;
for (int i = 0; i < p_types.size(); i++) {
@@ -624,26 +427,53 @@ void _OS::print_resources_by_type(const Vector<String> &p_types) {
}
type_count[r->get_class()]++;
+
+ print_line(vformat("%s: %s", r->get_class(), r->get_path()));
+
+ List<StringName> metas;
+ r->get_meta_list(&metas);
+ for (const StringName &meta : metas) {
+ print_line(vformat(" %s: %s", meta, r->get_meta(meta)));
+ }
+ }
+
+ for (const KeyValue<String, int> &E : type_count) {
+ print_line(vformat("%s count: %d", E.key, E.value));
}
}
-void _OS::print_all_resources(const String &p_to_file) {
- OS::get_singleton()->print_all_resources(p_to_file);
+void OS::print_all_resources(const String &p_to_file) {
+ ::OS::get_singleton()->print_all_resources(p_to_file);
+}
+
+void OS::print_resources_in_use(bool p_short) {
+ ::OS::get_singleton()->print_resources_in_use(p_short);
+}
+
+void OS::dump_resources_to_file(const String &p_file) {
+ ::OS::get_singleton()->dump_resources_to_file(p_file.utf8().get_data());
}
-void _OS::print_resources_in_use(bool p_short) {
- OS::get_singleton()->print_resources_in_use(p_short);
+String OS::get_user_data_dir() const {
+ return ::OS::get_singleton()->get_user_data_dir();
}
-void _OS::dump_resources_to_file(const String &p_file) {
- OS::get_singleton()->dump_resources_to_file(p_file.utf8().get_data());
+String OS::get_config_dir() const {
+ // Exposed as `get_config_dir()` instead of `get_config_path()` for consistency with other exposed OS methods.
+ return ::OS::get_singleton()->get_config_path();
}
-String _OS::get_user_data_dir() const {
- return OS::get_singleton()->get_user_data_dir();
+String OS::get_data_dir() const {
+ // Exposed as `get_data_dir()` instead of `get_data_path()` for consistency with other exposed OS methods.
+ return ::OS::get_singleton()->get_data_path();
}
-bool _OS::is_debug_build() const {
+String OS::get_cache_dir() const {
+ // Exposed as `get_cache_dir()` instead of `get_cache_path()` for consistency with other exposed OS methods.
+ return ::OS::get_singleton()->get_cache_path();
+}
+
+bool OS::is_debug_build() const {
#ifdef DEBUG_ENABLED
return true;
#else
@@ -651,118 +481,113 @@ bool _OS::is_debug_build() const {
#endif
}
-String _OS::get_system_dir(SystemDir p_dir) const {
- return OS::get_singleton()->get_system_dir(OS::SystemDir(p_dir));
+String OS::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {
+ return ::OS::get_singleton()->get_system_dir(::OS::SystemDir(p_dir), p_shared_storage);
}
-String _OS::get_keycode_string(uint32_t p_code) const {
- return keycode_get_string(p_code);
+String OS::get_keycode_string(uint32_t p_code) const {
+ return ::keycode_get_string(p_code);
}
-bool _OS::is_keycode_unicode(uint32_t p_unicode) const {
- return keycode_has_unicode(p_unicode);
+bool OS::is_keycode_unicode(uint32_t p_unicode) const {
+ return ::keycode_has_unicode(p_unicode);
}
-int _OS::find_keycode_from_string(const String &p_code) const {
+int OS::find_keycode_from_string(const String &p_code) const {
return find_keycode(p_code);
}
-bool _OS::request_permission(const String &p_name) {
- return OS::get_singleton()->request_permission(p_name);
+bool OS::request_permission(const String &p_name) {
+ return ::OS::get_singleton()->request_permission(p_name);
}
-bool _OS::request_permissions() {
- return OS::get_singleton()->request_permissions();
+bool OS::request_permissions() {
+ return ::OS::get_singleton()->request_permissions();
}
-Vector<String> _OS::get_granted_permissions() const {
- return OS::get_singleton()->get_granted_permissions();
+Vector<String> OS::get_granted_permissions() const {
+ return ::OS::get_singleton()->get_granted_permissions();
}
-String _OS::get_unique_id() const {
- return OS::get_singleton()->get_unique_id();
+String OS::get_unique_id() const {
+ return ::OS::get_singleton()->get_unique_id();
}
-_OS *_OS::singleton = nullptr;
+OS *OS::singleton = nullptr;
-void _OS::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_connected_midi_inputs"), &_OS::get_connected_midi_inputs);
- ClassDB::bind_method(D_METHOD("open_midi_inputs"), &_OS::open_midi_inputs);
- ClassDB::bind_method(D_METHOD("close_midi_inputs"), &_OS::close_midi_inputs);
+void OS::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_connected_midi_inputs"), &OS::get_connected_midi_inputs);
+ ClassDB::bind_method(D_METHOD("open_midi_inputs"), &OS::open_midi_inputs);
+ ClassDB::bind_method(D_METHOD("close_midi_inputs"), &OS::close_midi_inputs);
- ClassDB::bind_method(D_METHOD("set_low_processor_usage_mode", "enable"), &_OS::set_low_processor_usage_mode);
- ClassDB::bind_method(D_METHOD("is_in_low_processor_usage_mode"), &_OS::is_in_low_processor_usage_mode);
+ ClassDB::bind_method(D_METHOD("alert", "text", "title"), &OS::alert, DEFVAL("Alert!"));
- ClassDB::bind_method(D_METHOD("set_low_processor_usage_mode_sleep_usec", "usec"), &_OS::set_low_processor_usage_mode_sleep_usec);
- ClassDB::bind_method(D_METHOD("get_low_processor_usage_mode_sleep_usec"), &_OS::get_low_processor_usage_mode_sleep_usec);
+ ClassDB::bind_method(D_METHOD("set_low_processor_usage_mode", "enable"), &OS::set_low_processor_usage_mode);
+ ClassDB::bind_method(D_METHOD("is_in_low_processor_usage_mode"), &OS::is_in_low_processor_usage_mode);
- ClassDB::bind_method(D_METHOD("get_processor_count"), &_OS::get_processor_count);
+ ClassDB::bind_method(D_METHOD("set_low_processor_usage_mode_sleep_usec", "usec"), &OS::set_low_processor_usage_mode_sleep_usec);
+ ClassDB::bind_method(D_METHOD("get_low_processor_usage_mode_sleep_usec"), &OS::get_low_processor_usage_mode_sleep_usec);
- ClassDB::bind_method(D_METHOD("get_executable_path"), &_OS::get_executable_path);
- ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr"), &_OS::execute, DEFVAL(Array()), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("create_process", "path", "arguments"), &_OS::create_process);
- ClassDB::bind_method(D_METHOD("kill", "pid"), &_OS::kill);
- ClassDB::bind_method(D_METHOD("shell_open", "uri"), &_OS::shell_open);
- ClassDB::bind_method(D_METHOD("get_process_id"), &_OS::get_process_id);
+ ClassDB::bind_method(D_METHOD("get_processor_count"), &OS::get_processor_count);
- ClassDB::bind_method(D_METHOD("get_environment", "variable"), &_OS::get_environment);
- ClassDB::bind_method(D_METHOD("set_environment", "variable", "value"), &_OS::set_environment);
- ClassDB::bind_method(D_METHOD("has_environment", "variable"), &_OS::has_environment);
+ ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);
+ ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr"), &OS::execute, DEFVAL(Array()), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("create_process", "path", "arguments"), &OS::create_process);
+ ClassDB::bind_method(D_METHOD("kill", "pid"), &OS::kill);
+ ClassDB::bind_method(D_METHOD("shell_open", "uri"), &OS::shell_open);
+ ClassDB::bind_method(D_METHOD("get_process_id"), &OS::get_process_id);
- ClassDB::bind_method(D_METHOD("get_name"), &_OS::get_name);
- ClassDB::bind_method(D_METHOD("get_cmdline_args"), &_OS::get_cmdline_args);
+ ClassDB::bind_method(D_METHOD("get_environment", "variable"), &OS::get_environment);
+ ClassDB::bind_method(D_METHOD("set_environment", "variable", "value"), &OS::set_environment);
+ ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment);
- ClassDB::bind_method(D_METHOD("get_datetime", "utc"), &_OS::get_datetime, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("get_date", "utc"), &_OS::get_date, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("get_time", "utc"), &_OS::get_time, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("get_time_zone_info"), &_OS::get_time_zone_info);
- ClassDB::bind_method(D_METHOD("get_unix_time"), &_OS::get_unix_time);
- ClassDB::bind_method(D_METHOD("get_datetime_from_unix_time", "unix_time_val"), &_OS::get_datetime_from_unix_time);
- ClassDB::bind_method(D_METHOD("get_unix_time_from_datetime", "datetime"), &_OS::get_unix_time_from_datetime);
+ ClassDB::bind_method(D_METHOD("get_name"), &OS::get_name);
+ ClassDB::bind_method(D_METHOD("get_cmdline_args"), &OS::get_cmdline_args);
- ClassDB::bind_method(D_METHOD("delay_usec", "usec"), &_OS::delay_usec);
- ClassDB::bind_method(D_METHOD("delay_msec", "msec"), &_OS::delay_msec);
- ClassDB::bind_method(D_METHOD("get_ticks_msec"), &_OS::get_ticks_msec);
- ClassDB::bind_method(D_METHOD("get_ticks_usec"), &_OS::get_ticks_usec);
- ClassDB::bind_method(D_METHOD("get_locale"), &_OS::get_locale);
- ClassDB::bind_method(D_METHOD("get_model_name"), &_OS::get_model_name);
+ ClassDB::bind_method(D_METHOD("delay_usec", "usec"), &OS::delay_usec);
+ ClassDB::bind_method(D_METHOD("delay_msec", "msec"), &OS::delay_msec);
+ ClassDB::bind_method(D_METHOD("get_locale"), &OS::get_locale);
+ ClassDB::bind_method(D_METHOD("get_model_name"), &OS::get_model_name);
- ClassDB::bind_method(D_METHOD("is_userfs_persistent"), &_OS::is_userfs_persistent);
- ClassDB::bind_method(D_METHOD("is_stdout_verbose"), &_OS::is_stdout_verbose);
+ ClassDB::bind_method(D_METHOD("is_userfs_persistent"), &OS::is_userfs_persistent);
+ ClassDB::bind_method(D_METHOD("is_stdout_verbose"), &OS::is_stdout_verbose);
- ClassDB::bind_method(D_METHOD("can_use_threads"), &_OS::can_use_threads);
+ ClassDB::bind_method(D_METHOD("can_use_threads"), &OS::can_use_threads);
- ClassDB::bind_method(D_METHOD("is_debug_build"), &_OS::is_debug_build);
+ ClassDB::bind_method(D_METHOD("is_debug_build"), &OS::is_debug_build);
- ClassDB::bind_method(D_METHOD("dump_memory_to_file", "file"), &_OS::dump_memory_to_file);
- ClassDB::bind_method(D_METHOD("dump_resources_to_file", "file"), &_OS::dump_resources_to_file);
- ClassDB::bind_method(D_METHOD("print_resources_in_use", "short"), &_OS::print_resources_in_use, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("print_all_resources", "tofile"), &_OS::print_all_resources, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("dump_memory_to_file", "file"), &OS::dump_memory_to_file);
+ ClassDB::bind_method(D_METHOD("dump_resources_to_file", "file"), &OS::dump_resources_to_file);
+ ClassDB::bind_method(D_METHOD("print_resources_in_use", "short"), &OS::print_resources_in_use, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("print_all_resources", "tofile"), &OS::print_all_resources, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("get_static_memory_usage"), &_OS::get_static_memory_usage);
- ClassDB::bind_method(D_METHOD("get_static_memory_peak_usage"), &_OS::get_static_memory_peak_usage);
+ ClassDB::bind_method(D_METHOD("get_static_memory_usage"), &OS::get_static_memory_usage);
+ ClassDB::bind_method(D_METHOD("get_static_memory_peak_usage"), &OS::get_static_memory_peak_usage);
- ClassDB::bind_method(D_METHOD("get_user_data_dir"), &_OS::get_user_data_dir);
- ClassDB::bind_method(D_METHOD("get_system_dir", "dir"), &_OS::get_system_dir);
- ClassDB::bind_method(D_METHOD("get_unique_id"), &_OS::get_unique_id);
+ ClassDB::bind_method(D_METHOD("get_user_data_dir"), &OS::get_user_data_dir);
+ ClassDB::bind_method(D_METHOD("get_system_dir", "dir", "shared_storage"), &OS::get_system_dir, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("get_config_dir"), &OS::get_config_dir);
+ ClassDB::bind_method(D_METHOD("get_data_dir"), &OS::get_data_dir);
+ ClassDB::bind_method(D_METHOD("get_cache_dir"), &OS::get_cache_dir);
+ ClassDB::bind_method(D_METHOD("get_unique_id"), &OS::get_unique_id);
- ClassDB::bind_method(D_METHOD("print_all_textures_by_size"), &_OS::print_all_textures_by_size);
- ClassDB::bind_method(D_METHOD("print_resources_by_type", "types"), &_OS::print_resources_by_type);
+ ClassDB::bind_method(D_METHOD("print_all_textures_by_size"), &OS::print_all_textures_by_size);
+ ClassDB::bind_method(D_METHOD("print_resources_by_type", "types"), &OS::print_resources_by_type);
- ClassDB::bind_method(D_METHOD("get_keycode_string", "code"), &_OS::get_keycode_string);
- ClassDB::bind_method(D_METHOD("is_keycode_unicode", "code"), &_OS::is_keycode_unicode);
- ClassDB::bind_method(D_METHOD("find_keycode_from_string", "string"), &_OS::find_keycode_from_string);
+ ClassDB::bind_method(D_METHOD("get_keycode_string", "code"), &OS::get_keycode_string);
+ ClassDB::bind_method(D_METHOD("is_keycode_unicode", "code"), &OS::is_keycode_unicode);
+ ClassDB::bind_method(D_METHOD("find_keycode_from_string", "string"), &OS::find_keycode_from_string);
- ClassDB::bind_method(D_METHOD("set_use_file_access_save_and_swap", "enabled"), &_OS::set_use_file_access_save_and_swap);
+ ClassDB::bind_method(D_METHOD("set_use_file_access_save_and_swap", "enabled"), &OS::set_use_file_access_save_and_swap);
- ClassDB::bind_method(D_METHOD("set_thread_name", "name"), &_OS::set_thread_name);
- ClassDB::bind_method(D_METHOD("get_thread_caller_id"), &_OS::get_thread_caller_id);
+ ClassDB::bind_method(D_METHOD("set_thread_name", "name"), &OS::set_thread_name);
+ ClassDB::bind_method(D_METHOD("get_thread_caller_id"), &OS::get_thread_caller_id);
- ClassDB::bind_method(D_METHOD("has_feature", "tag_name"), &_OS::has_feature);
+ ClassDB::bind_method(D_METHOD("has_feature", "tag_name"), &OS::has_feature);
- ClassDB::bind_method(D_METHOD("request_permission", "name"), &_OS::request_permission);
- ClassDB::bind_method(D_METHOD("request_permissions"), &_OS::request_permissions);
- ClassDB::bind_method(D_METHOD("get_granted_permissions"), &_OS::get_granted_permissions);
+ ClassDB::bind_method(D_METHOD("request_permission", "name"), &OS::request_permission);
+ ClassDB::bind_method(D_METHOD("request_permissions"), &OS::request_permissions);
+ ClassDB::bind_method(D_METHOD("get_granted_permissions"), &OS::get_granted_permissions);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "low_processor_usage_mode"), "set_low_processor_usage_mode", "is_in_low_processor_usage_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "low_processor_usage_mode_sleep_usec"), "set_low_processor_usage_mode_sleep_usec", "get_low_processor_usage_mode_sleep_usec");
@@ -807,43 +632,43 @@ void _OS::_bind_methods() {
BIND_ENUM_CONSTANT(SYSTEM_DIR_RINGTONES);
}
-////// _Geometry2D //////
+////// Geometry2D //////
-_Geometry2D *_Geometry2D::singleton = nullptr;
+Geometry2D *Geometry2D::singleton = nullptr;
-_Geometry2D *_Geometry2D::get_singleton() {
+Geometry2D *Geometry2D::get_singleton() {
return singleton;
}
-bool _Geometry2D::is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius) {
- return Geometry2D::is_point_in_circle(p_point, p_circle_pos, p_circle_radius);
+bool Geometry2D::is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius) {
+ return ::Geometry2D::is_point_in_circle(p_point, p_circle_pos, p_circle_radius);
}
-real_t _Geometry2D::segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) {
- return Geometry2D::segment_intersects_circle(p_from, p_to, p_circle_pos, p_circle_radius);
+real_t Geometry2D::segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) {
+ return ::Geometry2D::segment_intersects_circle(p_from, p_to, p_circle_pos, p_circle_radius);
}
-Variant _Geometry2D::segment_intersects_segment(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b) {
+Variant Geometry2D::segment_intersects_segment(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b) {
Vector2 result;
- if (Geometry2D::segment_intersects_segment(p_from_a, p_to_a, p_from_b, p_to_b, &result)) {
+ if (::Geometry2D::segment_intersects_segment(p_from_a, p_to_a, p_from_b, p_to_b, &result)) {
return result;
} else {
return Variant();
}
}
-Variant _Geometry2D::line_intersects_line(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b) {
+Variant Geometry2D::line_intersects_line(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b) {
Vector2 result;
- if (Geometry2D::line_intersects_line(p_from_a, p_dir_a, p_from_b, p_dir_b, result)) {
+ if (::Geometry2D::line_intersects_line(p_from_a, p_dir_a, p_from_b, p_dir_b, result)) {
return result;
} else {
return Variant();
}
}
-Vector<Vector2> _Geometry2D::get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2) {
+Vector<Vector2> Geometry2D::get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2) {
Vector2 r1, r2;
- Geometry2D::get_closest_points_between_segments(p1, q1, p2, q2, r1, r2);
+ ::Geometry2D::get_closest_points_between_segments(p1, q1, p2, q2, r1, r2);
Vector<Vector2> r;
r.resize(2);
r.set(0, r1);
@@ -851,42 +676,42 @@ Vector<Vector2> _Geometry2D::get_closest_points_between_segments(const Vector2 &
return r;
}
-Vector2 _Geometry2D::get_closest_point_to_segment(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b) {
+Vector2 Geometry2D::get_closest_point_to_segment(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b) {
Vector2 s[2] = { p_a, p_b };
- return Geometry2D::get_closest_point_to_segment(p_point, s);
+ return ::Geometry2D::get_closest_point_to_segment(p_point, s);
}
-Vector2 _Geometry2D::get_closest_point_to_segment_uncapped(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b) {
+Vector2 Geometry2D::get_closest_point_to_segment_uncapped(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b) {
Vector2 s[2] = { p_a, p_b };
- return Geometry2D::get_closest_point_to_segment_uncapped(p_point, s);
+ return ::Geometry2D::get_closest_point_to_segment_uncapped(p_point, s);
}
-bool _Geometry2D::point_is_inside_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) const {
- return Geometry2D::is_point_in_triangle(s, a, b, c);
+bool Geometry2D::point_is_inside_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) const {
+ return ::Geometry2D::is_point_in_triangle(s, a, b, c);
}
-bool _Geometry2D::is_polygon_clockwise(const Vector<Vector2> &p_polygon) {
- return Geometry2D::is_polygon_clockwise(p_polygon);
+bool Geometry2D::is_polygon_clockwise(const Vector<Vector2> &p_polygon) {
+ return ::Geometry2D::is_polygon_clockwise(p_polygon);
}
-bool _Geometry2D::is_point_in_polygon(const Point2 &p_point, const Vector<Vector2> &p_polygon) {
- return Geometry2D::is_point_in_polygon(p_point, p_polygon);
+bool Geometry2D::is_point_in_polygon(const Point2 &p_point, const Vector<Vector2> &p_polygon) {
+ return ::Geometry2D::is_point_in_polygon(p_point, p_polygon);
}
-Vector<int> _Geometry2D::triangulate_polygon(const Vector<Vector2> &p_polygon) {
- return Geometry2D::triangulate_polygon(p_polygon);
+Vector<int> Geometry2D::triangulate_polygon(const Vector<Vector2> &p_polygon) {
+ return ::Geometry2D::triangulate_polygon(p_polygon);
}
-Vector<int> _Geometry2D::triangulate_delaunay(const Vector<Vector2> &p_points) {
- return Geometry2D::triangulate_delaunay(p_points);
+Vector<int> Geometry2D::triangulate_delaunay(const Vector<Vector2> &p_points) {
+ return ::Geometry2D::triangulate_delaunay(p_points);
}
-Vector<Point2> _Geometry2D::convex_hull(const Vector<Point2> &p_points) {
- return Geometry2D::convex_hull(p_points);
+Vector<Point2> Geometry2D::convex_hull(const Vector<Point2> &p_points) {
+ return ::Geometry2D::convex_hull(p_points);
}
-Array _Geometry2D::merge_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
- Vector<Vector<Point2>> polys = Geometry2D::merge_polygons(p_polygon_a, p_polygon_b);
+Array Geometry2D::merge_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
+ Vector<Vector<Point2>> polys = ::Geometry2D::merge_polygons(p_polygon_a, p_polygon_b);
Array ret;
@@ -896,8 +721,8 @@ Array _Geometry2D::merge_polygons(const Vector<Vector2> &p_polygon_a, const Vect
return ret;
}
-Array _Geometry2D::clip_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
- Vector<Vector<Point2>> polys = Geometry2D::clip_polygons(p_polygon_a, p_polygon_b);
+Array Geometry2D::clip_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
+ Vector<Vector<Point2>> polys = ::Geometry2D::clip_polygons(p_polygon_a, p_polygon_b);
Array ret;
@@ -907,8 +732,8 @@ Array _Geometry2D::clip_polygons(const Vector<Vector2> &p_polygon_a, const Vecto
return ret;
}
-Array _Geometry2D::intersect_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
- Vector<Vector<Point2>> polys = Geometry2D::intersect_polygons(p_polygon_a, p_polygon_b);
+Array Geometry2D::intersect_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
+ Vector<Vector<Point2>> polys = ::Geometry2D::intersect_polygons(p_polygon_a, p_polygon_b);
Array ret;
@@ -918,8 +743,8 @@ Array _Geometry2D::intersect_polygons(const Vector<Vector2> &p_polygon_a, const
return ret;
}
-Array _Geometry2D::exclude_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
- Vector<Vector<Point2>> polys = Geometry2D::exclude_polygons(p_polygon_a, p_polygon_b);
+Array Geometry2D::exclude_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
+ Vector<Vector<Point2>> polys = ::Geometry2D::exclude_polygons(p_polygon_a, p_polygon_b);
Array ret;
@@ -929,8 +754,8 @@ Array _Geometry2D::exclude_polygons(const Vector<Vector2> &p_polygon_a, const Ve
return ret;
}
-Array _Geometry2D::clip_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
- Vector<Vector<Point2>> polys = Geometry2D::clip_polyline_with_polygon(p_polyline, p_polygon);
+Array Geometry2D::clip_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
+ Vector<Vector<Point2>> polys = ::Geometry2D::clip_polyline_with_polygon(p_polyline, p_polygon);
Array ret;
@@ -940,8 +765,8 @@ Array _Geometry2D::clip_polyline_with_polygon(const Vector<Vector2> &p_polyline,
return ret;
}
-Array _Geometry2D::intersect_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
- Vector<Vector<Point2>> polys = Geometry2D::intersect_polyline_with_polygon(p_polyline, p_polygon);
+Array Geometry2D::intersect_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
+ Vector<Vector<Point2>> polys = ::Geometry2D::intersect_polyline_with_polygon(p_polyline, p_polygon);
Array ret;
@@ -951,8 +776,8 @@ Array _Geometry2D::intersect_polyline_with_polygon(const Vector<Vector2> &p_poly
return ret;
}
-Array _Geometry2D::offset_polygon(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) {
- Vector<Vector<Point2>> polys = Geometry2D::offset_polygon(p_polygon, p_delta, Geometry2D::PolyJoinType(p_join_type));
+Array Geometry2D::offset_polygon(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) {
+ Vector<Vector<Point2>> polys = ::Geometry2D::offset_polygon(p_polygon, p_delta, ::Geometry2D::PolyJoinType(p_join_type));
Array ret;
@@ -962,8 +787,8 @@ Array _Geometry2D::offset_polygon(const Vector<Vector2> &p_polygon, real_t p_del
return ret;
}
-Array _Geometry2D::offset_polyline(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) {
- Vector<Vector<Point2>> polys = Geometry2D::offset_polyline(p_polygon, p_delta, Geometry2D::PolyJoinType(p_join_type), Geometry2D::PolyEndType(p_end_type));
+Array Geometry2D::offset_polyline(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) {
+ Vector<Vector<Point2>> polys = ::Geometry2D::offset_polyline(p_polygon, p_delta, ::Geometry2D::PolyJoinType(p_join_type), ::Geometry2D::PolyEndType(p_end_type));
Array ret;
@@ -973,7 +798,7 @@ Array _Geometry2D::offset_polyline(const Vector<Vector2> &p_polygon, real_t p_de
return ret;
}
-Dictionary _Geometry2D::make_atlas(const Vector<Size2> &p_rects) {
+Dictionary Geometry2D::make_atlas(const Vector<Size2> &p_rects) {
Dictionary ret;
Vector<Size2i> rects;
@@ -984,7 +809,7 @@ Dictionary _Geometry2D::make_atlas(const Vector<Size2> &p_rects) {
Vector<Point2i> result;
Size2i size;
- Geometry2D::make_atlas(rects, result, size);
+ ::Geometry2D::make_atlas(rects, result, size);
Size2 r_size = size;
Vector<Point2> r_result;
@@ -998,37 +823,37 @@ Dictionary _Geometry2D::make_atlas(const Vector<Size2> &p_rects) {
return ret;
}
-void _Geometry2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("is_point_in_circle", "point", "circle_position", "circle_radius"), &_Geometry2D::is_point_in_circle);
- ClassDB::bind_method(D_METHOD("segment_intersects_segment", "from_a", "to_a", "from_b", "to_b"), &_Geometry2D::segment_intersects_segment);
- ClassDB::bind_method(D_METHOD("line_intersects_line", "from_a", "dir_a", "from_b", "dir_b"), &_Geometry2D::line_intersects_line);
+void Geometry2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("is_point_in_circle", "point", "circle_position", "circle_radius"), &Geometry2D::is_point_in_circle);
+ ClassDB::bind_method(D_METHOD("segment_intersects_segment", "from_a", "to_a", "from_b", "to_b"), &Geometry2D::segment_intersects_segment);
+ ClassDB::bind_method(D_METHOD("line_intersects_line", "from_a", "dir_a", "from_b", "dir_b"), &Geometry2D::line_intersects_line);
- ClassDB::bind_method(D_METHOD("get_closest_points_between_segments", "p1", "q1", "p2", "q2"), &_Geometry2D::get_closest_points_between_segments);
+ ClassDB::bind_method(D_METHOD("get_closest_points_between_segments", "p1", "q1", "p2", "q2"), &Geometry2D::get_closest_points_between_segments);
- ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "point", "s1", "s2"), &_Geometry2D::get_closest_point_to_segment);
+ ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "point", "s1", "s2"), &Geometry2D::get_closest_point_to_segment);
- ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &_Geometry2D::get_closest_point_to_segment_uncapped);
+ ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &Geometry2D::get_closest_point_to_segment_uncapped);
- ClassDB::bind_method(D_METHOD("point_is_inside_triangle", "point", "a", "b", "c"), &_Geometry2D::point_is_inside_triangle);
+ ClassDB::bind_method(D_METHOD("point_is_inside_triangle", "point", "a", "b", "c"), &Geometry2D::point_is_inside_triangle);
- ClassDB::bind_method(D_METHOD("is_polygon_clockwise", "polygon"), &_Geometry2D::is_polygon_clockwise);
- ClassDB::bind_method(D_METHOD("is_point_in_polygon", "point", "polygon"), &_Geometry2D::is_point_in_polygon);
- ClassDB::bind_method(D_METHOD("triangulate_polygon", "polygon"), &_Geometry2D::triangulate_polygon);
- ClassDB::bind_method(D_METHOD("triangulate_delaunay", "points"), &_Geometry2D::triangulate_delaunay);
- ClassDB::bind_method(D_METHOD("convex_hull", "points"), &_Geometry2D::convex_hull);
+ ClassDB::bind_method(D_METHOD("is_polygon_clockwise", "polygon"), &Geometry2D::is_polygon_clockwise);
+ ClassDB::bind_method(D_METHOD("is_point_in_polygon", "point", "polygon"), &Geometry2D::is_point_in_polygon);
+ ClassDB::bind_method(D_METHOD("triangulate_polygon", "polygon"), &Geometry2D::triangulate_polygon);
+ ClassDB::bind_method(D_METHOD("triangulate_delaunay", "points"), &Geometry2D::triangulate_delaunay);
+ ClassDB::bind_method(D_METHOD("convex_hull", "points"), &Geometry2D::convex_hull);
- ClassDB::bind_method(D_METHOD("merge_polygons", "polygon_a", "polygon_b"), &_Geometry2D::merge_polygons);
- ClassDB::bind_method(D_METHOD("clip_polygons", "polygon_a", "polygon_b"), &_Geometry2D::clip_polygons);
- ClassDB::bind_method(D_METHOD("intersect_polygons", "polygon_a", "polygon_b"), &_Geometry2D::intersect_polygons);
- ClassDB::bind_method(D_METHOD("exclude_polygons", "polygon_a", "polygon_b"), &_Geometry2D::exclude_polygons);
+ ClassDB::bind_method(D_METHOD("merge_polygons", "polygon_a", "polygon_b"), &Geometry2D::merge_polygons);
+ ClassDB::bind_method(D_METHOD("clip_polygons", "polygon_a", "polygon_b"), &Geometry2D::clip_polygons);
+ ClassDB::bind_method(D_METHOD("intersect_polygons", "polygon_a", "polygon_b"), &Geometry2D::intersect_polygons);
+ ClassDB::bind_method(D_METHOD("exclude_polygons", "polygon_a", "polygon_b"), &Geometry2D::exclude_polygons);
- ClassDB::bind_method(D_METHOD("clip_polyline_with_polygon", "polyline", "polygon"), &_Geometry2D::clip_polyline_with_polygon);
- ClassDB::bind_method(D_METHOD("intersect_polyline_with_polygon", "polyline", "polygon"), &_Geometry2D::intersect_polyline_with_polygon);
+ ClassDB::bind_method(D_METHOD("clip_polyline_with_polygon", "polyline", "polygon"), &Geometry2D::clip_polyline_with_polygon);
+ ClassDB::bind_method(D_METHOD("intersect_polyline_with_polygon", "polyline", "polygon"), &Geometry2D::intersect_polyline_with_polygon);
- ClassDB::bind_method(D_METHOD("offset_polygon", "polygon", "delta", "join_type"), &_Geometry2D::offset_polygon, DEFVAL(JOIN_SQUARE));
- ClassDB::bind_method(D_METHOD("offset_polyline", "polyline", "delta", "join_type", "end_type"), &_Geometry2D::offset_polyline, DEFVAL(JOIN_SQUARE), DEFVAL(END_SQUARE));
+ ClassDB::bind_method(D_METHOD("offset_polygon", "polygon", "delta", "join_type"), &Geometry2D::offset_polygon, DEFVAL(JOIN_SQUARE));
+ ClassDB::bind_method(D_METHOD("offset_polyline", "polyline", "delta", "join_type", "end_type"), &Geometry2D::offset_polyline, DEFVAL(JOIN_SQUARE), DEFVAL(END_SQUARE));
- ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &_Geometry2D::make_atlas);
+ ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &Geometry2D::make_atlas);
BIND_ENUM_CONSTANT(OPERATION_UNION);
BIND_ENUM_CONSTANT(OPERATION_DIFFERENCE);
@@ -1046,29 +871,29 @@ void _Geometry2D::_bind_methods() {
BIND_ENUM_CONSTANT(END_ROUND);
}
-////// _Geometry3D //////
+////// Geometry3D //////
-_Geometry3D *_Geometry3D::singleton = nullptr;
+Geometry3D *Geometry3D::singleton = nullptr;
-_Geometry3D *_Geometry3D::get_singleton() {
+Geometry3D *Geometry3D::get_singleton() {
return singleton;
}
-Vector<Plane> _Geometry3D::build_box_planes(const Vector3 &p_extents) {
- return Geometry3D::build_box_planes(p_extents);
+Vector<Plane> Geometry3D::build_box_planes(const Vector3 &p_extents) {
+ return ::Geometry3D::build_box_planes(p_extents);
}
-Vector<Plane> _Geometry3D::build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis) {
- return Geometry3D::build_cylinder_planes(p_radius, p_height, p_sides, p_axis);
+Vector<Plane> Geometry3D::build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis) {
+ return ::Geometry3D::build_cylinder_planes(p_radius, p_height, p_sides, p_axis);
}
-Vector<Plane> _Geometry3D::build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis) {
- return Geometry3D::build_capsule_planes(p_radius, p_height, p_sides, p_lats, p_axis);
+Vector<Plane> Geometry3D::build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis) {
+ return ::Geometry3D::build_capsule_planes(p_radius, p_height, p_sides, p_lats, p_axis);
}
-Vector<Vector3> _Geometry3D::get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2) {
+Vector<Vector3> Geometry3D::get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2) {
Vector3 r1, r2;
- Geometry3D::get_closest_points_between_segments(p1, p2, q1, q2, r1, r2);
+ ::Geometry3D::get_closest_points_between_segments(p1, p2, q1, q2, r1, r2);
Vector<Vector3> r;
r.resize(2);
r.set(0, r1);
@@ -1076,38 +901,38 @@ Vector<Vector3> _Geometry3D::get_closest_points_between_segments(const Vector3 &
return r;
}
-Vector3 _Geometry3D::get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b) {
+Vector3 Geometry3D::get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b) {
Vector3 s[2] = { p_a, p_b };
- return Geometry3D::get_closest_point_to_segment(p_point, s);
+ return ::Geometry3D::get_closest_point_to_segment(p_point, s);
}
-Vector3 _Geometry3D::get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b) {
+Vector3 Geometry3D::get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b) {
Vector3 s[2] = { p_a, p_b };
- return Geometry3D::get_closest_point_to_segment_uncapped(p_point, s);
+ return ::Geometry3D::get_closest_point_to_segment_uncapped(p_point, s);
}
-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) {
+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)) {
+ if (::Geometry3D::ray_intersects_triangle(p_from, p_dir, p_v0, p_v1, p_v2, &res)) {
return res;
} else {
return Variant();
}
}
-Variant _Geometry3D::segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) {
+Variant Geometry3D::segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) {
Vector3 res;
- if (Geometry3D::segment_intersects_triangle(p_from, p_to, p_v0, p_v1, p_v2, &res)) {
+ if (::Geometry3D::segment_intersects_triangle(p_from, p_to, p_v0, p_v1, p_v2, &res)) {
return res;
} else {
return Variant();
}
}
-Vector<Vector3> _Geometry3D::segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius) {
+Vector<Vector3> Geometry3D::segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius) {
Vector<Vector3> r;
Vector3 res, norm;
- if (!Geometry3D::segment_intersects_sphere(p_from, p_to, p_sphere_pos, p_sphere_radius, &res, &norm)) {
+ if (!::Geometry3D::segment_intersects_sphere(p_from, p_to, p_sphere_pos, p_sphere_radius, &res, &norm)) {
return r;
}
@@ -1117,10 +942,10 @@ Vector<Vector3> _Geometry3D::segment_intersects_sphere(const Vector3 &p_from, co
return r;
}
-Vector<Vector3> _Geometry3D::segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius) {
+Vector<Vector3> Geometry3D::segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius) {
Vector<Vector3> r;
Vector3 res, norm;
- if (!Geometry3D::segment_intersects_cylinder(p_from, p_to, p_height, p_radius, &res, &norm)) {
+ if (!::Geometry3D::segment_intersects_cylinder(p_from, p_to, p_height, p_radius, &res, &norm)) {
return r;
}
@@ -1130,10 +955,10 @@ Vector<Vector3> _Geometry3D::segment_intersects_cylinder(const Vector3 &p_from,
return r;
}
-Vector<Vector3> _Geometry3D::segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector<Plane> &p_planes) {
+Vector<Vector3> Geometry3D::segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector<Plane> &p_planes) {
Vector<Vector3> r;
Vector3 res, norm;
- if (!Geometry3D::segment_intersects_convex(p_from, p_to, p_planes.ptr(), p_planes.size(), &res, &norm)) {
+ if (!::Geometry3D::segment_intersects_convex(p_from, p_to, p_planes.ptr(), p_planes.size(), &res, &norm)) {
return r;
}
@@ -1143,33 +968,33 @@ Vector<Vector3> _Geometry3D::segment_intersects_convex(const Vector3 &p_from, co
return r;
}
-Vector<Vector3> _Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane) {
- return Geometry3D::clip_polygon(p_points, p_plane);
+Vector<Vector3> Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane) {
+ return ::Geometry3D::clip_polygon(p_points, p_plane);
}
-void _Geometry3D::_bind_methods() {
- 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));
+void Geometry3D::_bind_methods() {
+ 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));
- ClassDB::bind_method(D_METHOD("get_closest_points_between_segments", "p1", "p2", "q1", "q2"), &_Geometry3D::get_closest_points_between_segments);
+ ClassDB::bind_method(D_METHOD("get_closest_points_between_segments", "p1", "p2", "q1", "q2"), &Geometry3D::get_closest_points_between_segments);
- ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "point", "s1", "s2"), &_Geometry3D::get_closest_point_to_segment);
+ ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "point", "s1", "s2"), &Geometry3D::get_closest_point_to_segment);
- 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_closest_point_to_segment_uncapped", "point", "s1", "s2"), &Geometry3D::get_closest_point_to_segment_uncapped);
- 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);
- ClassDB::bind_method(D_METHOD("segment_intersects_cylinder", "from", "to", "height", "radius"), &_Geometry3D::segment_intersects_cylinder);
- ClassDB::bind_method(D_METHOD("segment_intersects_convex", "from", "to", "planes"), &_Geometry3D::segment_intersects_convex);
+ 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);
+ ClassDB::bind_method(D_METHOD("segment_intersects_cylinder", "from", "to", "height", "radius"), &Geometry3D::segment_intersects_cylinder);
+ ClassDB::bind_method(D_METHOD("segment_intersects_convex", "from", "to", "planes"), &Geometry3D::segment_intersects_convex);
- ClassDB::bind_method(D_METHOD("clip_polygon", "points", "plane"), &_Geometry3D::clip_polygon);
+ ClassDB::bind_method(D_METHOD("clip_polygon", "points", "plane"), &Geometry3D::clip_polygon);
}
-////// _File //////
+////// File //////
-Error _File::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) {
+Error File::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) {
Error err = open(p_path, p_mode_flags);
if (err) {
return err;
@@ -1186,7 +1011,7 @@ Error _File::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const
return OK;
}
-Error _File::open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass) {
+Error File::open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass) {
Error err = open(p_path, p_mode_flags);
if (err) {
return err;
@@ -1204,7 +1029,7 @@ Error _File::open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, c
return OK;
}
-Error _File::open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode) {
+Error File::open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode) {
FileAccessCompressed *fac = memnew(FileAccessCompressed);
fac->configure("GCPF", (Compression::Mode)p_compress_mode);
@@ -1220,104 +1045,104 @@ Error _File::open_compressed(const String &p_path, ModeFlags p_mode_flags, Compr
return OK;
}
-Error _File::open(const String &p_path, ModeFlags p_mode_flags) {
+Error File::open(const String &p_path, ModeFlags p_mode_flags) {
close();
Error err;
f = FileAccess::open(p_path, p_mode_flags, &err);
if (f) {
- f->set_endian_swap(eswap);
+ f->set_big_endian(big_endian);
}
return err;
}
-void _File::flush() {
+void File::flush() {
ERR_FAIL_COND_MSG(!f, "File must be opened before flushing.");
f->flush();
}
-void _File::close() {
+void File::close() {
if (f) {
memdelete(f);
}
f = nullptr;
}
-bool _File::is_open() const {
+bool File::is_open() const {
return f != nullptr;
}
-String _File::get_path() const {
+String File::get_path() const {
ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use.");
return f->get_path();
}
-String _File::get_path_absolute() const {
+String File::get_path_absolute() const {
ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use.");
return f->get_path_absolute();
}
-void _File::seek(int64_t p_position) {
+void File::seek(int64_t p_position) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
ERR_FAIL_COND_MSG(p_position < 0, "Seek position must be a positive integer.");
f->seek(p_position);
}
-void _File::seek_end(int64_t p_position) {
+void File::seek_end(int64_t p_position) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
f->seek_end(p_position);
}
-uint64_t _File::get_position() const {
+uint64_t File::get_position() const {
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
return f->get_position();
}
-uint64_t _File::get_length() const {
+uint64_t File::get_length() const {
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
return f->get_length();
}
-bool _File::eof_reached() const {
+bool File::eof_reached() const {
ERR_FAIL_COND_V_MSG(!f, false, "File must be opened before use.");
return f->eof_reached();
}
-uint8_t _File::get_8() const {
+uint8_t File::get_8() const {
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
return f->get_8();
}
-uint16_t _File::get_16() const {
+uint16_t File::get_16() const {
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
return f->get_16();
}
-uint32_t _File::get_32() const {
+uint32_t File::get_32() const {
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
return f->get_32();
}
-uint64_t _File::get_64() const {
+uint64_t File::get_64() const {
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
return f->get_64();
}
-float _File::get_float() const {
+float File::get_float() const {
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
return f->get_float();
}
-double _File::get_double() const {
+double File::get_double() const {
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
return f->get_double();
}
-real_t _File::get_real() const {
+real_t File::get_real() const {
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
return f->get_real();
}
-Vector<uint8_t> _File::get_buffer(int64_t p_length) const {
+Vector<uint8_t> File::get_buffer(int64_t p_length) const {
Vector<uint8_t> data;
ERR_FAIL_COND_V_MSG(!f, data, "File must be opened before use.");
@@ -1339,7 +1164,7 @@ Vector<uint8_t> _File::get_buffer(int64_t p_length) const {
return data;
}
-String _File::get_as_text() const {
+String File::get_as_text() const {
ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use.");
String text;
@@ -1358,20 +1183,20 @@ String _File::get_as_text() const {
return text;
}
-String _File::get_md5(const String &p_path) const {
+String File::get_md5(const String &p_path) const {
return FileAccess::get_md5(p_path);
}
-String _File::get_sha256(const String &p_path) const {
+String File::get_sha256(const String &p_path) const {
return FileAccess::get_sha256(p_path);
}
-String _File::get_line() const {
+String File::get_line() const {
ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use.");
return f->get_line();
}
-Vector<String> _File::get_csv_line(const String &p_delim) const {
+Vector<String> File::get_csv_line(const String &p_delim) const {
ERR_FAIL_COND_V_MSG(!f, Vector<String>(), "File must be opened before use.");
return f->get_csv_line(p_delim);
}
@@ -1381,95 +1206,95 @@ Vector<String> _File::get_csv_line(const String &p_delim) const {
* These flags get reset to false (little endian) on each open
*/
-void _File::set_endian_swap(bool p_swap) {
- eswap = p_swap;
+void File::set_big_endian(bool p_big_endian) {
+ big_endian = p_big_endian;
if (f) {
- f->set_endian_swap(p_swap);
+ f->set_big_endian(p_big_endian);
}
}
-bool _File::get_endian_swap() {
- return eswap;
+bool File::is_big_endian() {
+ return big_endian;
}
-Error _File::get_error() const {
+Error File::get_error() const {
if (!f) {
return ERR_UNCONFIGURED;
}
return f->get_error();
}
-void _File::store_8(uint8_t p_dest) {
+void File::store_8(uint8_t p_dest) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
f->store_8(p_dest);
}
-void _File::store_16(uint16_t p_dest) {
+void File::store_16(uint16_t p_dest) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
f->store_16(p_dest);
}
-void _File::store_32(uint32_t p_dest) {
+void File::store_32(uint32_t p_dest) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
f->store_32(p_dest);
}
-void _File::store_64(uint64_t p_dest) {
+void File::store_64(uint64_t p_dest) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
f->store_64(p_dest);
}
-void _File::store_float(float p_dest) {
+void File::store_float(float p_dest) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
f->store_float(p_dest);
}
-void _File::store_double(double p_dest) {
+void File::store_double(double p_dest) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
f->store_double(p_dest);
}
-void _File::store_real(real_t p_real) {
+void File::store_real(real_t p_real) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
f->store_real(p_real);
}
-void _File::store_string(const String &p_string) {
+void File::store_string(const String &p_string) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
f->store_string(p_string);
}
-void _File::store_pascal_string(const String &p_string) {
+void File::store_pascal_string(const String &p_string) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
f->store_pascal_string(p_string);
}
-String _File::get_pascal_string() {
+String File::get_pascal_string() {
ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use.");
return f->get_pascal_string();
}
-void _File::store_line(const String &p_string) {
+void File::store_line(const String &p_string) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
f->store_line(p_string);
}
-void _File::store_csv_line(const Vector<String> &p_values, const String &p_delim) {
+void File::store_csv_line(const Vector<String> &p_values, const String &p_delim) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
f->store_csv_line(p_values, p_delim);
}
-void _File::store_buffer(const Vector<uint8_t> &p_buffer) {
+void File::store_buffer(const Vector<uint8_t> &p_buffer) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
uint64_t len = p_buffer.size();
@@ -1482,11 +1307,11 @@ void _File::store_buffer(const Vector<uint8_t> &p_buffer) {
f->store_buffer(&r[0], len);
}
-bool _File::file_exists(const String &p_name) const {
+bool File::file_exists(const String &p_name) const {
return FileAccess::exists(p_name);
}
-void _File::store_var(const Variant &p_var, bool p_full_objects) {
+void File::store_var(const Variant &p_var, bool p_full_objects) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
int len;
Error err = encode_variant(p_var, nullptr, len, p_full_objects);
@@ -1503,7 +1328,7 @@ void _File::store_var(const Variant &p_var, bool p_full_objects) {
store_buffer(buff);
}
-Variant _File::get_var(bool p_allow_objects) const {
+Variant File::get_var(bool p_allow_objects) const {
ERR_FAIL_COND_V_MSG(!f, Variant(), "File must be opened before use.");
uint32_t len = get_32();
Vector<uint8_t> buff = get_buffer(len);
@@ -1518,64 +1343,64 @@ Variant _File::get_var(bool p_allow_objects) const {
return v;
}
-uint64_t _File::get_modified_time(const String &p_file) const {
+uint64_t File::get_modified_time(const String &p_file) const {
return FileAccess::get_modified_time(p_file);
}
-void _File::_bind_methods() {
- ClassDB::bind_method(D_METHOD("open_encrypted", "path", "mode_flags", "key"), &_File::open_encrypted);
- ClassDB::bind_method(D_METHOD("open_encrypted_with_pass", "path", "mode_flags", "pass"), &_File::open_encrypted_pass);
- ClassDB::bind_method(D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &_File::open_compressed, DEFVAL(0));
-
- ClassDB::bind_method(D_METHOD("open", "path", "flags"), &_File::open);
- ClassDB::bind_method(D_METHOD("flush"), &_File::flush);
- ClassDB::bind_method(D_METHOD("close"), &_File::close);
- ClassDB::bind_method(D_METHOD("get_path"), &_File::get_path);
- ClassDB::bind_method(D_METHOD("get_path_absolute"), &_File::get_path_absolute);
- ClassDB::bind_method(D_METHOD("is_open"), &_File::is_open);
- ClassDB::bind_method(D_METHOD("seek", "position"), &_File::seek);
- ClassDB::bind_method(D_METHOD("seek_end", "position"), &_File::seek_end, DEFVAL(0));
- ClassDB::bind_method(D_METHOD("get_position"), &_File::get_position);
- ClassDB::bind_method(D_METHOD("get_length"), &_File::get_length);
- ClassDB::bind_method(D_METHOD("eof_reached"), &_File::eof_reached);
- ClassDB::bind_method(D_METHOD("get_8"), &_File::get_8);
- ClassDB::bind_method(D_METHOD("get_16"), &_File::get_16);
- ClassDB::bind_method(D_METHOD("get_32"), &_File::get_32);
- ClassDB::bind_method(D_METHOD("get_64"), &_File::get_64);
- ClassDB::bind_method(D_METHOD("get_float"), &_File::get_float);
- ClassDB::bind_method(D_METHOD("get_double"), &_File::get_double);
- ClassDB::bind_method(D_METHOD("get_real"), &_File::get_real);
- ClassDB::bind_method(D_METHOD("get_buffer", "length"), &_File::get_buffer);
- ClassDB::bind_method(D_METHOD("get_line"), &_File::get_line);
- ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &_File::get_csv_line, DEFVAL(","));
- ClassDB::bind_method(D_METHOD("get_as_text"), &_File::get_as_text);
- ClassDB::bind_method(D_METHOD("get_md5", "path"), &_File::get_md5);
- ClassDB::bind_method(D_METHOD("get_sha256", "path"), &_File::get_sha256);
- ClassDB::bind_method(D_METHOD("get_endian_swap"), &_File::get_endian_swap);
- ClassDB::bind_method(D_METHOD("set_endian_swap", "enable"), &_File::set_endian_swap);
- ClassDB::bind_method(D_METHOD("get_error"), &_File::get_error);
- ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &_File::get_var, DEFVAL(false));
-
- ClassDB::bind_method(D_METHOD("store_8", "value"), &_File::store_8);
- ClassDB::bind_method(D_METHOD("store_16", "value"), &_File::store_16);
- ClassDB::bind_method(D_METHOD("store_32", "value"), &_File::store_32);
- ClassDB::bind_method(D_METHOD("store_64", "value"), &_File::store_64);
- ClassDB::bind_method(D_METHOD("store_float", "value"), &_File::store_float);
- ClassDB::bind_method(D_METHOD("store_double", "value"), &_File::store_double);
- ClassDB::bind_method(D_METHOD("store_real", "value"), &_File::store_real);
- ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), &_File::store_buffer);
- ClassDB::bind_method(D_METHOD("store_line", "line"), &_File::store_line);
- ClassDB::bind_method(D_METHOD("store_csv_line", "values", "delim"), &_File::store_csv_line, DEFVAL(","));
- ClassDB::bind_method(D_METHOD("store_string", "string"), &_File::store_string);
- ClassDB::bind_method(D_METHOD("store_var", "value", "full_objects"), &_File::store_var, DEFVAL(false));
-
- ClassDB::bind_method(D_METHOD("store_pascal_string", "string"), &_File::store_pascal_string);
- ClassDB::bind_method(D_METHOD("get_pascal_string"), &_File::get_pascal_string);
-
- ClassDB::bind_method(D_METHOD("file_exists", "path"), &_File::file_exists);
- ClassDB::bind_method(D_METHOD("get_modified_time", "file"), &_File::get_modified_time);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "endian_swap"), "set_endian_swap", "get_endian_swap");
+void File::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("open_encrypted", "path", "mode_flags", "key"), &File::open_encrypted);
+ ClassDB::bind_method(D_METHOD("open_encrypted_with_pass", "path", "mode_flags", "pass"), &File::open_encrypted_pass);
+ ClassDB::bind_method(D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &File::open_compressed, DEFVAL(0));
+
+ ClassDB::bind_method(D_METHOD("open", "path", "flags"), &File::open);
+ ClassDB::bind_method(D_METHOD("flush"), &File::flush);
+ ClassDB::bind_method(D_METHOD("close"), &File::close);
+ ClassDB::bind_method(D_METHOD("get_path"), &File::get_path);
+ ClassDB::bind_method(D_METHOD("get_path_absolute"), &File::get_path_absolute);
+ ClassDB::bind_method(D_METHOD("is_open"), &File::is_open);
+ ClassDB::bind_method(D_METHOD("seek", "position"), &File::seek);
+ ClassDB::bind_method(D_METHOD("seek_end", "position"), &File::seek_end, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("get_position"), &File::get_position);
+ ClassDB::bind_method(D_METHOD("get_length"), &File::get_length);
+ ClassDB::bind_method(D_METHOD("eof_reached"), &File::eof_reached);
+ ClassDB::bind_method(D_METHOD("get_8"), &File::get_8);
+ ClassDB::bind_method(D_METHOD("get_16"), &File::get_16);
+ ClassDB::bind_method(D_METHOD("get_32"), &File::get_32);
+ ClassDB::bind_method(D_METHOD("get_64"), &File::get_64);
+ ClassDB::bind_method(D_METHOD("get_float"), &File::get_float);
+ ClassDB::bind_method(D_METHOD("get_double"), &File::get_double);
+ ClassDB::bind_method(D_METHOD("get_real"), &File::get_real);
+ ClassDB::bind_method(D_METHOD("get_buffer", "length"), &File::get_buffer);
+ ClassDB::bind_method(D_METHOD("get_line"), &File::get_line);
+ ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &File::get_csv_line, DEFVAL(","));
+ ClassDB::bind_method(D_METHOD("get_as_text"), &File::get_as_text);
+ ClassDB::bind_method(D_METHOD("get_md5", "path"), &File::get_md5);
+ ClassDB::bind_method(D_METHOD("get_sha256", "path"), &File::get_sha256);
+ ClassDB::bind_method(D_METHOD("is_big_endian"), &File::is_big_endian);
+ ClassDB::bind_method(D_METHOD("set_big_endian", "big_endian"), &File::set_big_endian);
+ ClassDB::bind_method(D_METHOD("get_error"), &File::get_error);
+ ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &File::get_var, DEFVAL(false));
+
+ ClassDB::bind_method(D_METHOD("store_8", "value"), &File::store_8);
+ ClassDB::bind_method(D_METHOD("store_16", "value"), &File::store_16);
+ ClassDB::bind_method(D_METHOD("store_32", "value"), &File::store_32);
+ ClassDB::bind_method(D_METHOD("store_64", "value"), &File::store_64);
+ ClassDB::bind_method(D_METHOD("store_float", "value"), &File::store_float);
+ ClassDB::bind_method(D_METHOD("store_double", "value"), &File::store_double);
+ ClassDB::bind_method(D_METHOD("store_real", "value"), &File::store_real);
+ ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), &File::store_buffer);
+ ClassDB::bind_method(D_METHOD("store_line", "line"), &File::store_line);
+ ClassDB::bind_method(D_METHOD("store_csv_line", "values", "delim"), &File::store_csv_line, DEFVAL(","));
+ ClassDB::bind_method(D_METHOD("store_string", "string"), &File::store_string);
+ ClassDB::bind_method(D_METHOD("store_var", "value", "full_objects"), &File::store_var, DEFVAL(false));
+
+ ClassDB::bind_method(D_METHOD("store_pascal_string", "string"), &File::store_pascal_string);
+ ClassDB::bind_method(D_METHOD("get_pascal_string"), &File::get_pascal_string);
+
+ ClassDB::bind_method(D_METHOD("file_exists", "path"), &File::file_exists);
+ ClassDB::bind_method(D_METHOD("get_modified_time", "file"), &File::get_modified_time);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian");
BIND_ENUM_CONSTANT(READ);
BIND_ENUM_CONSTANT(WRITE);
@@ -1588,15 +1413,15 @@ void _File::_bind_methods() {
BIND_ENUM_CONSTANT(COMPRESSION_GZIP);
}
-_File::~_File() {
+File::~File() {
if (f) {
memdelete(f);
}
}
-////// _Directory //////
+////// Directory //////
-Error _Directory::open(const String &p_path) {
+Error Directory::open(const String &p_path) {
Error err;
DirAccess *alt = DirAccess::open(p_path, &err);
@@ -1612,11 +1437,11 @@ Error _Directory::open(const String &p_path) {
return OK;
}
-bool _Directory::is_open() const {
+bool Directory::is_open() const {
return d && dir_open;
}
-Error _Directory::list_dir_begin(bool p_show_navigational, bool p_show_hidden) {
+Error Directory::list_dir_begin(bool p_show_navigational, bool p_show_hidden) {
ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use.");
_list_skip_navigational = !p_show_navigational;
@@ -1625,7 +1450,7 @@ Error _Directory::list_dir_begin(bool p_show_navigational, bool p_show_hidden) {
return d->list_dir_begin();
}
-String _Directory::get_next() {
+String Directory::get_next() {
ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use.");
String next = d->get_next();
@@ -1635,32 +1460,32 @@ String _Directory::get_next() {
return next;
}
-bool _Directory::current_is_dir() const {
+bool Directory::current_is_dir() const {
ERR_FAIL_COND_V_MSG(!is_open(), false, "Directory must be opened before use.");
return d->current_is_dir();
}
-void _Directory::list_dir_end() {
+void Directory::list_dir_end() {
ERR_FAIL_COND_MSG(!is_open(), "Directory must be opened before use.");
d->list_dir_end();
}
-int _Directory::get_drive_count() {
+int Directory::get_drive_count() {
ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use.");
return d->get_drive_count();
}
-String _Directory::get_drive(int p_drive) {
+String Directory::get_drive(int p_drive) {
ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use.");
return d->get_drive(p_drive);
}
-int _Directory::get_current_drive() {
+int Directory::get_current_drive() {
ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use.");
return d->get_current_drive();
}
-Error _Directory::change_dir(String p_dir) {
+Error Directory::change_dir(String p_dir) {
ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly.");
Error err = d->change_dir(p_dir);
@@ -1672,14 +1497,14 @@ Error _Directory::change_dir(String p_dir) {
return OK;
}
-String _Directory::get_current_dir() {
+String Directory::get_current_dir() {
ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use.");
return d->get_current_dir();
}
-Error _Directory::make_dir(String p_dir) {
+Error Directory::make_dir(String p_dir) {
ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly.");
- if (!p_dir.is_rel_path()) {
+ if (!p_dir.is_relative_path()) {
DirAccess *d = DirAccess::create_for_path(p_dir);
Error err = d->make_dir(p_dir);
memdelete(d);
@@ -1688,9 +1513,9 @@ Error _Directory::make_dir(String p_dir) {
return d->make_dir(p_dir);
}
-Error _Directory::make_dir_recursive(String p_dir) {
+Error Directory::make_dir_recursive(String p_dir) {
ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly.");
- if (!p_dir.is_rel_path()) {
+ if (!p_dir.is_relative_path()) {
DirAccess *d = DirAccess::create_for_path(p_dir);
Error err = d->make_dir_recursive(p_dir);
memdelete(d);
@@ -1699,18 +1524,18 @@ Error _Directory::make_dir_recursive(String p_dir) {
return d->make_dir_recursive(p_dir);
}
-bool _Directory::file_exists(String p_file) {
+bool Directory::file_exists(String p_file) {
ERR_FAIL_COND_V_MSG(!d, false, "Directory is not configured properly.");
- if (!p_file.is_rel_path()) {
+ if (!p_file.is_relative_path()) {
return FileAccess::exists(p_file);
}
return d->file_exists(p_file);
}
-bool _Directory::dir_exists(String p_dir) {
+bool Directory::dir_exists(String p_dir) {
ERR_FAIL_COND_V_MSG(!d, false, "Directory is not configured properly.");
- if (!p_dir.is_rel_path()) {
+ if (!p_dir.is_relative_path()) {
DirAccess *d = DirAccess::create_for_path(p_dir);
bool exists = d->dir_exists(p_dir);
memdelete(d);
@@ -1720,33 +1545,35 @@ bool _Directory::dir_exists(String p_dir) {
return d->dir_exists(p_dir);
}
-uint64_t _Directory::get_space_left() {
+uint64_t Directory::get_space_left() {
ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use.");
return d->get_space_left() / 1024 * 1024; // Truncate to closest MiB.
}
-Error _Directory::copy(String p_from, String p_to) {
+Error Directory::copy(String p_from, String p_to) {
ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use.");
return d->copy(p_from, p_to);
}
-Error _Directory::rename(String p_from, String p_to) {
+Error Directory::rename(String p_from, String p_to) {
ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use.");
- if (!p_from.is_rel_path()) {
+ ERR_FAIL_COND_V_MSG(p_from.is_empty() || p_from == "." || p_from == "..", ERR_INVALID_PARAMETER, "Invalid path to rename.");
+
+ if (!p_from.is_relative_path()) {
DirAccess *d = DirAccess::create_for_path(p_from);
- ERR_FAIL_COND_V_MSG(!d->file_exists(p_from), ERR_DOES_NOT_EXIST, "File does not exist.");
+ ERR_FAIL_COND_V_MSG(!d->file_exists(p_from) && !d->dir_exists(p_from), ERR_DOES_NOT_EXIST, "File or directory does not exist.");
Error err = d->rename(p_from, p_to);
memdelete(d);
return err;
}
- ERR_FAIL_COND_V_MSG(!d->file_exists(p_from), ERR_DOES_NOT_EXIST, "File does not exist.");
+ ERR_FAIL_COND_V_MSG(!d->file_exists(p_from) && !d->dir_exists(p_from), ERR_DOES_NOT_EXIST, "File or directory does not exist.");
return d->rename(p_from, p_to);
}
-Error _Directory::remove(String p_name) {
+Error Directory::remove(String p_name) {
ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use.");
- if (!p_name.is_rel_path()) {
+ if (!p_name.is_relative_path()) {
DirAccess *d = DirAccess::create_for_path(p_name);
Error err = d->remove(p_name);
memdelete(d);
@@ -1756,47 +1583,47 @@ Error _Directory::remove(String p_name) {
return d->remove(p_name);
}
-void _Directory::_bind_methods() {
- ClassDB::bind_method(D_METHOD("open", "path"), &_Directory::open);
- ClassDB::bind_method(D_METHOD("list_dir_begin", "show_navigational", "show_hidden"), &_Directory::list_dir_begin, DEFVAL(false), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("get_next"), &_Directory::get_next);
- ClassDB::bind_method(D_METHOD("current_is_dir"), &_Directory::current_is_dir);
- ClassDB::bind_method(D_METHOD("list_dir_end"), &_Directory::list_dir_end);
- ClassDB::bind_method(D_METHOD("get_drive_count"), &_Directory::get_drive_count);
- ClassDB::bind_method(D_METHOD("get_drive", "idx"), &_Directory::get_drive);
- ClassDB::bind_method(D_METHOD("get_current_drive"), &_Directory::get_current_drive);
- ClassDB::bind_method(D_METHOD("change_dir", "todir"), &_Directory::change_dir);
- ClassDB::bind_method(D_METHOD("get_current_dir"), &_Directory::get_current_dir);
- ClassDB::bind_method(D_METHOD("make_dir", "path"), &_Directory::make_dir);
- ClassDB::bind_method(D_METHOD("make_dir_recursive", "path"), &_Directory::make_dir_recursive);
- ClassDB::bind_method(D_METHOD("file_exists", "path"), &_Directory::file_exists);
- ClassDB::bind_method(D_METHOD("dir_exists", "path"), &_Directory::dir_exists);
- //ClassDB::bind_method(D_METHOD("get_modified_time","file"),&_Directory::get_modified_time);
- ClassDB::bind_method(D_METHOD("get_space_left"), &_Directory::get_space_left);
- ClassDB::bind_method(D_METHOD("copy", "from", "to"), &_Directory::copy);
- ClassDB::bind_method(D_METHOD("rename", "from", "to"), &_Directory::rename);
- ClassDB::bind_method(D_METHOD("remove", "path"), &_Directory::remove);
-}
-
-_Directory::_Directory() {
+void Directory::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("open", "path"), &Directory::open);
+ ClassDB::bind_method(D_METHOD("list_dir_begin", "show_navigational", "show_hidden"), &Directory::list_dir_begin, DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_next"), &Directory::get_next);
+ ClassDB::bind_method(D_METHOD("current_is_dir"), &Directory::current_is_dir);
+ ClassDB::bind_method(D_METHOD("list_dir_end"), &Directory::list_dir_end);
+ ClassDB::bind_method(D_METHOD("get_drive_count"), &Directory::get_drive_count);
+ ClassDB::bind_method(D_METHOD("get_drive", "idx"), &Directory::get_drive);
+ ClassDB::bind_method(D_METHOD("get_current_drive"), &Directory::get_current_drive);
+ ClassDB::bind_method(D_METHOD("change_dir", "todir"), &Directory::change_dir);
+ ClassDB::bind_method(D_METHOD("get_current_dir"), &Directory::get_current_dir);
+ ClassDB::bind_method(D_METHOD("make_dir", "path"), &Directory::make_dir);
+ ClassDB::bind_method(D_METHOD("make_dir_recursive", "path"), &Directory::make_dir_recursive);
+ ClassDB::bind_method(D_METHOD("file_exists", "path"), &Directory::file_exists);
+ ClassDB::bind_method(D_METHOD("dir_exists", "path"), &Directory::dir_exists);
+ //ClassDB::bind_method(D_METHOD("get_modified_time","file"),&Directory::get_modified_time);
+ ClassDB::bind_method(D_METHOD("get_space_left"), &Directory::get_space_left);
+ ClassDB::bind_method(D_METHOD("copy", "from", "to"), &Directory::copy);
+ ClassDB::bind_method(D_METHOD("rename", "from", "to"), &Directory::rename);
+ ClassDB::bind_method(D_METHOD("remove", "path"), &Directory::remove);
+}
+
+Directory::Directory() {
d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
}
-_Directory::~_Directory() {
+Directory::~Directory() {
if (d) {
memdelete(d);
}
}
-////// _Marshalls //////
+////// Marshalls //////
-_Marshalls *_Marshalls::singleton = nullptr;
+Marshalls *Marshalls::singleton = nullptr;
-_Marshalls *_Marshalls::get_singleton() {
+Marshalls *Marshalls::get_singleton() {
return singleton;
}
-String _Marshalls::variant_to_base64(const Variant &p_var, bool p_full_objects) {
+String Marshalls::variant_to_base64(const Variant &p_var, bool p_full_objects) {
int len;
Error err = encode_variant(p_var, nullptr, len, p_full_objects);
ERR_FAIL_COND_V_MSG(err != OK, "", "Error when trying to encode Variant.");
@@ -1814,7 +1641,7 @@ String _Marshalls::variant_to_base64(const Variant &p_var, bool p_full_objects)
return ret;
}
-Variant _Marshalls::base64_to_variant(const String &p_str, bool p_allow_objects) {
+Variant Marshalls::base64_to_variant(const String &p_str, bool p_allow_objects) {
int strlen = p_str.length();
CharString cstr = p_str.ascii();
@@ -1832,13 +1659,13 @@ Variant _Marshalls::base64_to_variant(const String &p_str, bool p_allow_objects)
return v;
}
-String _Marshalls::raw_to_base64(const Vector<uint8_t> &p_arr) {
+String Marshalls::raw_to_base64(const Vector<uint8_t> &p_arr) {
String ret = CryptoCore::b64_encode_str(p_arr.ptr(), p_arr.size());
ERR_FAIL_COND_V(ret == "", ret);
return ret;
}
-Vector<uint8_t> _Marshalls::base64_to_raw(const String &p_str) {
+Vector<uint8_t> Marshalls::base64_to_raw(const String &p_str) {
int strlen = p_str.length();
CharString cstr = p_str.ascii();
@@ -1855,14 +1682,14 @@ Vector<uint8_t> _Marshalls::base64_to_raw(const String &p_str) {
return buf;
}
-String _Marshalls::utf8_to_base64(const String &p_str) {
+String Marshalls::utf8_to_base64(const String &p_str) {
CharString cstr = p_str.utf8();
String ret = CryptoCore::b64_encode_str((unsigned char *)cstr.get_data(), cstr.length());
ERR_FAIL_COND_V(ret == "", ret);
return ret;
}
-String _Marshalls::base64_to_utf8(const String &p_str) {
+String Marshalls::base64_to_utf8(const String &p_str) {
int strlen = p_str.length();
CharString cstr = p_str.ascii();
@@ -1879,69 +1706,99 @@ String _Marshalls::base64_to_utf8(const String &p_str) {
return ret;
}
-void _Marshalls::_bind_methods() {
- ClassDB::bind_method(D_METHOD("variant_to_base64", "variant", "full_objects"), &_Marshalls::variant_to_base64, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("base64_to_variant", "base64_str", "allow_objects"), &_Marshalls::base64_to_variant, DEFVAL(false));
+void Marshalls::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("variant_to_base64", "variant", "full_objects"), &Marshalls::variant_to_base64, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("base64_to_variant", "base64_str", "allow_objects"), &Marshalls::base64_to_variant, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("raw_to_base64", "array"), &_Marshalls::raw_to_base64);
- ClassDB::bind_method(D_METHOD("base64_to_raw", "base64_str"), &_Marshalls::base64_to_raw);
+ ClassDB::bind_method(D_METHOD("raw_to_base64", "array"), &Marshalls::raw_to_base64);
+ ClassDB::bind_method(D_METHOD("base64_to_raw", "base64_str"), &Marshalls::base64_to_raw);
- ClassDB::bind_method(D_METHOD("utf8_to_base64", "utf8_str"), &_Marshalls::utf8_to_base64);
- ClassDB::bind_method(D_METHOD("base64_to_utf8", "base64_str"), &_Marshalls::base64_to_utf8);
+ ClassDB::bind_method(D_METHOD("utf8_to_base64", "utf8_str"), &Marshalls::utf8_to_base64);
+ ClassDB::bind_method(D_METHOD("base64_to_utf8", "base64_str"), &Marshalls::base64_to_utf8);
}
-////// _Semaphore //////
+////// Semaphore //////
-void _Semaphore::wait() {
+void Semaphore::wait() {
semaphore.wait();
}
-Error _Semaphore::try_wait() {
+Error Semaphore::try_wait() {
return semaphore.try_wait() ? OK : ERR_BUSY;
}
-void _Semaphore::post() {
+void Semaphore::post() {
semaphore.post();
}
-void _Semaphore::_bind_methods() {
- ClassDB::bind_method(D_METHOD("wait"), &_Semaphore::wait);
- ClassDB::bind_method(D_METHOD("try_wait"), &_Semaphore::try_wait);
- ClassDB::bind_method(D_METHOD("post"), &_Semaphore::post);
+void Semaphore::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("wait"), &Semaphore::wait);
+ ClassDB::bind_method(D_METHOD("try_wait"), &Semaphore::try_wait);
+ ClassDB::bind_method(D_METHOD("post"), &Semaphore::post);
}
-////// _Mutex //////
+////// Mutex //////
-void _Mutex::lock() {
+void Mutex::lock() {
mutex.lock();
}
-Error _Mutex::try_lock() {
+Error Mutex::try_lock() {
return mutex.try_lock();
}
-void _Mutex::unlock() {
+void Mutex::unlock() {
mutex.unlock();
}
-void _Mutex::_bind_methods() {
- ClassDB::bind_method(D_METHOD("lock"), &_Mutex::lock);
- ClassDB::bind_method(D_METHOD("try_lock"), &_Mutex::try_lock);
- ClassDB::bind_method(D_METHOD("unlock"), &_Mutex::unlock);
+void Mutex::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("lock"), &Mutex::lock);
+ ClassDB::bind_method(D_METHOD("try_lock"), &Mutex::try_lock);
+ ClassDB::bind_method(D_METHOD("unlock"), &Mutex::unlock);
}
-////// _Thread //////
+////// Thread //////
-void _Thread::_start_func(void *ud) {
- Ref<_Thread> *tud = (Ref<_Thread> *)ud;
- Ref<_Thread> t = *tud;
+void Thread::_start_func(void *ud) {
+ Ref<Thread> *tud = (Ref<Thread> *)ud;
+ Ref<Thread> t = *tud;
memdelete(tud);
Callable::CallError ce;
const Variant *arg[1] = { &t->userdata };
+ int argc = 0;
+ if (arg[0]->get_type() != Variant::NIL) {
+ // Just pass to the target function whatever came as user data
+ argc = 1;
+ } else {
+ // There are two cases of null user data:
+ // a) The target function has zero parameters and the caller is just honoring that.
+ // b) The target function has at least one parameter with no default and the caller is
+ // leveraging the fact that user data defaults to null in Thread.start().
+ // We care about the case of more than one parameter because, even if a thread
+ // function can have one at most, out mindset here is to do our best with the
+ // only/first one and let the call handle any other error conditions, like too
+ // much arguments.
+ // We must check if we are in case b).
+ int target_param_count = 0;
+ int target_default_arg_count = 0;
+ Ref<Script> script = t->target_instance->get_script();
+ if (script.is_valid()) {
+ MethodInfo mi = script->get_method_info(t->target_method);
+ target_param_count = mi.arguments.size();
+ target_default_arg_count = mi.default_arguments.size();
+ } else {
+ MethodBind *method = ClassDB::get_method(t->target_instance->get_class_name(), t->target_method);
+ target_param_count = method->get_argument_count();
+ target_default_arg_count = method->get_default_argument_count();
+ }
+ if (target_param_count >= 1 && target_default_arg_count < target_param_count) {
+ argc = 1;
+ }
+ }
- Thread::set_name(t->target_method);
+ ::Thread::set_name(t->target_method);
- t->ret = t->target_instance->call(t->target_method, arg, 1, ce);
+ t->ret = t->target_instance->call(t->target_method, arg, argc, ce);
if (ce.error != Callable::CallError::CALL_OK) {
String reason;
switch (ce.error) {
@@ -1965,7 +1822,7 @@ void _Thread::_start_func(void *ud) {
}
}
-Error _Thread::start(Object *p_instance, const StringName &p_method, const Variant &p_userdata, Priority p_priority) {
+Error Thread::start(Object *p_instance, const StringName &p_method, const Variant &p_userdata, Priority p_priority) {
ERR_FAIL_COND_V_MSG(active.is_set(), ERR_ALREADY_IN_USE, "Thread already started.");
ERR_FAIL_COND_V(!p_instance, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(p_method == StringName(), ERR_INVALID_PARAMETER);
@@ -1977,24 +1834,24 @@ Error _Thread::start(Object *p_instance, const StringName &p_method, const Varia
userdata = p_userdata;
active.set();
- Ref<_Thread> *ud = memnew(Ref<_Thread>(this));
+ Ref<Thread> *ud = memnew(Ref<Thread>(this));
- Thread::Settings s;
- s.priority = (Thread::Priority)p_priority;
+ ::Thread::Settings s;
+ s.priority = (::Thread::Priority)p_priority;
thread.start(_start_func, ud, s);
return OK;
}
-String _Thread::get_id() const {
+String Thread::get_id() const {
return itos(thread.get_id());
}
-bool _Thread::is_active() const {
+bool Thread::is_active() const {
return active.is_set();
}
-Variant _Thread::wait_to_finish() {
+Variant Thread::wait_to_finish() {
ERR_FAIL_COND_V_MSG(!active.is_set(), Variant(), "Thread must be active to wait for its completion.");
thread.wait_to_finish();
Variant r = ret;
@@ -2006,70 +1863,72 @@ Variant _Thread::wait_to_finish() {
return r;
}
-void _Thread::_bind_methods() {
- ClassDB::bind_method(D_METHOD("start", "instance", "method", "userdata", "priority"), &_Thread::start, DEFVAL(Variant()), DEFVAL(PRIORITY_NORMAL));
- ClassDB::bind_method(D_METHOD("get_id"), &_Thread::get_id);
- ClassDB::bind_method(D_METHOD("is_active"), &_Thread::is_active);
- ClassDB::bind_method(D_METHOD("wait_to_finish"), &_Thread::wait_to_finish);
+void Thread::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("start", "instance", "method", "userdata", "priority"), &Thread::start, DEFVAL(Variant()), DEFVAL(PRIORITY_NORMAL));
+ ClassDB::bind_method(D_METHOD("get_id"), &Thread::get_id);
+ ClassDB::bind_method(D_METHOD("is_active"), &Thread::is_active);
+ ClassDB::bind_method(D_METHOD("wait_to_finish"), &Thread::wait_to_finish);
BIND_ENUM_CONSTANT(PRIORITY_LOW);
BIND_ENUM_CONSTANT(PRIORITY_NORMAL);
BIND_ENUM_CONSTANT(PRIORITY_HIGH);
}
-////// _ClassDB //////
+namespace special {
-PackedStringArray _ClassDB::get_class_list() const {
+////// ClassDB //////
+
+PackedStringArray ClassDB::get_class_list() const {
List<StringName> classes;
- ClassDB::get_class_list(&classes);
+ ::ClassDB::get_class_list(&classes);
PackedStringArray ret;
ret.resize(classes.size());
int idx = 0;
- for (List<StringName>::Element *E = classes.front(); E; E = E->next()) {
- ret.set(idx++, E->get());
+ for (const StringName &E : classes) {
+ ret.set(idx++, E);
}
return ret;
}
-PackedStringArray _ClassDB::get_inheriters_from_class(const StringName &p_class) const {
+PackedStringArray ClassDB::get_inheriters_from_class(const StringName &p_class) const {
List<StringName> classes;
- ClassDB::get_inheriters_from_class(p_class, &classes);
+ ::ClassDB::get_inheriters_from_class(p_class, &classes);
PackedStringArray ret;
ret.resize(classes.size());
int idx = 0;
- for (List<StringName>::Element *E = classes.front(); E; E = E->next()) {
- ret.set(idx++, E->get());
+ for (const StringName &E : classes) {
+ ret.set(idx++, E);
}
return ret;
}
-StringName _ClassDB::get_parent_class(const StringName &p_class) const {
- return ClassDB::get_parent_class(p_class);
+StringName ClassDB::get_parent_class(const StringName &p_class) const {
+ return ::ClassDB::get_parent_class(p_class);
}
-bool _ClassDB::class_exists(const StringName &p_class) const {
- return ClassDB::class_exists(p_class);
+bool ClassDB::class_exists(const StringName &p_class) const {
+ return ::ClassDB::class_exists(p_class);
}
-bool _ClassDB::is_parent_class(const StringName &p_class, const StringName &p_inherits) const {
- return ClassDB::is_parent_class(p_class, p_inherits);
+bool ClassDB::is_parent_class(const StringName &p_class, const StringName &p_inherits) const {
+ return ::ClassDB::is_parent_class(p_class, p_inherits);
}
-bool _ClassDB::can_instance(const StringName &p_class) const {
- return ClassDB::can_instance(p_class);
+bool ClassDB::can_instantiate(const StringName &p_class) const {
+ return ::ClassDB::can_instantiate(p_class);
}
-Variant _ClassDB::instance(const StringName &p_class) const {
- Object *obj = ClassDB::instance(p_class);
+Variant ClassDB::instantiate(const StringName &p_class) const {
+ Object *obj = ::ClassDB::instantiate(p_class);
if (!obj) {
return Variant();
}
- Reference *r = Object::cast_to<Reference>(obj);
+ RefCounted *r = Object::cast_to<RefCounted>(obj);
if (r) {
return REF(r);
} else {
@@ -2077,52 +1936,52 @@ Variant _ClassDB::instance(const StringName &p_class) const {
}
}
-bool _ClassDB::has_signal(StringName p_class, StringName p_signal) const {
- return ClassDB::has_signal(p_class, p_signal);
+bool ClassDB::has_signal(StringName p_class, StringName p_signal) const {
+ return ::ClassDB::has_signal(p_class, p_signal);
}
-Dictionary _ClassDB::get_signal(StringName p_class, StringName p_signal) const {
+Dictionary ClassDB::get_signal(StringName p_class, StringName p_signal) const {
MethodInfo signal;
- if (ClassDB::get_signal(p_class, p_signal, &signal)) {
+ if (::ClassDB::get_signal(p_class, p_signal, &signal)) {
return signal.operator Dictionary();
} else {
return Dictionary();
}
}
-Array _ClassDB::get_signal_list(StringName p_class, bool p_no_inheritance) const {
+Array ClassDB::get_signal_list(StringName p_class, bool p_no_inheritance) const {
List<MethodInfo> signals;
- ClassDB::get_signal_list(p_class, &signals, p_no_inheritance);
+ ::ClassDB::get_signal_list(p_class, &signals, p_no_inheritance);
Array ret;
- for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
- ret.push_back(E->get().operator Dictionary());
+ for (const MethodInfo &E : signals) {
+ ret.push_back(E.operator Dictionary());
}
return ret;
}
-Array _ClassDB::get_property_list(StringName p_class, bool p_no_inheritance) const {
+Array ClassDB::get_property_list(StringName p_class, bool p_no_inheritance) const {
List<PropertyInfo> plist;
- ClassDB::get_property_list(p_class, &plist, p_no_inheritance);
+ ::ClassDB::get_property_list(p_class, &plist, p_no_inheritance);
Array ret;
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- ret.push_back(E->get().operator Dictionary());
+ for (const PropertyInfo &E : plist) {
+ ret.push_back(E.operator Dictionary());
}
return ret;
}
-Variant _ClassDB::get_property(Object *p_object, const StringName &p_property) const {
+Variant ClassDB::get_property(Object *p_object, const StringName &p_property) const {
Variant ret;
- ClassDB::get_property(p_object, p_property, ret);
+ ::ClassDB::get_property(p_object, p_property, ret);
return ret;
}
-Error _ClassDB::set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const {
+Error ClassDB::set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const {
Variant ret;
bool valid;
- if (!ClassDB::set_property(p_object, p_property, p_value, &valid)) {
+ if (!::ClassDB::set_property(p_object, p_property, p_value, &valid)) {
return ERR_UNAVAILABLE;
} else if (!valid) {
return ERR_INVALID_DATA;
@@ -2130,21 +1989,21 @@ Error _ClassDB::set_property(Object *p_object, const StringName &p_property, con
return OK;
}
-bool _ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inheritance) const {
- return ClassDB::has_method(p_class, p_method, p_no_inheritance);
+bool ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inheritance) const {
+ return ::ClassDB::has_method(p_class, p_method, p_no_inheritance);
}
-Array _ClassDB::get_method_list(StringName p_class, bool p_no_inheritance) const {
+Array ClassDB::get_method_list(StringName p_class, bool p_no_inheritance) const {
List<MethodInfo> methods;
- ClassDB::get_method_list(p_class, &methods, p_no_inheritance);
+ ::ClassDB::get_method_list(p_class, &methods, p_no_inheritance);
Array ret;
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ for (const MethodInfo &E : methods) {
#ifdef DEBUG_METHODS_ENABLED
- ret.push_back(E->get().operator Dictionary());
+ ret.push_back(E.operator Dictionary());
#else
Dictionary dict;
- dict["name"] = E->get().name;
+ dict["name"] = E.name;
ret.push_back(dict);
#endif
}
@@ -2152,375 +2011,369 @@ Array _ClassDB::get_method_list(StringName p_class, bool p_no_inheritance) const
return ret;
}
-PackedStringArray _ClassDB::get_integer_constant_list(const StringName &p_class, bool p_no_inheritance) const {
+PackedStringArray ClassDB::get_integer_constant_list(const StringName &p_class, bool p_no_inheritance) const {
List<String> constants;
- ClassDB::get_integer_constant_list(p_class, &constants, p_no_inheritance);
+ ::ClassDB::get_integer_constant_list(p_class, &constants, p_no_inheritance);
PackedStringArray ret;
ret.resize(constants.size());
int idx = 0;
- for (List<String>::Element *E = constants.front(); E; E = E->next()) {
- ret.set(idx++, E->get());
+ for (const String &E : constants) {
+ ret.set(idx++, E);
}
return ret;
}
-bool _ClassDB::has_integer_constant(const StringName &p_class, const StringName &p_name) const {
+bool ClassDB::has_integer_constant(const StringName &p_class, const StringName &p_name) const {
bool success;
- ClassDB::get_integer_constant(p_class, p_name, &success);
+ ::ClassDB::get_integer_constant(p_class, p_name, &success);
return success;
}
-int _ClassDB::get_integer_constant(const StringName &p_class, const StringName &p_name) const {
+int ClassDB::get_integer_constant(const StringName &p_class, const StringName &p_name) const {
bool found;
- int c = ClassDB::get_integer_constant(p_class, p_name, &found);
+ int c = ::ClassDB::get_integer_constant(p_class, p_name, &found);
ERR_FAIL_COND_V(!found, 0);
return c;
}
-StringName _ClassDB::get_category(const StringName &p_node) const {
- return ClassDB::get_category(p_node);
+StringName ClassDB::get_category(const StringName &p_node) const {
+ return ::ClassDB::get_category(p_node);
}
-bool _ClassDB::is_class_enabled(StringName p_class) const {
- return ClassDB::is_class_enabled(p_class);
+bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const {
+ return ::ClassDB::has_enum(p_class, p_name, p_no_inheritance);
}
-void _ClassDB::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_class_list"), &_ClassDB::get_class_list);
- ClassDB::bind_method(D_METHOD("get_inheriters_from_class", "class"), &_ClassDB::get_inheriters_from_class);
- ClassDB::bind_method(D_METHOD("get_parent_class", "class"), &_ClassDB::get_parent_class);
- ClassDB::bind_method(D_METHOD("class_exists", "class"), &_ClassDB::class_exists);
- ClassDB::bind_method(D_METHOD("is_parent_class", "class", "inherits"), &_ClassDB::is_parent_class);
- ClassDB::bind_method(D_METHOD("can_instance", "class"), &_ClassDB::can_instance);
- ClassDB::bind_method(D_METHOD("instance", "class"), &_ClassDB::instance);
+PackedStringArray ClassDB::get_enum_list(const StringName &p_class, bool p_no_inheritance) const {
+ List<StringName> enums;
+ ::ClassDB::get_enum_list(p_class, &enums, p_no_inheritance);
- ClassDB::bind_method(D_METHOD("class_has_signal", "class", "signal"), &_ClassDB::has_signal);
- ClassDB::bind_method(D_METHOD("class_get_signal", "class", "signal"), &_ClassDB::get_signal);
- ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &_ClassDB::get_signal_list, DEFVAL(false));
+ PackedStringArray ret;
+ ret.resize(enums.size());
+ int idx = 0;
+ for (const StringName &E : enums) {
+ ret.set(idx++, E);
+ }
- ClassDB::bind_method(D_METHOD("class_get_property_list", "class", "no_inheritance"), &_ClassDB::get_property_list, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &_ClassDB::get_property);
- ClassDB::bind_method(D_METHOD("class_set_property", "object", "property", "value"), &_ClassDB::set_property);
+ return ret;
+}
- ClassDB::bind_method(D_METHOD("class_has_method", "class", "method", "no_inheritance"), &_ClassDB::has_method, DEFVAL(false));
+PackedStringArray ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance) const {
+ List<StringName> constants;
+ ::ClassDB::get_enum_constants(p_class, p_enum, &constants, p_no_inheritance);
- ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &_ClassDB::get_method_list, DEFVAL(false));
+ PackedStringArray ret;
+ ret.resize(constants.size());
+ int idx = 0;
+ for (const StringName &E : constants) {
+ ret.set(idx++, E);
+ }
- ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &_ClassDB::get_integer_constant_list, DEFVAL(false));
+ return ret;
+}
- ClassDB::bind_method(D_METHOD("class_has_integer_constant", "class", "name"), &_ClassDB::has_integer_constant);
- ClassDB::bind_method(D_METHOD("class_get_integer_constant", "class", "name"), &_ClassDB::get_integer_constant);
+StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const {
+ return ::ClassDB::get_integer_constant_enum(p_class, p_name, p_no_inheritance);
+}
- ClassDB::bind_method(D_METHOD("class_get_category", "class"), &_ClassDB::get_category);
- ClassDB::bind_method(D_METHOD("is_class_enabled", "class"), &_ClassDB::is_class_enabled);
+bool ClassDB::is_class_enabled(StringName p_class) const {
+ return ::ClassDB::is_class_enabled(p_class);
}
-////// _Engine //////
+void ClassDB::_bind_methods() {
+ ::ClassDB::bind_method(D_METHOD("get_class_list"), &ClassDB::get_class_list);
+ ::ClassDB::bind_method(D_METHOD("get_inheriters_from_class", "class"), &ClassDB::get_inheriters_from_class);
+ ::ClassDB::bind_method(D_METHOD("get_parent_class", "class"), &ClassDB::get_parent_class);
+ ::ClassDB::bind_method(D_METHOD("class_exists", "class"), &ClassDB::class_exists);
+ ::ClassDB::bind_method(D_METHOD("is_parent_class", "class", "inherits"), &ClassDB::is_parent_class);
+ ::ClassDB::bind_method(D_METHOD("can_instantiate", "class"), &ClassDB::can_instantiate);
+ ::ClassDB::bind_method(D_METHOD("instantiate", "class"), &ClassDB::instantiate);
-void _Engine::set_iterations_per_second(int p_ips) {
- Engine::get_singleton()->set_iterations_per_second(p_ips);
-}
+ ::ClassDB::bind_method(D_METHOD("class_has_signal", "class", "signal"), &ClassDB::has_signal);
+ ::ClassDB::bind_method(D_METHOD("class_get_signal", "class", "signal"), &ClassDB::get_signal);
+ ::ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &ClassDB::get_signal_list, DEFVAL(false));
-int _Engine::get_iterations_per_second() const {
- return Engine::get_singleton()->get_iterations_per_second();
-}
+ ::ClassDB::bind_method(D_METHOD("class_get_property_list", "class", "no_inheritance"), &ClassDB::get_property_list, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &ClassDB::get_property);
+ ::ClassDB::bind_method(D_METHOD("class_set_property", "object", "property", "value"), &ClassDB::set_property);
-void _Engine::set_physics_jitter_fix(float p_threshold) {
- Engine::get_singleton()->set_physics_jitter_fix(p_threshold);
-}
+ ::ClassDB::bind_method(D_METHOD("class_has_method", "class", "method", "no_inheritance"), &ClassDB::has_method, DEFVAL(false));
-float _Engine::get_physics_jitter_fix() const {
- return Engine::get_singleton()->get_physics_jitter_fix();
-}
+ ::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::get_method_list, DEFVAL(false));
-float _Engine::get_physics_interpolation_fraction() const {
- return Engine::get_singleton()->get_physics_interpolation_fraction();
-}
+ ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &ClassDB::get_integer_constant_list, DEFVAL(false));
-void _Engine::set_target_fps(int p_fps) {
- Engine::get_singleton()->set_target_fps(p_fps);
-}
+ ::ClassDB::bind_method(D_METHOD("class_has_integer_constant", "class", "name"), &ClassDB::has_integer_constant);
+ ::ClassDB::bind_method(D_METHOD("class_get_integer_constant", "class", "name"), &ClassDB::get_integer_constant);
-int _Engine::get_target_fps() const {
- return Engine::get_singleton()->get_target_fps();
-}
+ ::ClassDB::bind_method(D_METHOD("class_has_enum", "class", "name", "no_inheritance"), &ClassDB::has_enum, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_get_enum_list", "class", "no_inheritance"), &ClassDB::get_enum_list, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_get_enum_constants", "class", "enum", "no_inheritance"), &ClassDB::get_enum_constants, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_enum", "class", "name", "no_inheritance"), &ClassDB::get_integer_constant_enum, DEFVAL(false));
-float _Engine::get_frames_per_second() const {
- return Engine::get_singleton()->get_frames_per_second();
+ ::ClassDB::bind_method(D_METHOD("class_get_category", "class"), &ClassDB::get_category);
+ ::ClassDB::bind_method(D_METHOD("is_class_enabled", "class"), &ClassDB::is_class_enabled);
}
-uint64_t _Engine::get_physics_frames() const {
- return Engine::get_singleton()->get_physics_frames();
-}
+} // namespace special
-uint64_t _Engine::get_process_frames() const {
- return Engine::get_singleton()->get_process_frames();
-}
+////// Engine //////
-void _Engine::set_time_scale(float p_scale) {
- Engine::get_singleton()->set_time_scale(p_scale);
+void Engine::set_physics_ticks_per_second(int p_ips) {
+ ::Engine::get_singleton()->set_physics_ticks_per_second(p_ips);
}
-float _Engine::get_time_scale() {
- return Engine::get_singleton()->get_time_scale();
+int Engine::get_physics_ticks_per_second() const {
+ return ::Engine::get_singleton()->get_physics_ticks_per_second();
}
-int _Engine::get_frames_drawn() {
- return Engine::get_singleton()->get_frames_drawn();
+void Engine::set_physics_jitter_fix(double p_threshold) {
+ ::Engine::get_singleton()->set_physics_jitter_fix(p_threshold);
}
-MainLoop *_Engine::get_main_loop() const {
- //needs to remain in OS, since it's actually OS that interacts with it, but it's better exposed here
- return OS::get_singleton()->get_main_loop();
+double Engine::get_physics_jitter_fix() const {
+ return ::Engine::get_singleton()->get_physics_jitter_fix();
}
-Dictionary _Engine::get_version_info() const {
- return Engine::get_singleton()->get_version_info();
+double Engine::get_physics_interpolation_fraction() const {
+ return ::Engine::get_singleton()->get_physics_interpolation_fraction();
}
-Dictionary _Engine::get_author_info() const {
- return Engine::get_singleton()->get_author_info();
+void Engine::set_target_fps(int p_fps) {
+ ::Engine::get_singleton()->set_target_fps(p_fps);
}
-Array _Engine::get_copyright_info() const {
- return Engine::get_singleton()->get_copyright_info();
+int Engine::get_target_fps() const {
+ return ::Engine::get_singleton()->get_target_fps();
}
-Dictionary _Engine::get_donor_info() const {
- return Engine::get_singleton()->get_donor_info();
+double Engine::get_frames_per_second() const {
+ return ::Engine::get_singleton()->get_frames_per_second();
}
-Dictionary _Engine::get_license_info() const {
- return Engine::get_singleton()->get_license_info();
+uint64_t Engine::get_physics_frames() const {
+ return ::Engine::get_singleton()->get_physics_frames();
}
-String _Engine::get_license_text() const {
- return Engine::get_singleton()->get_license_text();
+uint64_t Engine::get_process_frames() const {
+ return ::Engine::get_singleton()->get_process_frames();
}
-bool _Engine::is_in_physics_frame() const {
- return Engine::get_singleton()->is_in_physics_frame();
+void Engine::set_time_scale(double p_scale) {
+ ::Engine::get_singleton()->set_time_scale(p_scale);
}
-bool _Engine::has_singleton(const String &p_name) const {
- return Engine::get_singleton()->has_singleton(p_name);
+double Engine::get_time_scale() {
+ return ::Engine::get_singleton()->get_time_scale();
}
-Object *_Engine::get_singleton_object(const String &p_name) const {
- return Engine::get_singleton()->get_singleton_object(p_name);
+int Engine::get_frames_drawn() {
+ return ::Engine::get_singleton()->get_frames_drawn();
}
-void _Engine::set_editor_hint(bool p_enabled) {
- Engine::get_singleton()->set_editor_hint(p_enabled);
+MainLoop *Engine::get_main_loop() const {
+ // Needs to remain in OS, since it's actually OS that interacts with it, but it's better exposed here
+ return ::OS::get_singleton()->get_main_loop();
}
-bool _Engine::is_editor_hint() const {
- return Engine::get_singleton()->is_editor_hint();
+Dictionary Engine::get_version_info() const {
+ return ::Engine::get_singleton()->get_version_info();
}
-void _Engine::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_iterations_per_second", "iterations_per_second"), &_Engine::set_iterations_per_second);
- ClassDB::bind_method(D_METHOD("get_iterations_per_second"), &_Engine::get_iterations_per_second);
- ClassDB::bind_method(D_METHOD("set_physics_jitter_fix", "physics_jitter_fix"), &_Engine::set_physics_jitter_fix);
- ClassDB::bind_method(D_METHOD("get_physics_jitter_fix"), &_Engine::get_physics_jitter_fix);
- ClassDB::bind_method(D_METHOD("get_physics_interpolation_fraction"), &_Engine::get_physics_interpolation_fraction);
- ClassDB::bind_method(D_METHOD("set_target_fps", "target_fps"), &_Engine::set_target_fps);
- ClassDB::bind_method(D_METHOD("get_target_fps"), &_Engine::get_target_fps);
-
- ClassDB::bind_method(D_METHOD("set_time_scale", "time_scale"), &_Engine::set_time_scale);
- ClassDB::bind_method(D_METHOD("get_time_scale"), &_Engine::get_time_scale);
-
- ClassDB::bind_method(D_METHOD("get_frames_drawn"), &_Engine::get_frames_drawn);
- ClassDB::bind_method(D_METHOD("get_frames_per_second"), &_Engine::get_frames_per_second);
- ClassDB::bind_method(D_METHOD("get_physics_frames"), &_Engine::get_physics_frames);
- ClassDB::bind_method(D_METHOD("get_process_frames"), &_Engine::get_process_frames);
-
- ClassDB::bind_method(D_METHOD("get_main_loop"), &_Engine::get_main_loop);
-
- ClassDB::bind_method(D_METHOD("get_version_info"), &_Engine::get_version_info);
- ClassDB::bind_method(D_METHOD("get_author_info"), &_Engine::get_author_info);
- ClassDB::bind_method(D_METHOD("get_copyright_info"), &_Engine::get_copyright_info);
- ClassDB::bind_method(D_METHOD("get_donor_info"), &_Engine::get_donor_info);
- ClassDB::bind_method(D_METHOD("get_license_info"), &_Engine::get_license_info);
- ClassDB::bind_method(D_METHOD("get_license_text"), &_Engine::get_license_text);
-
- ClassDB::bind_method(D_METHOD("is_in_physics_frame"), &_Engine::is_in_physics_frame);
-
- ClassDB::bind_method(D_METHOD("has_singleton", "name"), &_Engine::has_singleton);
- ClassDB::bind_method(D_METHOD("get_singleton", "name"), &_Engine::get_singleton_object);
-
- ClassDB::bind_method(D_METHOD("set_editor_hint", "enabled"), &_Engine::set_editor_hint);
- ClassDB::bind_method(D_METHOD("is_editor_hint"), &_Engine::is_editor_hint);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_hint"), "set_editor_hint", "is_editor_hint");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "iterations_per_second"), "set_iterations_per_second", "get_iterations_per_second");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "target_fps"), "set_target_fps", "get_target_fps");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_scale"), "set_time_scale", "get_time_scale");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "physics_jitter_fix"), "set_physics_jitter_fix", "get_physics_jitter_fix");
+Dictionary Engine::get_author_info() const {
+ return ::Engine::get_singleton()->get_author_info();
}
-_Engine *_Engine::singleton = nullptr;
-
-////// _JSON //////
-
-void JSONParseResult::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_error"), &JSONParseResult::get_error);
- ClassDB::bind_method(D_METHOD("get_error_string"), &JSONParseResult::get_error_string);
- ClassDB::bind_method(D_METHOD("get_error_line"), &JSONParseResult::get_error_line);
- ClassDB::bind_method(D_METHOD("get_result"), &JSONParseResult::get_result);
+Array Engine::get_copyright_info() const {
+ return ::Engine::get_singleton()->get_copyright_info();
+}
- ClassDB::bind_method(D_METHOD("set_error", "error"), &JSONParseResult::set_error);
- ClassDB::bind_method(D_METHOD("set_error_string", "error_string"), &JSONParseResult::set_error_string);
- ClassDB::bind_method(D_METHOD("set_error_line", "error_line"), &JSONParseResult::set_error_line);
- ClassDB::bind_method(D_METHOD("set_result", "result"), &JSONParseResult::set_result);
+Dictionary Engine::get_donor_info() const {
+ return ::Engine::get_singleton()->get_donor_info();
+}
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "error", PROPERTY_HINT_NONE, "Error", PROPERTY_USAGE_CLASS_IS_ENUM), "set_error", "get_error");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "error_string"), "set_error_string", "get_error_string");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "error_line"), "set_error_line", "get_error_line");
- ADD_PROPERTY(PropertyInfo(Variant::NIL, "result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "set_result", "get_result");
+Dictionary Engine::get_license_info() const {
+ return ::Engine::get_singleton()->get_license_info();
}
-void JSONParseResult::set_error(Error p_error) {
- error = p_error;
+String Engine::get_license_text() const {
+ return ::Engine::get_singleton()->get_license_text();
}
-Error JSONParseResult::get_error() const {
- return error;
+bool Engine::is_in_physics_frame() const {
+ return ::Engine::get_singleton()->is_in_physics_frame();
}
-void JSONParseResult::set_error_string(const String &p_error_string) {
- error_string = p_error_string;
+bool Engine::has_singleton(const StringName &p_name) const {
+ return ::Engine::get_singleton()->has_singleton(p_name);
}
-String JSONParseResult::get_error_string() const {
- return error_string;
+Object *Engine::get_singleton_object(const StringName &p_name) const {
+ return ::Engine::get_singleton()->get_singleton_object(p_name);
}
-void JSONParseResult::set_error_line(int p_error_line) {
- error_line = p_error_line;
+void Engine::register_singleton(const StringName &p_name, Object *p_object) {
+ ERR_FAIL_COND_MSG(has_singleton(p_name), "Singleton already registered: " + String(p_name));
+ ERR_FAIL_COND_MSG(p_name.operator String().is_valid_identifier(), "Singleton name is not a valid identifier: " + String(p_name));
+ ::Engine::Singleton s;
+ s.class_name = p_name;
+ s.name = p_name;
+ s.ptr = p_object;
+ s.user_created = true;
+ ::Engine::get_singleton()->add_singleton(s);
+ ;
+}
+void Engine::unregister_singleton(const StringName &p_name) {
+ ERR_FAIL_COND_MSG(!has_singleton(p_name), "Attempt to remove unregisteres singleton: " + String(p_name));
+ ERR_FAIL_COND_MSG(!::Engine::get_singleton()->is_singleton_user_created(p_name), "Attempt to remove non-user created singleton: " + String(p_name));
+ ::Engine::get_singleton()->remove_singleton(p_name);
}
-int JSONParseResult::get_error_line() const {
- return error_line;
+Vector<String> Engine::get_singleton_list() const {
+ List<::Engine::Singleton> singletons;
+ ::Engine::get_singleton()->get_singletons(&singletons);
+ Vector<String> ret;
+ for (List<::Engine::Singleton>::Element *E = singletons.front(); E; E = E->next()) {
+ ret.push_back(E->get().name);
+ }
+ return ret;
}
-void JSONParseResult::set_result(const Variant &p_result) {
- result = p_result;
+void Engine::set_editor_hint(bool p_enabled) {
+ ::Engine::get_singleton()->set_editor_hint(p_enabled);
}
-Variant JSONParseResult::get_result() const {
- return result;
+bool Engine::is_editor_hint() const {
+ return ::Engine::get_singleton()->is_editor_hint();
}
-void _JSON::_bind_methods() {
- ClassDB::bind_method(D_METHOD("print", "value", "indent", "sort_keys"), &_JSON::print, DEFVAL(String()), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("parse", "json"), &_JSON::parse);
+void Engine::set_print_error_messages(bool p_enabled) {
+ ::Engine::get_singleton()->set_print_error_messages(p_enabled);
}
-String _JSON::print(const Variant &p_value, const String &p_indent, bool p_sort_keys) {
- return JSON::print(p_value, p_indent, p_sort_keys);
+bool Engine::is_printing_error_messages() const {
+ return ::Engine::get_singleton()->is_printing_error_messages();
}
-Ref<JSONParseResult> _JSON::parse(const String &p_json) {
- Ref<JSONParseResult> result;
- result.instance();
+void Engine::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_physics_ticks_per_second", "physics_ticks_per_second"), &Engine::set_physics_ticks_per_second);
+ ClassDB::bind_method(D_METHOD("get_physics_ticks_per_second"), &Engine::get_physics_ticks_per_second);
+ ClassDB::bind_method(D_METHOD("set_physics_jitter_fix", "physics_jitter_fix"), &Engine::set_physics_jitter_fix);
+ ClassDB::bind_method(D_METHOD("get_physics_jitter_fix"), &Engine::get_physics_jitter_fix);
+ ClassDB::bind_method(D_METHOD("get_physics_interpolation_fraction"), &Engine::get_physics_interpolation_fraction);
+ ClassDB::bind_method(D_METHOD("set_target_fps", "target_fps"), &Engine::set_target_fps);
+ ClassDB::bind_method(D_METHOD("get_target_fps"), &Engine::get_target_fps);
- result->error = JSON::parse(p_json, result->result, result->error_string, result->error_line);
+ ClassDB::bind_method(D_METHOD("set_time_scale", "time_scale"), &Engine::set_time_scale);
+ ClassDB::bind_method(D_METHOD("get_time_scale"), &Engine::get_time_scale);
- if (result->error != OK) {
- ERR_PRINT(vformat("Error parsing JSON at line %s: %s", result->error_line, result->error_string));
- }
- return result;
-}
+ ClassDB::bind_method(D_METHOD("get_frames_drawn"), &Engine::get_frames_drawn);
+ ClassDB::bind_method(D_METHOD("get_frames_per_second"), &Engine::get_frames_per_second);
+ ClassDB::bind_method(D_METHOD("get_physics_frames"), &Engine::get_physics_frames);
+ ClassDB::bind_method(D_METHOD("get_process_frames"), &Engine::get_process_frames);
-_JSON *_JSON::singleton = nullptr;
+ ClassDB::bind_method(D_METHOD("get_main_loop"), &Engine::get_main_loop);
-////// _EngineDebugger //////
+ ClassDB::bind_method(D_METHOD("get_version_info"), &Engine::get_version_info);
+ ClassDB::bind_method(D_METHOD("get_author_info"), &Engine::get_author_info);
+ ClassDB::bind_method(D_METHOD("get_copyright_info"), &Engine::get_copyright_info);
+ ClassDB::bind_method(D_METHOD("get_donor_info"), &Engine::get_donor_info);
+ ClassDB::bind_method(D_METHOD("get_license_info"), &Engine::get_license_info);
+ ClassDB::bind_method(D_METHOD("get_license_text"), &Engine::get_license_text);
-void _EngineDebugger::_bind_methods() {
- ClassDB::bind_method(D_METHOD("is_active"), &_EngineDebugger::is_active);
+ ClassDB::bind_method(D_METHOD("is_in_physics_frame"), &Engine::is_in_physics_frame);
- ClassDB::bind_method(D_METHOD("register_profiler", "name", "toggle", "add", "tick"), &_EngineDebugger::register_profiler);
- ClassDB::bind_method(D_METHOD("unregister_profiler", "name"), &_EngineDebugger::unregister_profiler);
- ClassDB::bind_method(D_METHOD("is_profiling", "name"), &_EngineDebugger::is_profiling);
- ClassDB::bind_method(D_METHOD("has_profiler", "name"), &_EngineDebugger::has_profiler);
+ ClassDB::bind_method(D_METHOD("has_singleton", "name"), &Engine::has_singleton);
+ ClassDB::bind_method(D_METHOD("get_singleton", "name"), &Engine::get_singleton_object);
- ClassDB::bind_method(D_METHOD("profiler_add_frame_data", "name", "data"), &_EngineDebugger::profiler_add_frame_data);
- ClassDB::bind_method(D_METHOD("profiler_enable", "name", "enable", "arguments"), &_EngineDebugger::profiler_enable, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("register_singleton", "name", "instance"), &Engine::register_singleton);
+ ClassDB::bind_method(D_METHOD("unregister_singleton", "name"), &Engine::unregister_singleton);
+ ClassDB::bind_method(D_METHOD("get_singleton_list"), &Engine::get_singleton_list);
- ClassDB::bind_method(D_METHOD("register_message_capture", "name", "callable"), &_EngineDebugger::register_message_capture);
- ClassDB::bind_method(D_METHOD("unregister_message_capture", "name"), &_EngineDebugger::unregister_message_capture);
- ClassDB::bind_method(D_METHOD("has_capture", "name"), &_EngineDebugger::has_capture);
+ ClassDB::bind_method(D_METHOD("set_editor_hint", "enabled"), &Engine::set_editor_hint);
+ ClassDB::bind_method(D_METHOD("is_editor_hint"), &Engine::is_editor_hint);
- ClassDB::bind_method(D_METHOD("send_message", "message", "data"), &_EngineDebugger::send_message);
+ ClassDB::bind_method(D_METHOD("set_print_error_messages", "enabled"), &Engine::set_print_error_messages);
+ ClassDB::bind_method(D_METHOD("is_printing_error_messages"), &Engine::is_printing_error_messages);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_hint"), "set_editor_hint", "is_editor_hint");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "print_error_messages"), "set_print_error_messages", "is_printing_error_messages");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_ticks_per_second"), "set_physics_ticks_per_second", "get_physics_ticks_per_second");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "target_fps"), "set_target_fps", "get_target_fps");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_scale"), "set_time_scale", "get_time_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "physics_jitter_fix"), "set_physics_jitter_fix", "get_physics_jitter_fix");
}
-bool _EngineDebugger::is_active() {
- return EngineDebugger::is_active();
+Engine *Engine::singleton = nullptr;
+
+////// EngineDebugger //////
+
+bool EngineDebugger::is_active() {
+ return ::EngineDebugger::is_active();
}
-void _EngineDebugger::register_profiler(const StringName &p_name, const Callable &p_toggle, const Callable &p_add, const Callable &p_tick) {
+void EngineDebugger::register_profiler(const StringName &p_name, const Callable &p_toggle, const Callable &p_add, const Callable &p_tick) {
ERR_FAIL_COND_MSG(profilers.has(p_name) || has_profiler(p_name), "Profiler already registered: " + p_name);
profilers.insert(p_name, ProfilerCallable(p_toggle, p_add, p_tick));
ProfilerCallable &p = profilers[p_name];
- EngineDebugger::Profiler profiler(
+ ::EngineDebugger::Profiler profiler(
&p,
- &_EngineDebugger::call_toggle,
- &_EngineDebugger::call_add,
- &_EngineDebugger::call_tick);
- EngineDebugger::register_profiler(p_name, profiler);
+ &EngineDebugger::call_toggle,
+ &EngineDebugger::call_add,
+ &EngineDebugger::call_tick);
+ ::EngineDebugger::register_profiler(p_name, profiler);
}
-void _EngineDebugger::unregister_profiler(const StringName &p_name) {
+void EngineDebugger::unregister_profiler(const StringName &p_name) {
ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + p_name);
- EngineDebugger::unregister_profiler(p_name);
+ ::EngineDebugger::unregister_profiler(p_name);
profilers.erase(p_name);
}
-bool _EngineDebugger::_EngineDebugger::is_profiling(const StringName &p_name) {
- return EngineDebugger::is_profiling(p_name);
+bool EngineDebugger::is_profiling(const StringName &p_name) {
+ return ::EngineDebugger::is_profiling(p_name);
}
-bool _EngineDebugger::has_profiler(const StringName &p_name) {
- return EngineDebugger::has_profiler(p_name);
+bool EngineDebugger::has_profiler(const StringName &p_name) {
+ return ::EngineDebugger::has_profiler(p_name);
}
-void _EngineDebugger::profiler_add_frame_data(const StringName &p_name, const Array &p_data) {
- EngineDebugger::profiler_add_frame_data(p_name, p_data);
+void EngineDebugger::profiler_add_frame_data(const StringName &p_name, const Array &p_data) {
+ ::EngineDebugger::profiler_add_frame_data(p_name, p_data);
}
-void _EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts) {
- if (EngineDebugger::get_singleton()) {
- EngineDebugger::get_singleton()->profiler_enable(p_name, p_enabled, p_opts);
+void EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts) {
+ if (::EngineDebugger::get_singleton()) {
+ ::EngineDebugger::get_singleton()->profiler_enable(p_name, p_enabled, p_opts);
}
}
-void _EngineDebugger::register_message_capture(const StringName &p_name, const Callable &p_callable) {
+void EngineDebugger::register_message_capture(const StringName &p_name, const Callable &p_callable) {
ERR_FAIL_COND_MSG(captures.has(p_name) || has_capture(p_name), "Capture already registered: " + p_name);
captures.insert(p_name, p_callable);
Callable &c = captures[p_name];
- EngineDebugger::Capture capture(&c, &_EngineDebugger::call_capture);
- EngineDebugger::register_message_capture(p_name, capture);
+ ::EngineDebugger::Capture capture(&c, &EngineDebugger::call_capture);
+ ::EngineDebugger::register_message_capture(p_name, capture);
}
-void _EngineDebugger::unregister_message_capture(const StringName &p_name) {
+void EngineDebugger::unregister_message_capture(const StringName &p_name) {
ERR_FAIL_COND_MSG(!captures.has(p_name), "Capture not registered: " + p_name);
- EngineDebugger::unregister_message_capture(p_name);
+ ::EngineDebugger::unregister_message_capture(p_name);
captures.erase(p_name);
}
-bool _EngineDebugger::has_capture(const StringName &p_name) {
- return EngineDebugger::has_capture(p_name);
+bool EngineDebugger::has_capture(const StringName &p_name) {
+ return ::EngineDebugger::has_capture(p_name);
}
-void _EngineDebugger::send_message(const String &p_msg, const Array &p_data) {
- ERR_FAIL_COND_MSG(!EngineDebugger::is_active(), "Can't send message. No active debugger");
- EngineDebugger::get_singleton()->send_message(p_msg, p_data);
+void EngineDebugger::send_message(const String &p_msg, const Array &p_data) {
+ ERR_FAIL_COND_MSG(!::EngineDebugger::is_active(), "Can't send message. No active debugger");
+ ::EngineDebugger::get_singleton()->send_message(p_msg, p_data);
}
-void _EngineDebugger::call_toggle(void *p_user, bool p_enable, const Array &p_opts) {
+void EngineDebugger::call_toggle(void *p_user, bool p_enable, const Array &p_opts) {
Callable &toggle = ((ProfilerCallable *)p_user)->callable_toggle;
if (toggle.is_null()) {
return;
@@ -2533,7 +2386,7 @@ void _EngineDebugger::call_toggle(void *p_user, bool p_enable, const Array &p_op
ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'toggle' to callable: " + Variant::get_callable_error_text(toggle, args, 2, err));
}
-void _EngineDebugger::call_add(void *p_user, const Array &p_data) {
+void EngineDebugger::call_add(void *p_user, const Array &p_data) {
Callable &add = ((ProfilerCallable *)p_user)->callable_add;
if (add.is_null()) {
return;
@@ -2546,7 +2399,7 @@ void _EngineDebugger::call_add(void *p_user, const Array &p_data) {
ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'add' to callable: " + Variant::get_callable_error_text(add, args, 1, err));
}
-void _EngineDebugger::call_tick(void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+void EngineDebugger::call_tick(void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
Callable &tick = ((ProfilerCallable *)p_user)->callable_tick;
if (tick.is_null()) {
return;
@@ -2559,7 +2412,7 @@ void _EngineDebugger::call_tick(void *p_user, float p_frame_time, float p_idle_t
ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'tick' to callable: " + Variant::get_callable_error_text(tick, args, 4, err));
}
-Error _EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
+Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
Callable &capture = *(Callable *)p_user;
if (capture.is_null()) {
return FAILED;
@@ -2575,15 +2428,35 @@ Error _EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Arr
return OK;
}
-_EngineDebugger::~_EngineDebugger() {
+EngineDebugger::~EngineDebugger() {
for (Map<StringName, Callable>::Element *E = captures.front(); E; E = E->next()) {
- EngineDebugger::unregister_message_capture(E->key());
+ ::EngineDebugger::unregister_message_capture(E->key());
}
captures.clear();
for (Map<StringName, ProfilerCallable>::Element *E = profilers.front(); E; E = E->next()) {
- EngineDebugger::unregister_profiler(E->key());
+ ::EngineDebugger::unregister_profiler(E->key());
}
profilers.clear();
}
-_EngineDebugger *_EngineDebugger::singleton = nullptr;
+EngineDebugger *EngineDebugger::singleton = nullptr;
+
+void EngineDebugger::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("is_active"), &EngineDebugger::is_active);
+
+ ClassDB::bind_method(D_METHOD("register_profiler", "name", "toggle", "add", "tick"), &EngineDebugger::register_profiler);
+ ClassDB::bind_method(D_METHOD("unregister_profiler", "name"), &EngineDebugger::unregister_profiler);
+ ClassDB::bind_method(D_METHOD("is_profiling", "name"), &EngineDebugger::is_profiling);
+ ClassDB::bind_method(D_METHOD("has_profiler", "name"), &EngineDebugger::has_profiler);
+
+ ClassDB::bind_method(D_METHOD("profiler_add_frame_data", "name", "data"), &EngineDebugger::profiler_add_frame_data);
+ ClassDB::bind_method(D_METHOD("profiler_enable", "name", "enable", "arguments"), &EngineDebugger::profiler_enable, DEFVAL(Array()));
+
+ ClassDB::bind_method(D_METHOD("register_message_capture", "name", "callable"), &EngineDebugger::register_message_capture);
+ ClassDB::bind_method(D_METHOD("unregister_message_capture", "name"), &EngineDebugger::unregister_message_capture);
+ ClassDB::bind_method(D_METHOD("has_capture", "name"), &EngineDebugger::has_capture);
+
+ ClassDB::bind_method(D_METHOD("send_message", "message", "data"), &EngineDebugger::send_message);
+}
+
+} // namespace core_bind
diff --git a/core/core_bind.h b/core/core_bind.h
index 8253040a12..8e95629695 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -32,22 +32,26 @@
#define CORE_BIND_H
#include "core/io/compression.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/io/image.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/os/semaphore.h"
#include "core/os/thread.h"
#include "core/templates/safe_refcount.h"
-class _ResourceLoader : public Object {
- GDCLASS(_ResourceLoader, Object);
+class MainLoop;
+
+namespace core_bind {
+
+class ResourceLoader : public Object {
+ GDCLASS(ResourceLoader, Object);
protected:
static void _bind_methods();
- static _ResourceLoader *singleton;
+ static ResourceLoader *singleton;
public:
enum ThreadLoadStatus {
@@ -58,12 +62,12 @@ public:
};
enum CacheMode {
- CACHE_MODE_IGNORE, //resource and subresources do not use path cache, no path is set into resource.
- CACHE_MODE_REUSE, //resource and subresources use patch cache, reuse existing loaded resources instead of loading from disk when available
- CACHE_MODE_REPLACE, //resource and and subresource use path cache, but replace existing loaded resources when available with information from disk
+ CACHE_MODE_IGNORE, // Resource and subresources do not use path cache, no path is set into resource.
+ CACHE_MODE_REUSE, // Resource and subresources use patch cache, reuse existing loaded resources instead of loading from disk when available.
+ CACHE_MODE_REPLACE, // Resource and subresource use path cache, but replace existing loaded resources when available with information from disk.
};
- static _ResourceLoader *get_singleton() { return singleton; }
+ static ResourceLoader *get_singleton() { return singleton; }
Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false);
ThreadLoadStatus load_threaded_get_status(const String &p_path, Array r_progress = Array());
@@ -75,19 +79,17 @@ public:
PackedStringArray get_dependencies(const String &p_path);
bool has_cached(const String &p_path);
bool exists(const String &p_path, const String &p_type_hint = "");
+ ResourceUID::ID get_resource_uid(const String &p_path);
- _ResourceLoader() { singleton = this; }
+ ResourceLoader() { singleton = this; }
};
-VARIANT_ENUM_CAST(_ResourceLoader::ThreadLoadStatus);
-VARIANT_ENUM_CAST(_ResourceLoader::CacheMode);
-
-class _ResourceSaver : public Object {
- GDCLASS(_ResourceSaver, Object);
+class ResourceSaver : public Object {
+ GDCLASS(ResourceSaver, Object);
protected:
static void _bind_methods();
- static _ResourceSaver *singleton;
+ static ResourceSaver *singleton;
public:
enum SaverFlags {
@@ -100,24 +102,20 @@ public:
FLAG_REPLACE_SUBRESOURCE_PATHS = 64,
};
- static _ResourceSaver *get_singleton() { return singleton; }
+ static ResourceSaver *get_singleton() { return singleton; }
Error save(const String &p_path, const RES &p_resource, SaverFlags p_flags);
Vector<String> get_recognized_extensions(const RES &p_resource);
- _ResourceSaver() { singleton = this; }
+ ResourceSaver() { singleton = this; }
};
-VARIANT_ENUM_CAST(_ResourceSaver::SaverFlags);
-
-class MainLoop;
-
-class _OS : public Object {
- GDCLASS(_OS, Object);
+class OS : public Object {
+ GDCLASS(OS, Object);
protected:
static void _bind_methods();
- static _OS *singleton;
+ static OS *singleton;
public:
enum VideoDriver {
@@ -162,6 +160,8 @@ public:
void set_low_processor_usage_mode_sleep_usec(int p_usec);
int get_low_processor_usage_mode_sleep_usec() const;
+ void alert(const String &p_alert, const String &p_title = "ALERT!");
+
String get_executable_path() const;
int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false);
int create_process(const String &p_path, const Vector<String> &p_arguments);
@@ -199,14 +199,6 @@ public:
void set_use_file_access_save_and_swap(bool p_enable);
- Dictionary get_date(bool utc) const;
- Dictionary get_time(bool utc) const;
- Dictionary get_datetime(bool utc) const;
- Dictionary get_datetime_from_unix_time(int64_t unix_time_val) const;
- int64_t get_unix_time_from_datetime(Dictionary datetime) const;
- Dictionary get_time_zone_info() const;
- double get_unix_time() const;
-
uint64_t get_static_memory_usage() const;
uint64_t get_static_memory_peak_usage() const;
@@ -234,9 +226,12 @@ public:
SYSTEM_DIR_RINGTONES,
};
- String get_system_dir(SystemDir p_dir) const;
+ String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const;
String get_user_data_dir() const;
+ String get_config_dir() const;
+ String get_data_dir() const;
+ String get_cache_dir() const;
Error set_thread_name(const String &p_name);
Thread::ID get_thread_caller_id() const;
@@ -247,26 +242,21 @@ public:
bool request_permissions();
Vector<String> get_granted_permissions() const;
- static _OS *get_singleton() { return singleton; }
+ static OS *get_singleton() { return singleton; }
- _OS() { singleton = this; }
+ OS() { singleton = this; }
};
-VARIANT_ENUM_CAST(_OS::VideoDriver);
-VARIANT_ENUM_CAST(_OS::Weekday);
-VARIANT_ENUM_CAST(_OS::Month);
-VARIANT_ENUM_CAST(_OS::SystemDir);
+class Geometry2D : public Object {
+ GDCLASS(Geometry2D, Object);
-class _Geometry2D : public Object {
- GDCLASS(_Geometry2D, Object);
-
- static _Geometry2D *singleton;
+ static Geometry2D *singleton;
protected:
static void _bind_methods();
public:
- static _Geometry2D *get_singleton();
+ static Geometry2D *get_singleton();
Variant segment_intersects_segment(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b);
Variant line_intersects_line(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b);
Vector<Vector2> get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2);
@@ -317,23 +307,19 @@ public:
Dictionary make_atlas(const Vector<Size2> &p_rects);
- _Geometry2D() { singleton = this; }
+ Geometry2D() { singleton = this; }
};
-VARIANT_ENUM_CAST(_Geometry2D::PolyBooleanOperation);
-VARIANT_ENUM_CAST(_Geometry2D::PolyJoinType);
-VARIANT_ENUM_CAST(_Geometry2D::PolyEndType);
-
-class _Geometry3D : public Object {
- GDCLASS(_Geometry3D, Object);
+class Geometry3D : public Object {
+ GDCLASS(Geometry3D, Object);
- static _Geometry3D *singleton;
+ static Geometry3D *singleton;
protected:
static void _bind_methods();
public:
- static _Geometry3D *get_singleton();
+ static Geometry3D *get_singleton();
Vector<Plane> build_box_planes(const Vector3 &p_extents);
Vector<Plane> build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z);
Vector<Plane> build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z);
@@ -349,14 +335,14 @@ public:
Vector<Vector3> clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane);
- _Geometry3D() { singleton = this; }
+ Geometry3D() { singleton = this; }
};
-class _File : public Reference {
- GDCLASS(_File, Reference);
+class File : public RefCounted {
+ GDCLASS(File, RefCounted);
FileAccess *f = nullptr;
- bool eswap = false;
+ bool big_endian = false;
protected:
static void _bind_methods();
@@ -413,13 +399,13 @@ public:
String get_md5(const String &p_path) const;
String get_sha256(const String &p_path) const;
- /* Use this for files WRITTEN in _big_ endian machines (ie, amiga/mac).
+ /*
+ * Use this for files WRITTEN in _big_ endian machines (ie, amiga/mac).
* It's not about the current CPU type but file formats.
- * This flags get reset to false (little endian) on each open.
+ * This flag gets reset to `false` (little endian) on each open.
*/
-
- void set_endian_swap(bool p_swap);
- bool get_endian_swap();
+ void set_big_endian(bool p_big_endian);
+ bool is_big_endian();
Error get_error() const; // Get last error.
@@ -447,15 +433,12 @@ public:
uint64_t get_modified_time(const String &p_file) const;
- _File() {}
- virtual ~_File();
+ File() {}
+ virtual ~File();
};
-VARIANT_ENUM_CAST(_File::ModeFlags);
-VARIANT_ENUM_CAST(_File::CompressionMode);
-
-class _Directory : public Reference {
- GDCLASS(_Directory, Reference);
+class Directory : public RefCounted {
+ GDCLASS(Directory, RefCounted);
DirAccess *d;
bool dir_open = false;
@@ -492,24 +475,24 @@ public:
Error rename(String p_from, String p_to);
Error remove(String p_name);
- _Directory();
- virtual ~_Directory();
+ Directory();
+ virtual ~Directory();
private:
bool _list_skip_navigational = false;
bool _list_skip_hidden = false;
};
-class _Marshalls : public Object {
- GDCLASS(_Marshalls, Object);
+class Marshalls : public Object {
+ GDCLASS(Marshalls, Object);
- static _Marshalls *singleton;
+ static Marshalls *singleton;
protected:
static void _bind_methods();
public:
- static _Marshalls *get_singleton();
+ static Marshalls *get_singleton();
String variant_to_base64(const Variant &p_var, bool p_full_objects = false);
Variant base64_to_variant(const String &p_str, bool p_allow_objects = false);
@@ -520,13 +503,13 @@ public:
String utf8_to_base64(const String &p_str);
String base64_to_utf8(const String &p_str);
- _Marshalls() { singleton = this; }
- ~_Marshalls() { singleton = nullptr; }
+ Marshalls() { singleton = this; }
+ ~Marshalls() { singleton = nullptr; }
};
-class _Mutex : public Reference {
- GDCLASS(_Mutex, Reference);
- Mutex mutex;
+class Mutex : public RefCounted {
+ GDCLASS(Mutex, RefCounted);
+ ::Mutex mutex;
static void _bind_methods();
@@ -536,9 +519,9 @@ public:
void unlock();
};
-class _Semaphore : public Reference {
- GDCLASS(_Semaphore, Reference);
- Semaphore semaphore;
+class Semaphore : public RefCounted {
+ GDCLASS(Semaphore, RefCounted);
+ ::Semaphore semaphore;
static void _bind_methods();
@@ -548,8 +531,8 @@ public:
void post();
};
-class _Thread : public Reference {
- GDCLASS(_Thread, Reference);
+class Thread : public RefCounted {
+ GDCLASS(Thread, RefCounted);
protected:
Variant ret;
@@ -557,7 +540,7 @@ protected:
SafeFlag active;
Object *target_instance = nullptr;
StringName target_method;
- Thread thread;
+ ::Thread thread;
static void _bind_methods();
static void _start_func(void *ud);
@@ -575,10 +558,10 @@ public:
Variant wait_to_finish();
};
-VARIANT_ENUM_CAST(_Thread::Priority);
+namespace special {
-class _ClassDB : public Object {
- GDCLASS(_ClassDB, Object);
+class ClassDB : public Object {
+ GDCLASS(ClassDB, Object);
protected:
static void _bind_methods();
@@ -589,8 +572,8 @@ public:
StringName get_parent_class(const StringName &p_class) const;
bool class_exists(const StringName &p_class) const;
bool is_parent_class(const StringName &p_class, const StringName &p_inherits) const;
- bool can_instance(const StringName &p_class) const;
- Variant instance(const StringName &p_class) const;
+ bool can_instantiate(const StringName &p_class) const;
+ Variant instantiate(const StringName &p_class) const;
bool has_signal(StringName p_class, StringName p_signal) const;
Dictionary get_signal(StringName p_class, StringName p_signal) const;
@@ -609,39 +592,46 @@ public:
int get_integer_constant(const StringName &p_class, const StringName &p_name) const;
StringName get_category(const StringName &p_node) const;
+ bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const;
+ PackedStringArray get_enum_list(const StringName &p_class, bool p_no_inheritance = false) const;
+ PackedStringArray get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const;
+ StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const;
+
bool is_class_enabled(StringName p_class) const;
- _ClassDB() {}
- ~_ClassDB() {}
+ ClassDB() {}
+ ~ClassDB() {}
};
-class _Engine : public Object {
- GDCLASS(_Engine, Object);
+} // namespace special
+
+class Engine : public Object {
+ GDCLASS(Engine, Object);
protected:
static void _bind_methods();
- static _Engine *singleton;
+ static Engine *singleton;
public:
- static _Engine *get_singleton() { return singleton; }
- void set_iterations_per_second(int p_ips);
- int get_iterations_per_second() const;
+ static Engine *get_singleton() { return singleton; }
+ void set_physics_ticks_per_second(int p_ips);
+ int get_physics_ticks_per_second() const;
- void set_physics_jitter_fix(float p_threshold);
- float get_physics_jitter_fix() const;
- float get_physics_interpolation_fraction() const;
+ void set_physics_jitter_fix(double p_threshold);
+ double get_physics_jitter_fix() const;
+ double get_physics_interpolation_fraction() const;
void set_target_fps(int p_fps);
int get_target_fps() const;
- float get_frames_per_second() const;
+ double get_frames_per_second() const;
uint64_t get_physics_frames() const;
uint64_t get_process_frames() const;
int get_frames_drawn();
- void set_time_scale(float p_scale);
- float get_time_scale();
+ void set_time_scale(double p_scale);
+ double get_time_scale();
MainLoop *get_main_loop() const;
@@ -654,68 +644,26 @@ public:
bool is_in_physics_frame() const;
- bool has_singleton(const String &p_name) const;
- Object *get_singleton_object(const String &p_name) const;
+ bool has_singleton(const StringName &p_name) const;
+ Object *get_singleton_object(const StringName &p_name) const;
+ void register_singleton(const StringName &p_name, Object *p_object);
+ void unregister_singleton(const StringName &p_name);
+ Vector<String> get_singleton_list() const;
void set_editor_hint(bool p_enabled);
bool is_editor_hint() const;
- _Engine() { singleton = this; }
-};
-
-class _JSON;
-
-class JSONParseResult : public Reference {
- GDCLASS(JSONParseResult, Reference);
+ void set_print_error_messages(bool p_enabled);
+ bool is_printing_error_messages() const;
- friend class _JSON;
-
- Error error;
- String error_string;
- int error_line = -1;
-
- Variant result;
-
-protected:
- static void _bind_methods();
-
-public:
- void set_error(Error p_error);
- Error get_error() const;
-
- void set_error_string(const String &p_error_string);
- String get_error_string() const;
-
- void set_error_line(int p_error_line);
- int get_error_line() const;
-
- void set_result(const Variant &p_result);
- Variant get_result() const;
-
- JSONParseResult() {}
-};
-
-class _JSON : public Object {
- GDCLASS(_JSON, Object);
-
-protected:
- static void _bind_methods();
- static _JSON *singleton;
-
-public:
- static _JSON *get_singleton() { return singleton; }
-
- String print(const Variant &p_value, const String &p_indent = "", bool p_sort_keys = false);
- Ref<JSONParseResult> parse(const String &p_json);
-
- _JSON() { singleton = this; }
+ Engine() { singleton = this; }
};
-class _EngineDebugger : public Object {
- GDCLASS(_EngineDebugger, Object);
+class EngineDebugger : public Object {
+ GDCLASS(EngineDebugger, Object);
class ProfilerCallable {
- friend class _EngineDebugger;
+ friend class EngineDebugger;
Callable callable_toggle;
Callable callable_add;
@@ -736,10 +684,10 @@ class _EngineDebugger : public Object {
protected:
static void _bind_methods();
- static _EngineDebugger *singleton;
+ static EngineDebugger *singleton;
public:
- static _EngineDebugger *get_singleton() { return singleton; }
+ static EngineDebugger *get_singleton() { return singleton; }
bool is_active();
@@ -758,11 +706,32 @@ public:
static void call_toggle(void *p_user, bool p_enable, const Array &p_opts);
static void call_add(void *p_user, const Array &p_data);
- static void call_tick(void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
+ static void call_tick(void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time);
static Error call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured);
- _EngineDebugger() { singleton = this; }
- ~_EngineDebugger();
+ EngineDebugger() { singleton = this; }
+ ~EngineDebugger();
};
+} // namespace core_bind
+
+VARIANT_ENUM_CAST(core_bind::ResourceLoader::ThreadLoadStatus);
+VARIANT_ENUM_CAST(core_bind::ResourceLoader::CacheMode);
+
+VARIANT_ENUM_CAST(core_bind::ResourceSaver::SaverFlags);
+
+VARIANT_ENUM_CAST(core_bind::OS::VideoDriver);
+VARIANT_ENUM_CAST(core_bind::OS::Weekday);
+VARIANT_ENUM_CAST(core_bind::OS::Month);
+VARIANT_ENUM_CAST(core_bind::OS::SystemDir);
+
+VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyBooleanOperation);
+VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyJoinType);
+VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyEndType);
+
+VARIANT_ENUM_CAST(core_bind::File::ModeFlags);
+VARIANT_ENUM_CAST(core_bind::File::CompressionMode);
+
+VARIANT_ENUM_CAST(core_bind::Thread::Priority);
+
#endif // CORE_BIND_H
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index a0a41015dc..721e5ae622 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -31,6 +31,7 @@
#include "core_constants.h"
#include "core/input/input_event.h"
+#include "core/multiplayer/multiplayer.h"
#include "core/object/class_db.h"
#include "core/os/keyboard.h"
#include "core/variant/variant.h"
@@ -104,13 +105,6 @@ static Vector<_CoreConstant> _global_constants;
#endif
-VARIANT_ENUM_CAST(Key);
-VARIANT_ENUM_CAST(KeyModifierMask);
-VARIANT_ENUM_CAST(MouseButton);
-VARIANT_ENUM_CAST(JoyButton);
-VARIANT_ENUM_CAST(JoyAxis);
-VARIANT_ENUM_CAST(MIDIMessage);
-
void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(SIDE_LEFT);
BIND_CORE_ENUM_CONSTANT(SIDE_TOP);
@@ -137,6 +131,19 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(VALIGN_CENTER);
BIND_CORE_ENUM_CONSTANT(VALIGN_BOTTOM);
+ BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TOP_TO);
+ BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_CENTER_TO);
+ BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_BOTTOM_TO);
+
+ BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_TOP);
+ BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_CENTER);
+ BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_BASELINE);
+ BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_BOTTOM);
+
+ BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TOP);
+ BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_CENTER);
+ BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_BOTTOM);
+
// huge list of keys
BIND_CORE_CONSTANT(SPKEY);
@@ -512,8 +519,8 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NONE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_RANGE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_EXP_RANGE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ENUM);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ENUM_SUGGESTION);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_EXP_EASING);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LENGTH);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_KEY_ACCEL);
@@ -536,8 +543,27 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_COLOR_NO_ALPHA);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSY);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_OBJECT_ID);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_TYPE_STRING);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_VARIANT_TYPE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_BASE_TYPE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_INSTANCE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_SCRIPT);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_BASE_TYPE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_INSTANCE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_SCRIPT);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_OBJECT_TOO_BIG);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_PATH_VALID_TYPES);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_SAVE_FILE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_OBJECTID);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_POINTER);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ARRAY_TYPE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX);
+
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NONE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_STORAGE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR);
BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NETWORK);
@@ -553,6 +579,23 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_RESTART_IF_CHANGED);
BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_SCRIPT_VARIABLE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NULL);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_ANIMATE_AS_TRIGGER);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_CLASS_IS_ENUM);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NIL_IS_VARIANT);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_INTERNAL);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_HIGH_END_GFX);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_KEYING_INCREMENTS);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFERRED_SET_RESOURCE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR_BASIC_SETTING);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_ARRAY);
+
BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT);
BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT_INTL);
BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NOEDITOR);
@@ -565,8 +608,18 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_VIRTUAL);
BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_FROM_SCRIPT);
BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_STATIC);
+ BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_OBJECT_CORE);
BIND_CORE_ENUM_CONSTANT(METHOD_FLAGS_DEFAULT);
+ // rpc
+ BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_DISABLED", Multiplayer::RPC_MODE_DISABLED);
+ BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_ANY", Multiplayer::RPC_MODE_ANY);
+ BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_AUTH", Multiplayer::RPC_MODE_AUTHORITY);
+
+ BIND_CORE_ENUM_CONSTANT_CUSTOM("TRANSFER_MODE_UNRELIABLE", Multiplayer::TRANSFER_MODE_UNRELIABLE);
+ BIND_CORE_ENUM_CONSTANT_CUSTOM("TRANSFER_MODE_ORDERED", Multiplayer::TRANSFER_MODE_ORDERED);
+ BIND_CORE_ENUM_CONSTANT_CUSTOM("TRANSFER_MODE_RELIABLE", Multiplayer::TRANSFER_MODE_RELIABLE);
+
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NIL", Variant::NIL);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_BOOL", Variant::BOOL);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_INT", Variant::INT);
@@ -580,10 +633,10 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR3I", Variant::VECTOR3I);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_TRANSFORM2D", Variant::TRANSFORM2D);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PLANE", Variant::PLANE);
- BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_QUAT", Variant::QUAT);
+ BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_QUATERNION", Variant::QUATERNION);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_AABB", Variant::AABB);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_BASIS", Variant::BASIS);
- BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_TRANSFORM", Variant::TRANSFORM);
+ BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_TRANSFORM3D", Variant::TRANSFORM3D);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_COLOR", Variant::COLOR);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_STRING_NAME", Variant::STRING_NAME);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NODE_PATH", Variant::NODE_PATH);
diff --git a/core/crypto/aes_context.h b/core/crypto/aes_context.h
index cc00b18fd2..2f8422f537 100644
--- a/core/crypto/aes_context.h
+++ b/core/crypto/aes_context.h
@@ -32,10 +32,10 @@
#define AES_CONTEXT_H
#include "core/crypto/crypto_core.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
-class AESContext : public Reference {
- GDCLASS(AESContext, Reference);
+class AESContext : public RefCounted {
+ GDCLASS(AESContext, RefCounted);
public:
enum Mode {
diff --git a/core/crypto/crypto.h b/core/crypto/crypto.h
index 9438fcfea5..a46f42949d 100644
--- a/core/crypto/crypto.h
+++ b/core/crypto/crypto.h
@@ -35,7 +35,7 @@
#include "core/io/resource.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
class CryptoKey : public Resource {
GDCLASS(CryptoKey, Resource);
@@ -67,8 +67,8 @@ public:
virtual Error save(String p_path) = 0;
};
-class HMACContext : public Reference {
- GDCLASS(HMACContext, Reference);
+class HMACContext : public RefCounted {
+ GDCLASS(HMACContext, RefCounted);
protected:
static void _bind_methods();
@@ -82,10 +82,11 @@ public:
virtual PackedByteArray finish() = 0;
HMACContext() {}
+ virtual ~HMACContext() {}
};
-class Crypto : public Reference {
- GDCLASS(Crypto, Reference);
+class Crypto : public RefCounted {
+ GDCLASS(Crypto, RefCounted);
protected:
static void _bind_methods();
diff --git a/core/crypto/crypto_core.h b/core/crypto/crypto_core.h
index 27b380e838..7a2f4df589 100644
--- a/core/crypto/crypto_core.h
+++ b/core/crypto/crypto_core.h
@@ -31,7 +31,7 @@
#ifndef CRYPTO_CORE_H
#define CRYPTO_CORE_H
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
class CryptoCore {
public:
diff --git a/core/crypto/hashing_context.h b/core/crypto/hashing_context.h
index 892a48a4e8..31521a147c 100644
--- a/core/crypto/hashing_context.h
+++ b/core/crypto/hashing_context.h
@@ -31,10 +31,10 @@
#ifndef HASHING_CONTEXT_H
#define HASHING_CONTEXT_H
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
-class HashingContext : public Reference {
- GDCLASS(HashingContext, Reference);
+class HashingContext : public RefCounted {
+ GDCLASS(HashingContext, RefCounted);
public:
enum HashType {
diff --git a/core/debugger/debugger_marshalls.cpp b/core/debugger/debugger_marshalls.cpp
index 26f82c2658..2353a6ebf8 100644
--- a/core/debugger/debugger_marshalls.cpp
+++ b/core/debugger/debugger_marshalls.cpp
@@ -40,11 +40,11 @@ Array DebuggerMarshalls::ResourceUsage::serialize() {
Array arr;
arr.push_back(infos.size() * 4);
- for (List<ResourceInfo>::Element *E = infos.front(); E; E = E->next()) {
- arr.push_back(E->get().path);
- arr.push_back(E->get().format);
- arr.push_back(E->get().type);
- arr.push_back(E->get().vram);
+ for (const ResourceInfo &E : infos) {
+ arr.push_back(E.path);
+ arr.push_back(E.format);
+ arr.push_back(E.type);
+ arr.push_back(E.vram);
}
return arr;
}
diff --git a/core/debugger/debugger_marshalls.h b/core/debugger/debugger_marshalls.h
index 3e8c34d84b..98ad2b98d1 100644
--- a/core/debugger/debugger_marshalls.h
+++ b/core/debugger/debugger_marshalls.h
@@ -83,14 +83,14 @@ struct DebuggerMarshalls {
StringName name;
int sig_id = -1;
int call_count = 0;
- float self_time = 0;
- float total_time = 0;
+ double self_time = 0;
+ double total_time = 0;
};
// Servers profiler
struct ServerFunctionInfo {
StringName name;
- float time = 0;
+ double time = 0;
};
struct ServerInfo {
@@ -100,11 +100,11 @@ struct DebuggerMarshalls {
struct ServersProfilerFrame {
int frame_number = 0;
- float frame_time = 0;
- float idle_time = 0;
- float physics_time = 0;
- float physics_frame_time = 0;
- float script_time = 0;
+ double frame_time = 0;
+ double idle_time = 0;
+ double physics_time = 0;
+ double physics_frame_time = 0;
+ double script_time = 0;
List<ServerInfo> servers;
Vector<ScriptFunctionInfo> script_functions;
diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp
index e5dba029c9..a522b1310f 100644
--- a/core/debugger/engine_debugger.cpp
+++ b/core/debugger/engine_debugger.cpp
@@ -117,7 +117,7 @@ void EngineDebugger::line_poll() {
poll_every++;
}
-void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, float p_physics_frame_time) {
+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);
physics_time = USEC_TO_SEC(p_physics_ticks);
diff --git a/core/debugger/engine_debugger.h b/core/debugger/engine_debugger.h
index c6daea6e2f..22c6ef943e 100644
--- a/core/debugger/engine_debugger.h
+++ b/core/debugger/engine_debugger.h
@@ -44,7 +44,7 @@ class ScriptDebugger;
class EngineDebugger {
public:
typedef void (*ProfilingToggle)(void *p_user, bool p_enable, const Array &p_opts);
- typedef void (*ProfilingTick)(void *p_user, float p_frame_time, float p_process_time, float p_physics_time, float p_physics_frame_time);
+ typedef void (*ProfilingTick)(void *p_user, double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time);
typedef void (*ProfilingAdd)(void *p_user, const Array &p_arr);
typedef Error (*CaptureFunc)(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured);
@@ -85,10 +85,10 @@ public:
};
private:
- float frame_time = 0.0;
- float process_time = 0.0;
- float physics_time = 0.0;
- float physics_frame_time = 0.0;
+ double frame_time = 0.0;
+ double process_time = 0.0;
+ double physics_time = 0.0;
+ double physics_frame_time = 0.0;
uint32_t poll_every = 0;
@@ -120,7 +120,7 @@ public:
static void register_uri_handler(const String &p_protocol, CreatePeerFunc p_func);
- void iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, float p_physics_frame_time);
+ void iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, double p_physics_frame_time);
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);
diff --git a/core/debugger/local_debugger.cpp b/core/debugger/local_debugger.cpp
index ab368471e4..b0b3f11424 100644
--- a/core/debugger/local_debugger.cpp
+++ b/core/debugger/local_debugger.cpp
@@ -41,7 +41,7 @@ struct LocalDebugger::ScriptsProfiler {
}
};
- float frame_time = 0;
+ double frame_time = 0;
uint64_t idle_accum = 0;
Vector<ScriptLanguage::ProfilingInfo> pinfo;
@@ -61,7 +61,7 @@ struct LocalDebugger::ScriptsProfiler {
}
}
- void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
frame_time = p_frame_time;
_print_frame_data(false);
}
@@ -92,8 +92,8 @@ struct LocalDebugger::ScriptsProfiler {
for (int i = 0; i < ofs; i++) {
script_time_us += pinfo[i].self_time;
}
- float script_time = USEC_TO_SEC(script_time_us);
- float total_time = p_accumulated ? script_time : frame_time;
+ double script_time = USEC_TO_SEC(script_time_us);
+ double total_time = p_accumulated ? script_time : frame_time;
if (!p_accumulated) {
print_line("FRAME: total: " + rtos(total_time) + " script: " + rtos(script_time) + "/" + itos(script_time * 100 / total_time) + " %");
@@ -103,8 +103,8 @@ struct LocalDebugger::ScriptsProfiler {
for (int i = 0; i < ofs; i++) {
print_line(itos(i) + ":" + pinfo[i].signature);
- float tt = USEC_TO_SEC(pinfo[i].total_time);
- float st = USEC_TO_SEC(pinfo[i].self_time);
+ double tt = USEC_TO_SEC(pinfo[i].total_time);
+ double st = USEC_TO_SEC(pinfo[i].self_time);
print_line("\ttotal: " + rtos(tt) + "/" + itos(tt * 100 / total_time) + " % \tself: " + rtos(st) + "/" + itos(st * 100 / total_time) + " % tcalls: " + itos(pinfo[i].call_count));
}
}
@@ -320,13 +320,13 @@ void LocalDebugger::print_variables(const List<String> &names, const List<Varian
String value;
Vector<String> value_lines;
const List<Variant>::Element *V = values.front();
- for (const List<String>::Element *E = names.front(); E; E = E->next()) {
+ for (const String &E : names) {
value = String(V->get());
if (variable_prefix.is_empty()) {
- print_line(E->get() + ": " + String(V->get()));
+ print_line(E + ": " + String(V->get()));
} else {
- print_line(E->get() + ":");
+ print_line(E + ":");
value_lines = value.split("\n");
for (int i = 0; i < value_lines.size(); ++i) {
print_line(variable_prefix + value_lines[i]);
@@ -373,7 +373,7 @@ LocalDebugger::LocalDebugger() {
((ScriptsProfiler *)p_user)->toggle(p_enable, p_opts);
},
nullptr,
- [](void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ [](void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
((ScriptsProfiler *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time);
});
register_profiler("scripts", scr_prof);
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index bdbb7766fa..f865dfe102 100644
--- a/core/debugger/remote_debugger.cpp
+++ b/core/debugger/remote_debugger.cpp
@@ -50,7 +50,7 @@ void RemoteDebugger::_bind_profiler(const String &p_name, T *p_prof) {
[](void *p_user, const Array &p_data) {
((T *)p_user)->add(p_data);
},
- [](void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ [](void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
((T *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time);
});
EngineDebugger::register_profiler(p_name, prof);
@@ -164,7 +164,7 @@ public:
}
}
- void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
uint64_t pt = OS::get_singleton()->get_ticks_msec();
if (pt - last_bandwidth_time > 200) {
last_bandwidth_time = pt;
@@ -278,10 +278,10 @@ struct RemoteDebugger::ServersProfiler {
Map<StringName, ServerInfo> server_data;
ScriptsProfiler scripts_profiler;
- float frame_time = 0;
- float idle_time = 0;
- float physics_time = 0;
- float physics_frame_time = 0;
+ double frame_time = 0;
+ double idle_time = 0;
+ double physics_time = 0;
+ double physics_frame_time = 0;
void toggle(bool p_enable, const Array &p_opts) {
skip_profile_frame = false;
@@ -308,7 +308,7 @@ struct RemoteDebugger::ServersProfiler {
srv.functions.push_back(fi);
}
- void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
frame_time = p_frame_time;
idle_time = p_idle_time;
physics_time = p_physics_time;
@@ -358,7 +358,7 @@ struct RemoteDebugger::VisualProfiler {
void add(const Array &p_data) {}
- void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
Vector<RS::FrameProfileArea> profile_areas = RS::get_singleton()->get_frame_profile();
DebuggerMarshalls::VisualProfilerFrame frame;
if (!profile_areas.size()) {
@@ -378,7 +378,7 @@ struct RemoteDebugger::PerformanceProfiler {
void toggle(bool p_enable, const Array &p_opts) {}
void add(const Array &p_data) {}
- void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) {
if (!performance) {
return;
}
@@ -427,16 +427,16 @@ void RemoteDebugger::_send_resource_usage() {
List<RS::TextureInfo> tinfo;
RS::get_singleton()->texture_debug_usage(&tinfo);
- for (List<RS::TextureInfo>::Element *E = tinfo.front(); E; E = E->next()) {
+ for (const RS::TextureInfo &E : tinfo) {
DebuggerMarshalls::ResourceInfo info;
- info.path = E->get().path;
- info.vram = E->get().bytes;
- info.id = E->get().texture;
+ info.path = E.path;
+ info.vram = E.bytes;
+ info.id = E.texture;
info.type = "Texture";
- if (E->get().depth == 0) {
- info.format = itos(E->get().width) + "x" + itos(E->get().height) + " " + Image::get_format_name(E->get().format);
+ if (E.depth == 0) {
+ info.format = itos(E.width) + "x" + itos(E.height) + " " + Image::get_format_name(E.format);
} else {
- info.format = itos(E->get().width) + "x" + itos(E->get().height) + "x" + itos(E->get().depth) + " " + Image::get_format_name(E->get().format);
+ info.format = itos(E.width) + "x" + itos(E.height) + "x" + itos(E.depth) + " " + Image::get_format_name(E.format);
}
usage.infos.push_back(info);
}
@@ -706,6 +706,8 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
Array msg;
msg.push_back(p_can_continue);
msg.push_back(error_str);
+ ERR_FAIL_COND(!script_lang);
+ msg.push_back(script_lang->debug_get_stack_level_count() > 0);
send_message("debug_enter", msg);
servers_profiler->skip_profile_frame = true; // Avoid frame time spike in debug.
@@ -754,7 +756,6 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
break;
} else if (command == "get_stack_dump") {
- ERR_FAIL_COND(!script_lang);
DebuggerMarshalls::ScriptStackDump dump;
int slc = script_lang->debug_get_stack_level_count();
for (int i = 0; i < slc; i++) {
@@ -790,7 +791,9 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
script_lang->debug_get_globals(&globals, &globals_vals);
ERR_FAIL_COND(globals.size() != globals_vals.size());
- send_message("stack_frame_vars", Array());
+ Array var_size;
+ var_size.push_back(local_vals.size() + member_vals.size() + globals_vals.size());
+ send_message("stack_frame_vars", var_size);
_send_stack_vars(locals, local_vals, 0);
_send_stack_vars(members, member_vals, 1);
_send_stack_vars(globals, globals_vals, 2);
diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp
index 39113eda14..ea5e32203c 100644
--- a/core/debugger/remote_debugger_peer.cpp
+++ b/core/debugger/remote_debugger_peer.cpp
@@ -84,7 +84,7 @@ RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_tcp) {
thread.start(_thread_func, this);
#endif
} else {
- tcp_client.instance();
+ tcp_client.instantiate();
}
}
diff --git a/core/debugger/remote_debugger_peer.h b/core/debugger/remote_debugger_peer.h
index 652e2d9d20..8cba53a81c 100644
--- a/core/debugger/remote_debugger_peer.h
+++ b/core/debugger/remote_debugger_peer.h
@@ -32,12 +32,12 @@
#define REMOTE_DEBUGGER_PEER_H
#include "core/io/stream_peer_tcp.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
#include "core/os/mutex.h"
#include "core/os/thread.h"
#include "core/string/ustring.h"
-class RemoteDebuggerPeer : public Reference {
+class RemoteDebuggerPeer : public RefCounted {
protected:
int max_queued_messages = 4096;
diff --git a/core/doc_data.cpp b/core/doc_data.cpp
index 45450bf97a..4b284a30aa 100644
--- a/core/doc_data.cpp
+++ b/core/doc_data.cpp
@@ -31,7 +31,14 @@
#include "doc_data.h"
void DocData::return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo) {
- if (p_retinfo.type == Variant::INT && p_retinfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ if (p_retinfo.type == Variant::INT && p_retinfo.hint == PROPERTY_HINT_INT_IS_POINTER) {
+ p_method.return_type = p_retinfo.hint_string;
+ if (p_method.return_type == "") {
+ p_method.return_type = "void*";
+ } else {
+ p_method.return_type += "*";
+ }
+ } else if (p_retinfo.type == Variant::INT && p_retinfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
p_method.return_enum = p_retinfo.class_name;
if (p_method.return_enum.begins_with("_")) { //proxy class
p_method.return_enum = p_method.return_enum.substr(1, p_method.return_enum.length());
@@ -55,7 +62,14 @@ void DocData::return_doc_from_retinfo(DocData::MethodDoc &p_method, const Proper
void DocData::argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const PropertyInfo &p_arginfo) {
p_argument.name = p_arginfo.name;
- if (p_arginfo.type == Variant::INT && p_arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ if (p_arginfo.type == Variant::INT && p_arginfo.hint == PROPERTY_HINT_INT_IS_POINTER) {
+ p_argument.type = p_arginfo.hint_string;
+ if (p_argument.type == "") {
+ p_argument.type = "void*";
+ } else {
+ p_argument.type += "*";
+ }
+ } else if (p_arginfo.type == Variant::INT && p_arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
p_argument.enumeration = p_arginfo.class_name;
if (p_argument.enumeration.begins_with("_")) { //proxy class
p_argument.enumeration = p_argument.enumeration.substr(1, p_argument.enumeration.length());
diff --git a/core/doc_data.h b/core/doc_data.h
index 46ab697768..19dec71927 100644
--- a/core/doc_data.h
+++ b/core/doc_data.h
@@ -67,6 +67,7 @@ public:
String qualifiers;
String description;
Vector<ArgumentDoc> arguments;
+ Vector<int> errors_returned;
bool operator<(const MethodDoc &p_method) const {
if (name == p_method.name) {
// Must be a constructor since there is no overloading.
@@ -116,6 +117,17 @@ public:
}
};
+ struct ThemeItemDoc {
+ String name;
+ String type;
+ String data_type;
+ String description;
+ String default_value;
+ bool operator<(const ThemeItemDoc &p_theme_item) const {
+ return name < p_theme_item.name;
+ }
+ };
+
struct TutorialDoc {
String link;
String title;
@@ -133,7 +145,7 @@ public:
Vector<ConstantDoc> constants;
Map<String, String> enums;
Vector<PropertyDoc> properties;
- Vector<PropertyDoc> theme_properties;
+ Vector<ThemeItemDoc> theme_properties;
bool is_script_doc = false;
String script_path;
bool operator<(const ClassDoc &p_class) const {
diff --git a/core/error/error_list.cpp b/core/error/error_list.cpp
new file mode 100644
index 0000000000..e1e94dd65d
--- /dev/null
+++ b/core/error/error_list.cpp
@@ -0,0 +1,85 @@
+/*************************************************************************/
+/* error_list.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "error_list.h"
+
+const char *error_names[] = {
+ "OK", // OK
+ "Failed", // FAILED
+ "Unavailable", // ERR_UNAVAILABLE
+ "Unconfigured", // ERR_UNCONFIGURED
+ "Unauthorized", // ERR_UNAUTHORIZED
+ "Parameter out of range", // ERR_PARAMETER_RANGE_ERROR
+ "Out of memory", // ERR_OUT_OF_MEMORY
+ "File not found", // ERR_FILE_NOT_FOUND
+ "File: Bad drive", // ERR_FILE_BAD_DRIVE
+ "File: Bad path", // ERR_FILE_BAD_PATH
+ "File: Permission denied", // ERR_FILE_NO_PERMISSION
+ "File already in use", // ERR_FILE_ALREADY_IN_USE
+ "Can't open file", // ERR_FILE_CANT_OPEN
+ "Can't write file", // ERR_FILE_CANT_WRITE
+ "Can't read file", // ERR_FILE_CANT_READ
+ "File unrecognized", // ERR_FILE_UNRECOGNIZED
+ "File corrupt", // ERR_FILE_CORRUPT
+ "Missing dependencies for file", // ERR_FILE_MISSING_DEPENDENCIES
+ "End of file", // ERR_FILE_EOF
+ "Can't open", // ERR_CANT_OPEN
+ "Can't create", // ERR_CANT_CREATE
+ "Query failed", // ERR_QUERY_FAILED
+ "Already in use", // ERR_ALREADY_IN_USE
+ "Locked", // ERR_LOCKED
+ "Timeout", // ERR_TIMEOUT
+ "Can't connect", // ERR_CANT_CONNECT
+ "Can't resolve", // ERR_CANT_RESOLVE
+ "Connection error", // ERR_CONNECTION_ERROR
+ "Can't acquire resource", // ERR_CANT_ACQUIRE_RESOURCE
+ "Can't fork", // ERR_CANT_FORK
+ "Invalid data", // ERR_INVALID_DATA
+ "Invalid parameter", // ERR_INVALID_PARAMETER
+ "Already exists", // ERR_ALREADY_EXISTS
+ "Does not exist", // ERR_DOES_NOT_EXIST
+ "Can't read database", // ERR_DATABASE_CANT_READ
+ "Can't write database", // ERR_DATABASE_CANT_WRITE
+ "Compilation failed", // ERR_COMPILATION_FAILED
+ "Method not found", // ERR_METHOD_NOT_FOUND
+ "Link failed", // ERR_LINK_FAILED
+ "Script failed", // ERR_SCRIPT_FAILED
+ "Cyclic link detected", // ERR_CYCLIC_LINK
+ "Invalid declaration", // ERR_INVALID_DECLARATION
+ "Duplicate symbol", // ERR_DUPLICATE_SYMBOL
+ "Parse error", // ERR_PARSE_ERROR
+ "Busy", // ERR_BUSY
+ "Skip", // ERR_SKIP
+ "Help", // ERR_HELP
+ "Bug", // ERR_BUG
+ "Printer on fire", // ERR_PRINTER_ON_FIRE
+};
+
+static_assert(sizeof(error_names) / sizeof(*error_names) == ERR_MAX);
diff --git a/core/error/error_list.h b/core/error/error_list.h
index f032f44c1f..852825dda5 100644
--- a/core/error/error_list.h
+++ b/core/error/error_list.h
@@ -36,6 +36,11 @@
* values can be more detailed in the future.
*
* This is a generic error list, mainly for organizing a language of returning errors.
+ *
+ * Errors:
+ * - Are added to the Error enum in core/error/error_list.h
+ * - Have a description added to error_names in core/error/error_list.cpp
+ * - Are bound with BIND_CORE_ENUM_CONSTANT() in core/core_constants.cpp
*/
enum Error {
@@ -88,6 +93,9 @@ enum Error {
ERR_HELP, ///< user requested help!!
ERR_BUG, ///< a bug in the software certainly happened, due to a double check failing or unexpected behavior.
ERR_PRINTER_ON_FIRE, /// the parallel port printer is engulfed in flames
+ ERR_MAX, // Not being returned, value represents the number of errors
};
+extern const char *error_names[];
+
#endif // ERROR_LIST_H
diff --git a/core/extension/SCsub b/core/extension/SCsub
new file mode 100644
index 0000000000..a3a54250c1
--- /dev/null
+++ b/core/extension/SCsub
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env_extension = env.Clone()
+
+env_extension.add_source_files(env.core_sources, "*.cpp")
diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp
new file mode 100644
index 0000000000..a8547a0090
--- /dev/null
+++ b/core/extension/extension_api_dump.cpp
@@ -0,0 +1,869 @@
+/*************************************************************************/
+/* extension_api_dump.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "extension_api_dump.h"
+#include "core/config/engine.h"
+#include "core/core_constants.h"
+#include "core/io/file_access.h"
+#include "core/io/json.h"
+#include "core/templates/pair.h"
+#include "core/version.h"
+
+#ifdef TOOLS_ENABLED
+
+static String get_type_name(const PropertyInfo &p_info) {
+ if (p_info.type == Variant::INT && (p_info.hint == PROPERTY_HINT_INT_IS_POINTER)) {
+ if (p_info.hint_string == "") {
+ return "void*";
+ } else {
+ return p_info.hint_string + "*";
+ }
+ }
+ if (p_info.type == Variant::INT && (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM)) {
+ return String("enum::") + String(p_info.class_name);
+ }
+ if (p_info.class_name != StringName()) {
+ return p_info.class_name;
+ }
+ if (p_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ return p_info.hint_string;
+ }
+ if (p_info.type == Variant::NIL && (p_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {
+ return "Variant";
+ }
+ if (p_info.type == Variant::NIL) {
+ return "void";
+ }
+ return Variant::get_type_name(p_info.type);
+}
+
+Dictionary NativeExtensionAPIDump::generate_extension_api() {
+ Dictionary api_dump;
+
+ {
+ //header
+ Dictionary header;
+ header["version_major"] = VERSION_MAJOR;
+ header["version_minor"] = VERSION_MINOR;
+#if VERSION_PATCH
+ header["version_patch"] = VERSION_PATCH;
+#else
+ header["version_patch"] = 0;
+#endif
+ header["version_status"] = VERSION_STATUS;
+ header["version_build"] = VERSION_BUILD;
+ header["version_full_name"] = VERSION_FULL_NAME;
+
+ api_dump["header"] = header;
+ }
+
+ const uint32_t vec3_elems = 3;
+ const uint32_t ptrsize_32 = 4;
+ const uint32_t ptrsize_64 = 8;
+ static const char *build_config_name[4] = { "float_32", "float_64", "double_32", "double_64" };
+
+ {
+ //type sizes
+ constexpr struct {
+ Variant::Type type;
+ uint32_t size_32_bits_real_float;
+ uint32_t size_64_bits_real_float;
+ uint32_t size_32_bits_real_double;
+ uint32_t size_64_bits_real_double;
+
+ // For compile-time size check.
+ constexpr uint32_t operator[](int index) const {
+ switch (index) {
+#ifndef REAL_T_IS_DOUBLE
+ case sizeof(uint32_t):
+ return size_32_bits_real_float;
+ case sizeof(uint64_t):
+ return size_64_bits_real_float;
+#else // REAL_T_IS_DOUBLE
+ case sizeof(uint32_t):
+ return size_32_bits_real_double;
+ case sizeof(uint64_t):
+ return size_64_bits_real_double;
+#endif
+ }
+ return -1;
+ }
+ } type_size_array[Variant::VARIANT_MAX + 1] = {
+ { Variant::NIL, 0, 0, 0, 0 },
+ { Variant::BOOL, sizeof(uint8_t), sizeof(uint8_t), sizeof(uint8_t), sizeof(uint8_t) },
+ { Variant::INT, sizeof(int64_t), sizeof(int64_t), sizeof(int64_t), sizeof(int64_t) },
+ { Variant::FLOAT, sizeof(double), sizeof(double), sizeof(double), sizeof(double) },
+ { Variant::STRING, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
+ { Variant::VECTOR2, 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) },
+ { Variant::VECTOR2I, 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) },
+ { Variant::RECT2, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) },
+ { Variant::RECT2I, 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t) },
+ { Variant::VECTOR3, vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) },
+ { Variant::VECTOR3I, 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t) },
+ { Variant::TRANSFORM2D, 6 * sizeof(float), 6 * sizeof(float), 6 * sizeof(double), 6 * sizeof(double) },
+ { Variant::PLANE, (vec3_elems + 1) * sizeof(float), (vec3_elems + 1) * sizeof(float), (vec3_elems + 1) * sizeof(double), (vec3_elems + 1) * sizeof(double) },
+ { Variant::QUATERNION, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) },
+ { Variant::AABB, (vec3_elems * 2) * sizeof(float), (vec3_elems * 2) * sizeof(float), (vec3_elems * 2) * sizeof(double), (vec3_elems * 2) * sizeof(double) },
+ { Variant::BASIS, (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(double), (vec3_elems * 3) * sizeof(double) },
+ { Variant::TRANSFORM3D, (vec3_elems * 4) * sizeof(float), (vec3_elems * 4) * sizeof(float), (vec3_elems * 4) * sizeof(double), (vec3_elems * 4) * sizeof(double) },
+ { Variant::COLOR, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(float) },
+ { Variant::STRING_NAME, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
+ { Variant::NODE_PATH, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
+ { Variant::RID, sizeof(uint64_t), sizeof(uint64_t), sizeof(uint64_t), sizeof(uint64_t) },
+ { Variant::OBJECT, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
+ { Variant::CALLABLE, sizeof(Callable), sizeof(Callable), sizeof(Callable), sizeof(Callable) }, // Hardcoded align.
+ { Variant::SIGNAL, sizeof(Signal), sizeof(Signal), sizeof(Signal), sizeof(Signal) }, // Hardcoded align.
+ { Variant::DICTIONARY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
+ { Variant::ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
+ { Variant::PACKED_BYTE_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
+ { Variant::PACKED_INT32_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
+ { Variant::PACKED_INT64_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
+ { Variant::PACKED_FLOAT32_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
+ { Variant::PACKED_FLOAT64_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
+ { Variant::PACKED_STRING_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
+ { Variant::PACKED_VECTOR2_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
+ { Variant::PACKED_VECTOR3_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
+ { Variant::PACKED_COLOR_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
+ { Variant::VARIANT_MAX, sizeof(uint64_t) + sizeof(float) * 4, sizeof(uint64_t) + sizeof(float) * 4, sizeof(uint64_t) + sizeof(double) * 4, sizeof(uint64_t) + sizeof(double) * 4 },
+ };
+
+ // Validate sizes at compile time for the current build configuration.
+ static_assert(type_size_array[Variant::BOOL][sizeof(void *)] == sizeof(GDNativeBool), "Size of bool mismatch");
+ static_assert(type_size_array[Variant::INT][sizeof(void *)] == sizeof(GDNativeInt), "Size of int mismatch");
+ static_assert(type_size_array[Variant::FLOAT][sizeof(void *)] == sizeof(double), "Size of float mismatch");
+ static_assert(type_size_array[Variant::STRING][sizeof(void *)] == sizeof(String), "Size of String mismatch");
+ static_assert(type_size_array[Variant::VECTOR2][sizeof(void *)] == sizeof(Vector2), "Size of Vector2 mismatch");
+ static_assert(type_size_array[Variant::VECTOR2I][sizeof(void *)] == sizeof(Vector2i), "Size of Vector2i mismatch");
+ static_assert(type_size_array[Variant::RECT2][sizeof(void *)] == sizeof(Rect2), "Size of Rect2 mismatch");
+ static_assert(type_size_array[Variant::RECT2I][sizeof(void *)] == sizeof(Rect2i), "Size of Rect2i mismatch");
+ static_assert(type_size_array[Variant::VECTOR3][sizeof(void *)] == sizeof(Vector3), "Size of Vector3 mismatch");
+ static_assert(type_size_array[Variant::VECTOR3I][sizeof(void *)] == sizeof(Vector3i), "Size of Vector3i mismatch");
+ static_assert(type_size_array[Variant::TRANSFORM2D][sizeof(void *)] == sizeof(Transform2D), "Size of Transform2D mismatch");
+ static_assert(type_size_array[Variant::PLANE][sizeof(void *)] == sizeof(Plane), "Size of Plane mismatch");
+ static_assert(type_size_array[Variant::QUATERNION][sizeof(void *)] == sizeof(Quaternion), "Size of Quaternion mismatch");
+ static_assert(type_size_array[Variant::AABB][sizeof(void *)] == sizeof(AABB), "Size of AABB mismatch");
+ static_assert(type_size_array[Variant::BASIS][sizeof(void *)] == sizeof(Basis), "Size of Basis mismatch");
+ static_assert(type_size_array[Variant::TRANSFORM3D][sizeof(void *)] == sizeof(Transform3D), "Size of Transform3D mismatch");
+ static_assert(type_size_array[Variant::COLOR][sizeof(void *)] == sizeof(Color), "Size of Color mismatch");
+ static_assert(type_size_array[Variant::STRING_NAME][sizeof(void *)] == sizeof(StringName), "Size of StringName mismatch");
+ static_assert(type_size_array[Variant::NODE_PATH][sizeof(void *)] == sizeof(NodePath), "Size of NodePath mismatch");
+ static_assert(type_size_array[Variant::RID][sizeof(void *)] == sizeof(RID), "Size of RID mismatch");
+ static_assert(type_size_array[Variant::OBJECT][sizeof(void *)] == sizeof(Object *), "Size of Object mismatch");
+ static_assert(type_size_array[Variant::CALLABLE][sizeof(void *)] == sizeof(Callable), "Size of Callable mismatch");
+ static_assert(type_size_array[Variant::SIGNAL][sizeof(void *)] == sizeof(Signal), "Size of Signal mismatch");
+ static_assert(type_size_array[Variant::DICTIONARY][sizeof(void *)] == sizeof(Dictionary), "Size of Dictionary mismatch");
+ static_assert(type_size_array[Variant::ARRAY][sizeof(void *)] == sizeof(Array), "Size of Array mismatch");
+ static_assert(type_size_array[Variant::PACKED_BYTE_ARRAY][sizeof(void *)] == sizeof(PackedByteArray), "Size of PackedByteArray mismatch");
+ static_assert(type_size_array[Variant::PACKED_INT32_ARRAY][sizeof(void *)] == sizeof(PackedInt32Array), "Size of PackedInt32Array mismatch");
+ static_assert(type_size_array[Variant::PACKED_INT64_ARRAY][sizeof(void *)] == sizeof(PackedInt64Array), "Size of PackedInt64Array mismatch");
+ static_assert(type_size_array[Variant::PACKED_FLOAT32_ARRAY][sizeof(void *)] == sizeof(PackedFloat32Array), "Size of PackedFloat32Array mismatch");
+ static_assert(type_size_array[Variant::PACKED_FLOAT64_ARRAY][sizeof(void *)] == sizeof(PackedFloat64Array), "Size of PackedFloat64Array mismatch");
+ static_assert(type_size_array[Variant::PACKED_STRING_ARRAY][sizeof(void *)] == sizeof(PackedStringArray), "Size of PackedStringArray mismatch");
+ static_assert(type_size_array[Variant::PACKED_VECTOR2_ARRAY][sizeof(void *)] == sizeof(PackedVector2Array), "Size of PackedVector2Array mismatch");
+ static_assert(type_size_array[Variant::PACKED_VECTOR3_ARRAY][sizeof(void *)] == sizeof(PackedVector3Array), "Size of PackedVector3Array mismatch");
+ static_assert(type_size_array[Variant::PACKED_COLOR_ARRAY][sizeof(void *)] == sizeof(PackedColorArray), "Size of PackedColorArray mismatch");
+ static_assert(type_size_array[Variant::VARIANT_MAX][sizeof(void *)] == sizeof(Variant), "Size of Variant mismatch");
+
+ Array core_type_sizes;
+
+ for (int i = 0; i < 4; i++) {
+ Dictionary d;
+ d["build_configuration"] = build_config_name[i];
+ Array sizes;
+ for (int j = 0; j <= Variant::VARIANT_MAX; j++) {
+ Variant::Type t = type_size_array[j].type;
+ String name = t == Variant::VARIANT_MAX ? String("Variant") : Variant::get_type_name(t);
+ Dictionary d2;
+ d2["name"] = name;
+ uint32_t size;
+ switch (i) {
+ case 0:
+ size = type_size_array[j].size_32_bits_real_float;
+ break;
+ case 1:
+ size = type_size_array[j].size_64_bits_real_float;
+ break;
+ case 2:
+ size = type_size_array[j].size_32_bits_real_double;
+ break;
+ case 3:
+ size = type_size_array[j].size_64_bits_real_double;
+ break;
+ }
+ d2["size"] = size;
+ sizes.push_back(d2);
+ }
+ d["sizes"] = sizes;
+ core_type_sizes.push_back(d);
+ }
+ api_dump["builtin_class_sizes"] = core_type_sizes;
+ }
+
+ {
+ // Member offsets sizes.
+ struct {
+ Variant::Type type;
+ const char *member;
+ uint32_t offset_32_bits_real_float;
+ uint32_t offset_64_bits_real_float;
+ uint32_t offset_32_bits_real_double;
+ uint32_t offset_64_bits_real_double;
+ } member_offset_array[] = {
+ { Variant::VECTOR2, "x", 0, 0, 0, 0 },
+ { Variant::VECTOR2, "y", sizeof(float), sizeof(float), sizeof(double), sizeof(double) },
+ { Variant::VECTOR2I, "x", 0, 0, 0, 0 },
+ { Variant::VECTOR2I, "y", sizeof(int32_t), sizeof(int32_t), sizeof(int32_t), sizeof(int32_t) },
+ { Variant::RECT2, "position", 0, 0, 0, 0 },
+ { Variant::RECT2, "size", 2 * sizeof(Vector2), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) },
+ { Variant::RECT2I, "position", 0, 0, 0, 0 },
+ { Variant::RECT2I, "size", 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) },
+ { Variant::VECTOR3, "x", 0, 0, 0, 0 },
+ { Variant::VECTOR3, "y", sizeof(float), sizeof(float), sizeof(double), sizeof(double) },
+ { Variant::VECTOR3, "z", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) },
+ { Variant::VECTOR3I, "x", 0, 0, 0, 0 },
+ { Variant::VECTOR3I, "y", sizeof(int32_t), sizeof(int32_t), sizeof(int32_t), sizeof(int32_t) },
+ { Variant::VECTOR3I, "z", 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) },
+ { Variant::TRANSFORM2D, "x", 0, 0, 0, 0 },
+ { Variant::TRANSFORM2D, "y", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) },
+ { Variant::TRANSFORM2D, "origin", 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) },
+ { Variant::PLANE, "normal", 0, 0, 0, 0 },
+ { Variant::PLANE, "d", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) },
+ { Variant::QUATERNION, "x", 0, 0, 0, 0 },
+ { Variant::QUATERNION, "y", sizeof(float), sizeof(float), sizeof(double), sizeof(double) },
+ { Variant::QUATERNION, "z", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) },
+ { Variant::QUATERNION, "w", 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(double), 3 * sizeof(double) },
+ { Variant::AABB, "position", 0, 0, 0, 0 },
+ { Variant::AABB, "size", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) },
+ // Remember that basis vectors are flipped!
+ { Variant::BASIS, "x", 0, 0, 0, 0 },
+ { Variant::BASIS, "y", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) },
+ { Variant::BASIS, "z", vec3_elems * 2 * sizeof(float), vec3_elems * 2 * sizeof(float), vec3_elems * 2 * sizeof(double), vec3_elems * 2 * sizeof(double) },
+ { Variant::TRANSFORM3D, "basis", 0, 0, 0, 0 },
+ { Variant::TRANSFORM3D, "origin", (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(double), (vec3_elems * 3) * sizeof(double) },
+ { Variant::COLOR, "x", 0, 0, 0, 0 },
+ { Variant::COLOR, "y", sizeof(float), sizeof(float), sizeof(float), sizeof(float) },
+ { Variant::COLOR, "z", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(float) },
+ { Variant::COLOR, "w", 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(float) },
+ { Variant::NIL, nullptr, 0, 0, 0, 0 },
+ };
+
+ Array core_type_member_offsets;
+
+ for (int i = 0; i < 4; i++) {
+ Dictionary d;
+ d["build_configuration"] = build_config_name[i];
+ Array type_offsets;
+ uint32_t idx = 0;
+
+ Variant::Type last_type = Variant::NIL;
+
+ Dictionary d2;
+ Array members;
+
+ while (true) {
+ Variant::Type t = member_offset_array[idx].type;
+ if (t != last_type) {
+ if (last_type != Variant::NIL) {
+ d2["members"] = members;
+ type_offsets.push_back(d2);
+ }
+ if (t == Variant::NIL) {
+ break;
+ }
+
+ String name = t == Variant::VARIANT_MAX ? String("Variant") : Variant::get_type_name(t);
+ d2 = Dictionary();
+ members = Array();
+ d2["name"] = name;
+ last_type = t;
+ }
+ Dictionary d3;
+ uint32_t offset;
+ switch (i) {
+ case 0:
+ offset = member_offset_array[idx].offset_32_bits_real_float;
+ break;
+ case 1:
+ offset = member_offset_array[idx].offset_64_bits_real_float;
+ break;
+ case 2:
+ offset = member_offset_array[idx].offset_32_bits_real_double;
+ break;
+ case 3:
+ offset = member_offset_array[idx].offset_64_bits_real_double;
+ break;
+ }
+ d3["member"] = member_offset_array[idx].member;
+ d3["offset"] = offset;
+ members.push_back(d3);
+ idx++;
+ }
+ d["classes"] = type_offsets;
+ core_type_member_offsets.push_back(d);
+ }
+ api_dump["builtin_class_member_offsets"] = core_type_member_offsets;
+ }
+
+ {
+ // Global enums and constants.
+ Array constants;
+ Map<String, List<Pair<String, int>>> enum_list;
+
+ for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
+ int value = CoreConstants::get_global_constant_value(i);
+ String enum_name = CoreConstants::get_global_constant_enum(i);
+ String name = CoreConstants::get_global_constant_name(i);
+ if (enum_name != String()) {
+ enum_list[enum_name].push_back(Pair<String, int>(name, value));
+ } else {
+ Dictionary d;
+ d["name"] = name;
+ d["value"] = value;
+ constants.push_back(d);
+ }
+ }
+
+ api_dump["global_constants"] = constants;
+
+ Array enums;
+ for (Map<String, List<Pair<String, int>>>::Element *E = enum_list.front(); E; E = E->next()) {
+ Dictionary d1;
+ d1["name"] = E->key();
+ Array values;
+ for (const Pair<String, int> &F : E->get()) {
+ Dictionary d2;
+ d2["name"] = F.first;
+ d2["value"] = F.second;
+ values.push_back(d2);
+ }
+ d1["values"] = values;
+ enums.push_back(d1);
+ }
+
+ api_dump["global_enums"] = enums;
+ }
+ {
+ Array utility_funcs;
+
+ List<StringName> utility_func_names;
+ Variant::get_utility_function_list(&utility_func_names);
+
+ for (const StringName &name : utility_func_names) {
+ Dictionary func;
+ func["name"] = String(name);
+ if (Variant::has_utility_function_return_value(name)) {
+ Variant::Type rt = Variant::get_utility_function_return_type(name);
+ func["return_type"] = rt == Variant::NIL ? String("Variant") : Variant::get_type_name(rt);
+ }
+ switch (Variant::get_utility_function_type(name)) {
+ case Variant::UTILITY_FUNC_TYPE_MATH:
+ func["category"] = "math";
+ break;
+ case Variant::UTILITY_FUNC_TYPE_RANDOM:
+ func["category"] = "random";
+ break;
+ case Variant::UTILITY_FUNC_TYPE_GENERAL:
+ func["category"] = "general";
+ break;
+ }
+ bool vararg = Variant::is_utility_function_vararg(name);
+ func["is_vararg"] = Variant::is_utility_function_vararg(name);
+ func["hash"] = Variant::get_utility_function_hash(name);
+ Array arguments;
+ int argcount = Variant::get_utility_function_argument_count(name);
+ for (int i = 0; i < argcount; i++) {
+ Dictionary arg;
+ String argname = vararg ? "arg" + itos(i + 1) : Variant::get_utility_function_argument_name(name, i);
+ arg["name"] = argname;
+ Variant::Type argtype = Variant::get_utility_function_argument_type(name, i);
+ arg["type"] = argtype == Variant::NIL ? String("Variant") : Variant::get_type_name(argtype);
+ //no default value support in utility functions
+ arguments.push_back(arg);
+ }
+
+ if (arguments.size()) {
+ func["arguments"] = arguments;
+ }
+
+ utility_funcs.push_back(func);
+ }
+
+ api_dump["utility_functions"] = utility_funcs;
+ }
+
+ {
+ // builtin types
+
+ Array builtins;
+
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ if (i == Variant::OBJECT) {
+ continue;
+ }
+
+ Variant::Type type = Variant::Type(i);
+
+ Dictionary d;
+ d["name"] = Variant::get_type_name(type);
+ if (Variant::has_indexing(type)) {
+ Variant::Type index_type = Variant::get_indexed_element_type(type);
+ d["indexing_return_type"] = index_type == Variant::NIL ? String("Variant") : Variant::get_type_name(index_type);
+ }
+
+ d["is_keyed"] = Variant::ValidatedKeyedSetter(type);
+
+ {
+ //members
+ Array members;
+
+ List<StringName> member_names;
+ Variant::get_member_list(type, &member_names);
+ for (const StringName &member_name : member_names) {
+ Dictionary d2;
+ d2["name"] = String(member_name);
+ d2["type"] = Variant::get_type_name(Variant::get_member_type(type, member_name));
+ members.push_back(d2);
+ }
+ if (members.size()) {
+ d["members"] = members;
+ }
+ }
+ {
+ //constants
+ Array constants;
+
+ List<StringName> constant_names;
+ Variant::get_constants_for_type(type, &constant_names);
+ for (const StringName &constant_name : constant_names) {
+ Dictionary d2;
+ d2["name"] = String(constant_name);
+ Variant constant = Variant::get_constant_value(type, constant_name);
+ d2["type"] = Variant::get_type_name(constant.get_type());
+ d2["value"] = constant.get_construct_string();
+ constants.push_back(d2);
+ }
+ if (constants.size()) {
+ d["constants"] = constants;
+ }
+ }
+ {
+ //operators
+ Array operators;
+
+ for (int j = 0; j < Variant::VARIANT_MAX; j++) {
+ for (int k = 0; k < Variant::OP_MAX; k++) {
+ Variant::Type rt = Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j));
+ if (rt != Variant::NIL) {
+ Dictionary d2;
+ d2["name"] = Variant::get_operator_name(Variant::Operator(k));
+ if (k != Variant::OP_NEGATE && k != Variant::OP_POSITIVE && k != Variant::OP_NOT && k != Variant::OP_BIT_NEGATE) {
+ d2["right_type"] = Variant::get_type_name(Variant::Type(j));
+ }
+ d2["return_type"] = Variant::get_type_name(Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j)));
+ operators.push_back(d2);
+ }
+ }
+ }
+ if (operators.size()) {
+ d["operators"] = operators;
+ }
+ }
+ {
+ //methods
+ Array methods;
+
+ List<StringName> method_names;
+ Variant::get_builtin_method_list(type, &method_names);
+ for (const StringName &method_name : method_names) {
+ Dictionary d2;
+ d2["name"] = String(method_name);
+ if (Variant::has_builtin_method_return_value(type, method_name)) {
+ Variant::Type ret_type = Variant::get_builtin_method_return_type(type, method_name);
+ d2["return_type"] = ret_type == Variant::NIL ? String("Variant") : Variant::get_type_name(ret_type);
+ }
+ d2["is_vararg"] = Variant::is_builtin_method_vararg(type, method_name);
+ d2["is_const"] = Variant::is_builtin_method_const(type, method_name);
+ d2["is_static"] = Variant::is_builtin_method_static(type, method_name);
+ d2["hash"] = Variant::get_builtin_method_hash(type, method_name);
+
+ Vector<Variant> default_args = Variant::get_builtin_method_default_arguments(type, method_name);
+
+ Array arguments;
+ int argcount = Variant::get_builtin_method_argument_count(type, method_name);
+ for (int j = 0; j < argcount; j++) {
+ Dictionary d3;
+ d3["name"] = Variant::get_builtin_method_argument_name(type, method_name, j);
+ Variant::Type argtype = Variant::get_builtin_method_argument_type(type, method_name, j);
+ d3["type"] = argtype == Variant::NIL ? String("Variant") : Variant::get_type_name(argtype);
+
+ if (j >= (argcount - default_args.size())) {
+ int dargidx = j - (argcount - default_args.size());
+ d3["default_value"] = default_args[dargidx].get_construct_string();
+ }
+ arguments.push_back(d3);
+ }
+
+ if (arguments.size()) {
+ d2["arguments"] = arguments;
+ }
+
+ methods.push_back(d2);
+ }
+ if (methods.size()) {
+ d["methods"] = methods;
+ }
+ }
+ {
+ //constructors
+ Array constructors;
+
+ for (int j = 0; j < Variant::get_constructor_count(type); j++) {
+ Dictionary d2;
+ d2["index"] = j;
+
+ Array arguments;
+ int argcount = Variant::get_constructor_argument_count(type, j);
+ for (int k = 0; k < argcount; k++) {
+ Dictionary d3;
+ d3["name"] = Variant::get_constructor_argument_name(type, j, k);
+ d3["type"] = Variant::get_type_name(Variant::get_constructor_argument_type(type, j, k));
+ arguments.push_back(d3);
+ }
+ if (arguments.size()) {
+ d2["arguments"] = arguments;
+ }
+ constructors.push_back(d2);
+ }
+
+ if (constructors.size()) {
+ d["constructors"] = constructors;
+ }
+ }
+ {
+ //destructor
+ d["has_destructor"] = Variant::has_destructor(type);
+ }
+
+ builtins.push_back(d);
+ }
+
+ api_dump["builtin_classes"] = builtins;
+ }
+
+ {
+ // classes
+ Array classes;
+
+ List<StringName> class_list;
+
+ ClassDB::get_class_list(&class_list);
+
+ class_list.sort_custom<StringName::AlphCompare>();
+
+ for (const StringName &class_name : class_list) {
+ Dictionary d;
+ d["name"] = String(class_name);
+ d["is_refcounted"] = ClassDB::is_parent_class(class_name, "RefCounted");
+ d["is_instantiable"] = ClassDB::can_instantiate(class_name);
+ StringName parent_class = ClassDB::get_parent_class(class_name);
+ if (parent_class != StringName()) {
+ d["inherits"] = String(parent_class);
+ }
+
+ {
+ ClassDB::APIType api = ClassDB::get_api_type(class_name);
+ static const char *api_type[5] = { "core", "editor", "extension", "editor_extension" };
+ d["api_type"] = api_type[api];
+ }
+
+ {
+ //constants
+ Array constants;
+ List<String> constant_list;
+ ClassDB::get_integer_constant_list(class_name, &constant_list, true);
+ for (const String &F : constant_list) {
+ StringName enum_name = ClassDB::get_integer_constant_enum(class_name, F);
+ if (enum_name != StringName()) {
+ continue; //enums will be handled on their own
+ }
+
+ Dictionary d2;
+ d2["name"] = String(F);
+ d2["value"] = ClassDB::get_integer_constant(class_name, F);
+
+ constants.push_back(d2);
+ }
+
+ if (constants.size()) {
+ d["constants"] = constants;
+ }
+ }
+ {
+ //enum
+ Array enums;
+ List<StringName> enum_list;
+ ClassDB::get_enum_list(class_name, &enum_list, true);
+ for (const StringName &F : enum_list) {
+ Dictionary d2;
+ d2["name"] = String(F);
+
+ Array values;
+ List<StringName> enum_constant_list;
+ ClassDB::get_enum_constants(class_name, F, &enum_constant_list, true);
+ for (List<StringName>::Element *G = enum_constant_list.front(); G; G = G->next()) {
+ Dictionary d3;
+ d3["name"] = String(G->get());
+ d3["value"] = ClassDB::get_integer_constant(class_name, G->get());
+ values.push_back(d3);
+ }
+
+ d2["values"] = values;
+
+ enums.push_back(d2);
+ }
+
+ if (enums.size()) {
+ d["enums"] = enums;
+ }
+ }
+ {
+ //methods
+ Array methods;
+ List<MethodInfo> method_list;
+ ClassDB::get_method_list(class_name, &method_list, true);
+ for (const MethodInfo &F : method_list) {
+ StringName method_name = F.name;
+ if ((F.flags & METHOD_FLAG_VIRTUAL) && !(F.flags & METHOD_FLAG_OBJECT_CORE)) {
+ //virtual method
+ const MethodInfo &mi = F;
+ Dictionary d2;
+ d2["name"] = String(method_name);
+ d2["is_const"] = (F.flags & METHOD_FLAG_CONST) ? true : false;
+ d2["is_vararg"] = false;
+ d2["is_virtual"] = true;
+ // virtual functions have no hash since no MethodBind is involved
+ bool has_return = mi.return_val.type != Variant::NIL || (mi.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT);
+ Array arguments;
+ for (int i = (has_return ? -1 : 0); i < mi.arguments.size(); i++) {
+ PropertyInfo pinfo = i == -1 ? mi.return_val : mi.arguments[i];
+ Dictionary d3;
+
+ if (i >= 0) {
+ d3["name"] = pinfo.name;
+ }
+
+ d3["type"] = get_type_name(pinfo);
+
+ if (i == -1) {
+ d2["return_value"] = d3;
+ } else {
+ arguments.push_back(d3);
+ }
+ }
+
+ if (arguments.size()) {
+ d2["arguments"] = arguments;
+ }
+
+ methods.push_back(d2);
+
+ } else if (F.name.begins_with("_")) {
+ //hidden method, ignore
+
+ } else {
+ Dictionary d2;
+ d2["name"] = String(method_name);
+
+ MethodBind *method = ClassDB::get_method(class_name, method_name);
+ if (!method) {
+ continue;
+ }
+
+ d2["is_const"] = method->is_const();
+ d2["is_vararg"] = method->is_vararg();
+ d2["is_virtual"] = false;
+ d2["hash"] = method->get_hash();
+
+ Vector<Variant> default_args = method->get_default_arguments();
+
+ Array arguments;
+ for (int i = (method->has_return() ? -1 : 0); i < method->get_argument_count(); i++) {
+ PropertyInfo pinfo = i == -1 ? method->get_return_info() : method->get_argument_info(i);
+ Dictionary d3;
+
+ if (i >= 0) {
+ d3["name"] = pinfo.name;
+ }
+ d3["type"] = get_type_name(pinfo);
+
+ if (method->get_argument_meta(i) > 0) {
+ static const char *argmeta[11] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double" };
+ d3["meta"] = argmeta[method->get_argument_meta(i)];
+ }
+
+ if (i >= 0 && i >= (method->get_argument_count() - default_args.size())) {
+ int dargidx = i - (method->get_argument_count() - default_args.size());
+ d3["default_value"] = default_args[dargidx].get_construct_string();
+ }
+
+ if (i == -1) {
+ d2["return_value"] = d3;
+ } else {
+ arguments.push_back(d3);
+ }
+ }
+
+ if (arguments.size()) {
+ d2["arguments"] = arguments;
+ }
+
+ methods.push_back(d2);
+ }
+ }
+
+ if (methods.size()) {
+ d["methods"] = methods;
+ }
+ }
+
+ {
+ //signals
+ Array signals;
+ List<MethodInfo> signal_list;
+ ClassDB::get_signal_list(class_name, &signal_list, true);
+ for (const MethodInfo &F : signal_list) {
+ StringName signal_name = F.name;
+ Dictionary d2;
+ d2["name"] = String(signal_name);
+
+ Array arguments;
+
+ for (int i = 0; i < F.arguments.size(); i++) {
+ Dictionary d3;
+ d3["name"] = F.arguments[i].name;
+ d3["type"] = get_type_name(F.arguments[i]);
+ arguments.push_back(d3);
+ }
+ if (arguments.size()) {
+ d2["arguments"] = arguments;
+ }
+
+ signals.push_back(d2);
+ }
+
+ if (signals.size()) {
+ d["signals"] = signals;
+ }
+ }
+ {
+ //properties
+ Array properties;
+ List<PropertyInfo> property_list;
+ ClassDB::get_property_list(class_name, &property_list, true);
+ for (const PropertyInfo &F : property_list) {
+ if (F.usage & PROPERTY_USAGE_CATEGORY || F.usage & PROPERTY_USAGE_GROUP || F.usage & PROPERTY_USAGE_SUBGROUP) {
+ continue; //not real properties
+ }
+ if (F.name.begins_with("_")) {
+ continue; //hidden property
+ }
+ StringName property_name = F.name;
+ Dictionary d2;
+ d2["type"] = get_type_name(F);
+ d2["name"] = String(property_name);
+ d2["setter"] = ClassDB::get_property_setter(class_name, F.name);
+ d2["getter"] = ClassDB::get_property_getter(class_name, F.name);
+ d2["index"] = ClassDB::get_property_index(class_name, F.name);
+ properties.push_back(d2);
+ }
+
+ if (properties.size()) {
+ d["properties"] = properties;
+ }
+ }
+
+ classes.push_back(d);
+ }
+
+ api_dump["classes"] = classes;
+ }
+
+ {
+ // singletons
+
+ Array singletons;
+ List<Engine::Singleton> singleton_list;
+ Engine::get_singleton()->get_singletons(&singleton_list);
+
+ for (const Engine::Singleton &s : singleton_list) {
+ Dictionary d;
+ d["name"] = s.name;
+ if (s.class_name != StringName()) {
+ d["type"] = String(s.class_name);
+ } else {
+ d["type"] = String(s.ptr->get_class());
+ }
+ singletons.push_back(d);
+ }
+
+ if (singletons.size()) {
+ api_dump["singletons"] = singletons;
+ }
+ }
+
+ {
+ Array native_structures;
+
+ {
+ Dictionary d;
+ d["name"] = "AudioFrame";
+ d["format"] = "float left,float right";
+
+ native_structures.push_back(d);
+ }
+
+ api_dump["native_structures"] = native_structures;
+ }
+
+ return api_dump;
+}
+
+void NativeExtensionAPIDump::generate_extension_json_file(const String &p_path) {
+ Dictionary api = generate_extension_api();
+ Ref<JSON> json;
+ json.instantiate();
+
+ String text = json->stringify(api, "\t", false);
+ FileAccessRef fa = FileAccess::open(p_path, FileAccess::WRITE);
+ CharString cs = text.ascii();
+ fa->store_buffer((const uint8_t *)cs.ptr(), cs.length());
+ fa->close();
+}
+#endif
diff --git a/core/extension/extension_api_dump.h b/core/extension/extension_api_dump.h
new file mode 100644
index 0000000000..a7825c10a9
--- /dev/null
+++ b/core/extension/extension_api_dump.h
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* extension_api_dump.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef API_DUMP_H
+#define API_DUMP_H
+
+#include "core/extension/native_extension.h"
+
+#ifdef TOOLS_ENABLED
+
+class NativeExtensionAPIDump {
+public:
+ static Dictionary generate_extension_api();
+ static void generate_extension_json_file(const String &p_path);
+};
+#endif
+
+#endif // API_DUMP_H
diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp
new file mode 100644
index 0000000000..77229a784d
--- /dev/null
+++ b/core/extension/gdnative_interface.cpp
@@ -0,0 +1,1012 @@
+/*************************************************************************/
+/* gdnative_interface.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "gdnative_interface.h"
+
+#include "core/config/engine.h"
+#include "core/object/class_db.h"
+#include "core/os/memory.h"
+#include "core/variant/variant.h"
+#include "core/version.h"
+
+// Memory Functions
+static void *gdnative_alloc(size_t p_size) {
+ return memalloc(p_size);
+}
+
+static void *gdnative_realloc(void *p_mem, size_t p_size) {
+ return memrealloc(p_mem, p_size);
+}
+
+static void gdnative_free(void *p_mem) {
+ memfree(p_mem);
+}
+
+// Helper print functions.
+static void gdnative_print_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) {
+ _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_ERROR);
+}
+static void gdnative_print_warning(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) {
+ _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_WARNING);
+}
+static void gdnative_print_script_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) {
+ _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_SCRIPT);
+}
+
+// Variant functions
+
+static void gdnative_variant_new_copy(GDNativeVariantPtr r_dest, const GDNativeVariantPtr p_src) {
+ memnew_placement(reinterpret_cast<Variant *>(r_dest), Variant(*reinterpret_cast<Variant *>(p_src)));
+}
+static void gdnative_variant_new_nil(GDNativeVariantPtr r_dest) {
+ memnew_placement(reinterpret_cast<Variant *>(r_dest), Variant);
+}
+static void gdnative_variant_destroy(GDNativeVariantPtr p_self) {
+ reinterpret_cast<Variant *>(p_self)->~Variant();
+}
+
+// variant type
+
+static void gdnative_variant_call(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argcount, GDNativeVariantPtr r_return, GDNativeCallError *r_error) {
+ Variant *self = (Variant *)p_self;
+ const StringName *method = (const StringName *)p_method;
+ const Variant **args = (const Variant **)p_args;
+ Variant ret;
+ Callable::CallError error;
+ self->call(*method, args, p_argcount, ret, error);
+ memnew_placement(r_return, Variant(ret));
+
+ if (r_error) {
+ r_error->error = (GDNativeCallErrorType)(error.error);
+ r_error->argument = error.argument;
+ r_error->expected = error.expected;
+ }
+}
+
+static void gdnative_variant_call_static(GDNativeVariantType p_type, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argcount, GDNativeVariantPtr r_return, GDNativeCallError *r_error) {
+ Variant::Type type = (Variant::Type)p_type;
+ const StringName *method = (const StringName *)p_method;
+ const Variant **args = (const Variant **)p_args;
+ Variant ret;
+ Callable::CallError error;
+ Variant::call_static(type, *method, args, p_argcount, ret, error);
+ memnew_placement(r_return, Variant(ret));
+
+ if (r_error) {
+ r_error->error = (GDNativeCallErrorType)error.error;
+ r_error->argument = error.argument;
+ r_error->expected = error.expected;
+ }
+}
+
+static void gdnative_variant_evaluate(GDNativeVariantOperator p_op, const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_return, GDNativeBool *r_valid) {
+ Variant::Operator op = (Variant::Operator)p_op;
+ const Variant *a = (const Variant *)p_a;
+ const Variant *b = (const Variant *)p_b;
+ Variant *ret = (Variant *)r_return;
+ bool valid;
+ Variant::evaluate(op, *a, *b, *ret, valid);
+ *r_valid = valid;
+}
+
+static void gdnative_variant_set(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid) {
+ Variant *self = (Variant *)p_self;
+ const Variant *key = (const Variant *)p_key;
+ const Variant *value = (const Variant *)p_value;
+
+ bool valid;
+ self->set(*key, *value, &valid);
+ *r_valid = valid;
+}
+
+static void gdnative_variant_set_named(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid) {
+ Variant *self = (Variant *)p_self;
+ const StringName *key = (const StringName *)p_key;
+ const Variant *value = (const Variant *)p_value;
+
+ bool valid;
+ self->set_named(*key, *value, valid);
+ *r_valid = valid;
+}
+
+static void gdnative_variant_set_keyed(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid) {
+ Variant *self = (Variant *)p_self;
+ const Variant *key = (const Variant *)p_key;
+ const Variant *value = (const Variant *)p_value;
+
+ bool valid;
+ self->set_keyed(*key, *value, valid);
+ *r_valid = valid;
+}
+
+static void gdnative_variant_set_indexed(GDNativeVariantPtr p_self, GDNativeInt p_index, const GDNativeVariantPtr p_value, GDNativeBool *r_valid, GDNativeBool *r_oob) {
+ Variant *self = (Variant *)p_self;
+ const Variant *value = (const Variant *)p_value;
+
+ bool valid;
+ bool oob;
+ self->set_indexed(p_index, value, valid, oob);
+ *r_valid = valid;
+ *r_oob = oob;
+}
+
+static void gdnative_variant_get(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) {
+ const Variant *self = (const Variant *)p_self;
+ const Variant *key = (const Variant *)p_key;
+
+ bool valid;
+ memnew_placement(r_ret, Variant(self->get(*key, &valid)));
+ *r_valid = valid;
+}
+
+static void gdnative_variant_get_named(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) {
+ const Variant *self = (const Variant *)p_self;
+ const StringName *key = (const StringName *)p_key;
+
+ bool valid;
+ memnew_placement(r_ret, Variant(self->get_named(*key, valid)));
+ *r_valid = valid;
+}
+
+static void gdnative_variant_get_keyed(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) {
+ const Variant *self = (const Variant *)p_self;
+ const Variant *key = (const Variant *)p_key;
+
+ bool valid;
+ memnew_placement(r_ret, Variant(self->get_keyed(*key, valid)));
+ *r_valid = valid;
+}
+
+static void gdnative_variant_get_indexed(const GDNativeVariantPtr p_self, GDNativeInt p_index, GDNativeVariantPtr r_ret, GDNativeBool *r_valid, GDNativeBool *r_oob) {
+ const Variant *self = (const Variant *)p_self;
+
+ bool valid;
+ bool oob;
+ memnew_placement(r_ret, Variant(self->get_indexed(p_index, valid, oob)));
+ *r_valid = valid;
+ *r_oob = oob;
+}
+
+/// Iteration.
+static GDNativeBool gdnative_variant_iter_init(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid) {
+ const Variant *self = (const Variant *)p_self;
+ Variant *iter = (Variant *)r_iter;
+
+ bool valid;
+ bool ret = self->iter_init(*iter, valid);
+ *r_valid = valid;
+ return ret;
+}
+
+static GDNativeBool gdnative_variant_iter_next(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid) {
+ const Variant *self = (const Variant *)p_self;
+ Variant *iter = (Variant *)r_iter;
+
+ bool valid;
+ bool ret = self->iter_next(*iter, valid);
+ *r_valid = valid;
+ return ret;
+}
+
+static void gdnative_variant_iter_get(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) {
+ const Variant *self = (const Variant *)p_self;
+ Variant *iter = (Variant *)r_iter;
+
+ bool valid;
+ memnew_placement(r_ret, Variant(self->iter_next(*iter, valid)));
+ *r_valid = valid;
+}
+
+/// Variant functions.
+static GDNativeBool gdnative_variant_hash_compare(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other) {
+ const Variant *self = (const Variant *)p_self;
+ const Variant *other = (const Variant *)p_other;
+ return self->hash_compare(*other);
+}
+
+static GDNativeBool gdnative_variant_booleanize(const GDNativeVariantPtr p_self) {
+ const Variant *self = (const Variant *)p_self;
+ return self->booleanize();
+}
+
+static void gdnative_variant_blend(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst) {
+ const Variant *a = (const Variant *)p_a;
+ const Variant *b = (const Variant *)p_b;
+ memnew_placement(r_dst, Variant);
+ Variant::blend(*a, *b, p_c, *(Variant *)r_dst);
+}
+
+static void gdnative_variant_interpolate(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst) {
+ const Variant *a = (const Variant *)p_a;
+ const Variant *b = (const Variant *)p_b;
+ memnew_placement(r_dst, Variant);
+ Variant::interpolate(*a, *b, p_c, *(Variant *)r_dst);
+}
+
+static void gdnative_variant_duplicate(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep) {
+ const Variant *self = (const Variant *)p_self;
+ memnew_placement(r_ret, Variant(self->duplicate(p_deep)));
+}
+
+static void gdnative_variant_stringify(const GDNativeVariantPtr p_self, GDNativeStringPtr r_ret) {
+ const Variant *self = (const Variant *)p_self;
+ memnew_placement(r_ret, String(*self));
+}
+
+static GDNativeVariantType gdnative_variant_get_type(const GDNativeVariantPtr p_self) {
+ const Variant *self = (const Variant *)p_self;
+ return (GDNativeVariantType)self->get_type();
+}
+
+static GDNativeBool gdnative_variant_has_method(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method) {
+ const Variant *self = (const Variant *)p_self;
+ const StringName *method = (const StringName *)p_method;
+ return self->has_method(*method);
+}
+
+static GDNativeBool gdnative_variant_has_member(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member) {
+ return Variant::has_member((Variant::Type)p_type, *((const StringName *)p_member));
+}
+
+static GDNativeBool gdnative_variant_has_key(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeBool *r_valid) {
+ const Variant *self = (const Variant *)p_self;
+ const Variant *key = (const Variant *)p_key;
+ bool valid;
+ bool ret = self->has_key(*key, valid);
+ *r_valid = valid;
+ return ret;
+}
+
+static void gdnative_variant_get_type_name(GDNativeVariantType p_type, GDNativeStringPtr r_ret) {
+ String name = Variant::get_type_name((Variant::Type)p_type);
+ memnew_placement(r_ret, String(name));
+}
+
+static GDNativeBool gdnative_variant_can_convert(GDNativeVariantType p_from, GDNativeVariantType p_to) {
+ return Variant::can_convert((Variant::Type)p_from, (Variant::Type)p_to);
+}
+
+static GDNativeBool gdnative_variant_can_convert_strict(GDNativeVariantType p_from, GDNativeVariantType p_to) {
+ return Variant::can_convert_strict((Variant::Type)p_from, (Variant::Type)p_to);
+}
+
+// Variant interaction.
+static GDNativeVariantFromTypeConstructorFunc gdnative_get_variant_from_type_constructor(GDNativeVariantType p_type) {
+ switch (p_type) {
+ case GDNATIVE_VARIANT_TYPE_BOOL:
+ return VariantTypeConstructor<bool>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_INT:
+ return VariantTypeConstructor<int64_t>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_FLOAT:
+ return VariantTypeConstructor<double>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_STRING:
+ return VariantTypeConstructor<String>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_VECTOR2:
+ return VariantTypeConstructor<Vector2>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_VECTOR2I:
+ return VariantTypeConstructor<Vector2i>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_RECT2:
+ return VariantTypeConstructor<Rect2>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_RECT2I:
+ return VariantTypeConstructor<Rect2i>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_VECTOR3:
+ return VariantTypeConstructor<Vector3>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_VECTOR3I:
+ return VariantTypeConstructor<Vector3i>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_TRANSFORM2D:
+ return VariantTypeConstructor<Transform2D>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_PLANE:
+ return VariantTypeConstructor<Plane>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_QUATERNION:
+ return VariantTypeConstructor<Quaternion>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_AABB:
+ return VariantTypeConstructor<AABB>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_BASIS:
+ return VariantTypeConstructor<Basis>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_TRANSFORM3D:
+ return VariantTypeConstructor<Transform3D>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_COLOR:
+ return VariantTypeConstructor<Color>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_STRING_NAME:
+ return VariantTypeConstructor<StringName>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_NODE_PATH:
+ return VariantTypeConstructor<NodePath>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_RID:
+ return VariantTypeConstructor<RID>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_OBJECT:
+ return VariantTypeConstructor<Object *>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_CALLABLE:
+ return VariantTypeConstructor<Callable>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_SIGNAL:
+ return VariantTypeConstructor<Signal>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_DICTIONARY:
+ return VariantTypeConstructor<Dictionary>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_ARRAY:
+ return VariantTypeConstructor<Array>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_PACKED_BYTE_ARRAY:
+ return VariantTypeConstructor<PackedByteArray>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_PACKED_INT32_ARRAY:
+ return VariantTypeConstructor<PackedInt32Array>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_PACKED_INT64_ARRAY:
+ return VariantTypeConstructor<PackedInt64Array>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_PACKED_FLOAT32_ARRAY:
+ return VariantTypeConstructor<PackedFloat32Array>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_PACKED_FLOAT64_ARRAY:
+ return VariantTypeConstructor<PackedFloat64Array>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_PACKED_STRING_ARRAY:
+ return VariantTypeConstructor<PackedStringArray>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_PACKED_VECTOR2_ARRAY:
+ return VariantTypeConstructor<PackedVector2Array>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_PACKED_VECTOR3_ARRAY:
+ return VariantTypeConstructor<PackedVector3Array>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_PACKED_COLOR_ARRAY:
+ return VariantTypeConstructor<PackedColorArray>::variant_from_type;
+ case GDNATIVE_VARIANT_TYPE_NIL:
+ case GDNATIVE_VARIANT_TYPE_VARIANT_MAX:
+ ERR_FAIL_V_MSG(nullptr, "Getting Variant conversion function with invalid type");
+ }
+ ERR_FAIL_V_MSG(nullptr, "Getting Variant conversion function with invalid type");
+}
+
+static GDNativeTypeFromVariantConstructorFunc gdnative_get_type_from_variant_constructor(GDNativeVariantType p_type) {
+ switch (p_type) {
+ case GDNATIVE_VARIANT_TYPE_BOOL:
+ return VariantTypeConstructor<bool>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_INT:
+ return VariantTypeConstructor<int64_t>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_FLOAT:
+ return VariantTypeConstructor<double>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_STRING:
+ return VariantTypeConstructor<String>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_VECTOR2:
+ return VariantTypeConstructor<Vector2>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_VECTOR2I:
+ return VariantTypeConstructor<Vector2i>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_RECT2:
+ return VariantTypeConstructor<Rect2>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_RECT2I:
+ return VariantTypeConstructor<Rect2i>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_VECTOR3:
+ return VariantTypeConstructor<Vector3>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_VECTOR3I:
+ return VariantTypeConstructor<Vector3i>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_TRANSFORM2D:
+ return VariantTypeConstructor<Transform2D>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_PLANE:
+ return VariantTypeConstructor<Plane>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_QUATERNION:
+ return VariantTypeConstructor<Quaternion>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_AABB:
+ return VariantTypeConstructor<AABB>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_BASIS:
+ return VariantTypeConstructor<Basis>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_TRANSFORM3D:
+ return VariantTypeConstructor<Transform3D>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_COLOR:
+ return VariantTypeConstructor<Color>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_STRING_NAME:
+ return VariantTypeConstructor<StringName>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_NODE_PATH:
+ return VariantTypeConstructor<NodePath>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_RID:
+ return VariantTypeConstructor<RID>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_OBJECT:
+ return VariantTypeConstructor<Object *>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_CALLABLE:
+ return VariantTypeConstructor<Callable>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_SIGNAL:
+ return VariantTypeConstructor<Signal>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_DICTIONARY:
+ return VariantTypeConstructor<Dictionary>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_ARRAY:
+ return VariantTypeConstructor<Array>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_PACKED_BYTE_ARRAY:
+ return VariantTypeConstructor<PackedByteArray>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_PACKED_INT32_ARRAY:
+ return VariantTypeConstructor<PackedInt32Array>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_PACKED_INT64_ARRAY:
+ return VariantTypeConstructor<PackedInt64Array>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_PACKED_FLOAT32_ARRAY:
+ return VariantTypeConstructor<PackedFloat32Array>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_PACKED_FLOAT64_ARRAY:
+ return VariantTypeConstructor<PackedFloat64Array>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_PACKED_STRING_ARRAY:
+ return VariantTypeConstructor<PackedStringArray>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_PACKED_VECTOR2_ARRAY:
+ return VariantTypeConstructor<PackedVector2Array>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_PACKED_VECTOR3_ARRAY:
+ return VariantTypeConstructor<PackedVector3Array>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_PACKED_COLOR_ARRAY:
+ return VariantTypeConstructor<PackedColorArray>::type_from_variant;
+ case GDNATIVE_VARIANT_TYPE_NIL:
+ case GDNATIVE_VARIANT_TYPE_VARIANT_MAX:
+ ERR_FAIL_V_MSG(nullptr, "Getting Variant conversion function with invalid type");
+ }
+ ERR_FAIL_V_MSG(nullptr, "Getting Variant conversion function with invalid type");
+}
+
+// ptrcalls
+static GDNativePtrOperatorEvaluator gdnative_variant_get_ptr_operator_evaluator(GDNativeVariantOperator p_operator, GDNativeVariantType p_type_a, GDNativeVariantType p_type_b) {
+ return (GDNativePtrOperatorEvaluator)Variant::get_ptr_operator_evaluator(Variant::Operator(p_operator), Variant::Type(p_type_a), Variant::Type(p_type_b));
+}
+static GDNativePtrBuiltInMethod gdnative_variant_get_ptr_builtin_method(GDNativeVariantType p_type, const char *p_method, GDNativeInt p_hash) {
+ StringName method = p_method;
+ uint32_t hash = Variant::get_builtin_method_hash(Variant::Type(p_type), method);
+ if (hash != p_hash) {
+ ERR_PRINT_ONCE("Error getting method " + String(method) + ", hash mismatch.");
+ return nullptr;
+ }
+
+ return (GDNativePtrBuiltInMethod)Variant::get_ptr_builtin_method(Variant::Type(p_type), method);
+}
+static GDNativePtrConstructor gdnative_variant_get_ptr_constructor(GDNativeVariantType p_type, int32_t p_constructor) {
+ return (GDNativePtrConstructor)Variant::get_ptr_constructor(Variant::Type(p_type), p_constructor);
+}
+static GDNativePtrDestructor gdnative_variant_get_ptr_destructor(GDNativeVariantType p_type) {
+ return (GDNativePtrDestructor)Variant::get_ptr_destructor(Variant::Type(p_type));
+}
+static void gdnative_variant_construct(GDNativeVariantType p_type, GDNativeVariantPtr p_base, const GDNativeVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error) {
+ memnew_placement(p_base, Variant);
+
+ Callable::CallError error;
+ Variant::construct(Variant::Type(p_type), *(Variant *)p_base, (const Variant **)p_args, p_argument_count, error);
+
+ if (r_error) {
+ r_error->error = (GDNativeCallErrorType)(error.error);
+ r_error->argument = error.argument;
+ r_error->expected = error.expected;
+ }
+}
+static GDNativePtrSetter gdnative_variant_get_ptr_setter(GDNativeVariantType p_type, const char *p_member) {
+ return (GDNativePtrSetter)Variant::get_member_ptr_setter(Variant::Type(p_type), p_member);
+}
+static GDNativePtrGetter gdnative_variant_get_ptr_getter(GDNativeVariantType p_type, const char *p_member) {
+ return (GDNativePtrGetter)Variant::get_member_ptr_getter(Variant::Type(p_type), p_member);
+}
+static GDNativePtrIndexedSetter gdnative_variant_get_ptr_indexed_setter(GDNativeVariantType p_type) {
+ return (GDNativePtrIndexedSetter)Variant::get_member_ptr_indexed_setter(Variant::Type(p_type));
+}
+static GDNativePtrIndexedGetter gdnative_variant_get_ptr_indexed_getter(GDNativeVariantType p_type) {
+ return (GDNativePtrIndexedGetter)Variant::get_member_ptr_indexed_getter(Variant::Type(p_type));
+}
+static GDNativePtrKeyedSetter gdnative_variant_get_ptr_keyed_setter(GDNativeVariantType p_type) {
+ return (GDNativePtrKeyedSetter)Variant::get_member_ptr_keyed_setter(Variant::Type(p_type));
+}
+static GDNativePtrKeyedGetter gdnative_variant_get_ptr_keyed_getter(GDNativeVariantType p_type) {
+ return (GDNativePtrKeyedGetter)Variant::get_member_ptr_keyed_getter(Variant::Type(p_type));
+}
+static GDNativePtrKeyedChecker gdnative_variant_get_ptr_keyed_checker(GDNativeVariantType p_type) {
+ return (GDNativePtrKeyedChecker)Variant::get_member_ptr_keyed_checker(Variant::Type(p_type));
+}
+static void gdnative_variant_get_constant_value(GDNativeVariantType p_type, const char *p_constant, GDNativeVariantPtr r_ret) {
+ memnew_placement(r_ret, Variant(Variant::get_constant_value(Variant::Type(p_type), p_constant)));
+}
+static GDNativePtrUtilityFunction gdnative_variant_get_ptr_utility_function(const char *p_function, GDNativeInt p_hash) {
+ StringName function = p_function;
+ uint32_t hash = Variant::get_utility_function_hash(function);
+ if (hash != p_hash) {
+ ERR_PRINT_ONCE("Error getting utility function " + String(function) + ", hash mismatch.");
+ return nullptr;
+ }
+ return (GDNativePtrUtilityFunction)Variant::get_ptr_utility_function(function);
+}
+
+//string helpers
+
+static void gdnative_string_new_with_latin1_chars(GDNativeStringPtr r_dest, const char *p_contents) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ *dest = String(p_contents);
+}
+
+static void gdnative_string_new_with_utf8_chars(GDNativeStringPtr r_dest, const char *p_contents) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ dest->parse_utf8(p_contents);
+}
+
+static void gdnative_string_new_with_utf16_chars(GDNativeStringPtr r_dest, const char16_t *p_contents) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ dest->parse_utf16(p_contents);
+}
+
+static void gdnative_string_new_with_utf32_chars(GDNativeStringPtr r_dest, const char32_t *p_contents) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ *dest = String((const char32_t *)p_contents);
+}
+
+static void gdnative_string_new_with_wide_chars(GDNativeStringPtr r_dest, const wchar_t *p_contents) {
+ String *dest = (String *)r_dest;
+ if (sizeof(wchar_t) == 2) {
+ // wchar_t is 16 bit, parse.
+ memnew_placement(dest, String);
+ dest->parse_utf16((const char16_t *)p_contents);
+ } else {
+ // wchar_t is 32 bit, copy.
+ memnew_placement(dest, String);
+ *dest = String((const char32_t *)p_contents);
+ }
+}
+
+static void gdnative_string_new_with_latin1_chars_and_len(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ *dest = String(p_contents, p_size);
+}
+
+static void gdnative_string_new_with_utf8_chars_and_len(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ dest->parse_utf8(p_contents, p_size);
+}
+
+static void gdnative_string_new_with_utf16_chars_and_len(GDNativeStringPtr r_dest, const char16_t *p_contents, const GDNativeInt p_size) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ dest->parse_utf16(p_contents, p_size);
+}
+
+static void gdnative_string_new_with_utf32_chars_and_len(GDNativeStringPtr r_dest, const char32_t *p_contents, const GDNativeInt p_size) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ *dest = String((const char32_t *)p_contents, p_size);
+}
+
+static void gdnative_string_new_with_wide_chars_and_len(GDNativeStringPtr r_dest, const wchar_t *p_contents, const GDNativeInt p_size) {
+ String *dest = (String *)r_dest;
+ if (sizeof(wchar_t) == 2) {
+ // wchar_t is 16 bit, parse.
+ memnew_placement(dest, String);
+ dest->parse_utf16((const char16_t *)p_contents, p_size);
+ } else {
+ // wchar_t is 32 bit, copy.
+ memnew_placement(dest, String);
+ *dest = String((const char32_t *)p_contents, p_size);
+ }
+}
+
+static GDNativeInt gdnative_string_to_latin1_chars(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length) {
+ String *self = (String *)p_self;
+ CharString cs = self->ascii(true);
+ GDNativeInt len = cs.length();
+ if (r_text) {
+ const char *s_text = cs.ptr();
+ for (GDNativeInt i = 0; i < MIN(len, p_max_write_length); i++) {
+ r_text[i] = s_text[i];
+ }
+ }
+ return len;
+}
+static GDNativeInt gdnative_string_to_utf8_chars(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length) {
+ String *self = (String *)p_self;
+ CharString cs = self->utf8();
+ GDNativeInt len = cs.length();
+ if (r_text) {
+ const char *s_text = cs.ptr();
+ for (GDNativeInt i = 0; i < MIN(len, p_max_write_length); i++) {
+ r_text[i] = s_text[i];
+ }
+ }
+ return len;
+}
+static GDNativeInt gdnative_string_to_utf16_chars(const GDNativeStringPtr p_self, char16_t *r_text, GDNativeInt p_max_write_length) {
+ String *self = (String *)p_self;
+ Char16String cs = self->utf16();
+ GDNativeInt len = cs.length();
+ if (r_text) {
+ const char16_t *s_text = cs.ptr();
+ for (GDNativeInt i = 0; i < MIN(len, p_max_write_length); i++) {
+ r_text[i] = s_text[i];
+ }
+ }
+ return len;
+}
+static GDNativeInt gdnative_string_to_utf32_chars(const GDNativeStringPtr p_self, char32_t *r_text, GDNativeInt p_max_write_length) {
+ String *self = (String *)p_self;
+ GDNativeInt len = self->length();
+ if (r_text) {
+ const char32_t *s_text = self->ptr();
+ for (GDNativeInt i = 0; i < MIN(len, p_max_write_length); i++) {
+ r_text[i] = s_text[i];
+ }
+ }
+ return len;
+}
+static GDNativeInt gdnative_string_to_wide_chars(const GDNativeStringPtr p_self, wchar_t *r_text, GDNativeInt p_max_write_length) {
+ if (sizeof(wchar_t) == 4) {
+ return gdnative_string_to_utf32_chars(p_self, (char32_t *)r_text, p_max_write_length);
+ } else {
+ return gdnative_string_to_utf16_chars(p_self, (char16_t *)r_text, p_max_write_length);
+ }
+}
+
+static char32_t *gdnative_string_operator_index(GDNativeStringPtr p_self, GDNativeInt p_index) {
+ String *self = (String *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->length() + 1, nullptr);
+ return &self->ptrw()[p_index];
+}
+
+static const char32_t *gdnative_string_operator_index_const(const GDNativeStringPtr p_self, GDNativeInt p_index) {
+ const String *self = (const String *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->length() + 1, nullptr);
+ return &self->ptr()[p_index];
+}
+
+/* Packed array functions */
+
+static uint8_t *gdnative_packed_byte_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) {
+ PackedByteArray *self = (PackedByteArray *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return &self->ptrw()[p_index];
+}
+
+static const uint8_t *gdnative_packed_byte_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+ const PackedByteArray *self = (const PackedByteArray *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return &self->ptr()[p_index];
+}
+
+static GDNativeTypePtr gdnative_packed_color_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) {
+ PackedColorArray *self = (PackedColorArray *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return (GDNativeTypePtr)&self->ptrw()[p_index];
+}
+
+static GDNativeTypePtr gdnative_packed_color_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+ const PackedColorArray *self = (const PackedColorArray *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return (GDNativeTypePtr)&self->ptr()[p_index];
+}
+
+static float *gdnative_packed_float32_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) {
+ PackedFloat32Array *self = (PackedFloat32Array *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return &self->ptrw()[p_index];
+}
+
+static const float *gdnative_packed_float32_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+ const PackedFloat32Array *self = (const PackedFloat32Array *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return &self->ptr()[p_index];
+}
+
+static double *gdnative_packed_float64_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) {
+ PackedFloat64Array *self = (PackedFloat64Array *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return &self->ptrw()[p_index];
+}
+
+static const double *gdnative_packed_float64_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+ const PackedFloat64Array *self = (const PackedFloat64Array *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return &self->ptr()[p_index];
+}
+
+static int32_t *gdnative_packed_int32_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) {
+ PackedInt32Array *self = (PackedInt32Array *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return &self->ptrw()[p_index];
+}
+
+static const int32_t *gdnative_packed_int32_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+ const PackedInt32Array *self = (const PackedInt32Array *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return &self->ptr()[p_index];
+}
+
+static int64_t *gdnative_packed_int64_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) {
+ PackedInt64Array *self = (PackedInt64Array *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return &self->ptrw()[p_index];
+}
+
+static const int64_t *gdnative_packed_int64_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+ const PackedInt64Array *self = (const PackedInt64Array *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return &self->ptr()[p_index];
+}
+
+static GDNativeStringPtr gdnative_packed_string_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) {
+ PackedStringArray *self = (PackedStringArray *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return (GDNativeStringPtr)&self->ptrw()[p_index];
+}
+
+static GDNativeStringPtr gdnative_packed_string_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+ const PackedStringArray *self = (const PackedStringArray *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return (GDNativeStringPtr)&self->ptr()[p_index];
+}
+
+static GDNativeTypePtr gdnative_packed_vector2_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) {
+ PackedVector2Array *self = (PackedVector2Array *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return (GDNativeTypePtr)&self->ptrw()[p_index];
+}
+
+static GDNativeTypePtr gdnative_packed_vector2_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+ const PackedVector2Array *self = (const PackedVector2Array *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return (GDNativeTypePtr)&self->ptr()[p_index];
+}
+
+static GDNativeTypePtr gdnative_packed_vector3_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) {
+ PackedVector3Array *self = (PackedVector3Array *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return (GDNativeTypePtr)&self->ptrw()[p_index];
+}
+
+static GDNativeTypePtr gdnative_packed_vector3_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+ const PackedVector3Array *self = (const PackedVector3Array *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return (GDNativeTypePtr)&self->ptr()[p_index];
+}
+
+/* OBJECT API */
+
+static void gdnative_object_method_bind_call(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error) {
+ MethodBind *mb = (MethodBind *)p_method_bind;
+ Object *o = (Object *)p_instance;
+ const Variant **args = (const Variant **)p_args;
+ Callable::CallError error;
+
+ Variant ret = mb->call(o, args, p_arg_count, error);
+ memnew_placement(r_return, Variant(ret));
+
+ if (r_error) {
+ r_error->error = (GDNativeCallErrorType)(error.error);
+ r_error->argument = error.argument;
+ r_error->expected = error.expected;
+ }
+}
+
+static void gdnative_object_method_bind_ptrcall(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr p_ret) {
+ MethodBind *mb = (MethodBind *)p_method_bind;
+ Object *o = (Object *)p_instance;
+ mb->ptrcall(o, (const void **)p_args, p_ret);
+}
+
+static void gdnative_object_destroy(GDNativeObjectPtr p_o) {
+ memdelete((Object *)p_o);
+}
+
+static GDNativeObjectPtr gdnative_global_get_singleton(const char *p_name) {
+ return (GDNativeObjectPtr)Engine::get_singleton()->get_singleton_object(String(p_name));
+}
+
+static void *gdnative_object_get_instance_binding(GDNativeObjectPtr p_instance, void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks) {
+ Object *o = (Object *)p_instance;
+ return o->get_instance_binding(p_token, p_callbacks);
+}
+
+static void gdnative_object_set_instance_binding(GDNativeObjectPtr p_instance, void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks) {
+ Object *o = (Object *)p_instance;
+ o->set_instance_binding(p_token, p_binding, p_callbacks);
+}
+
+static GDNativeObjectPtr gdnative_object_get_instance_from_id(GDObjectInstanceID p_instance_id) {
+ return (GDNativeObjectPtr)ObjectDB::get_instance(ObjectID(p_instance_id));
+}
+
+static GDNativeObjectPtr gdnative_object_cast_to(const GDNativeObjectPtr p_object, void *p_class_tag) {
+ if (!p_object) {
+ return nullptr;
+ }
+ Object *o = (Object *)p_object;
+
+ return o->is_class_ptr(p_class_tag) ? (GDNativeObjectPtr)o : (GDNativeObjectPtr) nullptr;
+}
+
+static GDObjectInstanceID gdnative_object_get_instance_id(const GDNativeObjectPtr p_object) {
+ const Object *o = (const Object *)p_object;
+ return (GDObjectInstanceID)o->get_instance_id();
+}
+
+static GDNativeMethodBindPtr gdnative_classdb_get_method_bind(const char *p_classname, const char *p_methodname, GDNativeInt p_hash) {
+ MethodBind *mb = ClassDB::get_method(StringName(p_classname), StringName(p_methodname));
+ ERR_FAIL_COND_V(!mb, nullptr);
+ if (mb->get_hash() != p_hash) {
+ ERR_PRINT_ONCE("Hash mismatch for method '" + String(p_classname) + "." + String(p_methodname) + "'.");
+ return nullptr;
+ }
+ // MethodBind *mb = ClassDB::get_method("Node", "get_name");
+ return (GDNativeMethodBindPtr)mb;
+}
+
+static GDNativeClassConstructor gdnative_classdb_get_constructor(const char *p_classname) {
+ ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(StringName(p_classname));
+ if (class_info) {
+ return (GDNativeClassConstructor)class_info->creation_func;
+ }
+ return nullptr;
+}
+
+static void *gdnative_classdb_get_class_tag(const char *p_classname) {
+ ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(p_classname);
+ return class_info ? class_info->class_ptr : nullptr;
+}
+
+void gdnative_setup_interface(GDNativeInterface *p_interface) {
+ GDNativeInterface &gdni = *p_interface;
+
+ gdni.version_major = VERSION_MAJOR;
+ gdni.version_minor = VERSION_MINOR;
+#if VERSION_PATCH
+ gdni.version_patch = VERSION_PATCH;
+#else
+ gdni.version_patch = 0;
+#endif
+ gdni.version_string = VERSION_FULL_NAME;
+
+ /* GODOT CORE */
+
+ gdni.mem_alloc = gdnative_alloc;
+ gdni.mem_realloc = gdnative_realloc;
+ gdni.mem_free = gdnative_free;
+
+ gdni.print_error = gdnative_print_error;
+ gdni.print_warning = gdnative_print_warning;
+ gdni.print_script_error = gdnative_print_script_error;
+
+ /* GODOT VARIANT */
+
+ // variant general
+ gdni.variant_new_copy = gdnative_variant_new_copy;
+ gdni.variant_new_nil = gdnative_variant_new_nil;
+ gdni.variant_destroy = gdnative_variant_destroy;
+
+ gdni.variant_call = gdnative_variant_call;
+ gdni.variant_call_static = gdnative_variant_call_static;
+ gdni.variant_evaluate = gdnative_variant_evaluate;
+ gdni.variant_set = gdnative_variant_set;
+ gdni.variant_set_named = gdnative_variant_set_named;
+ gdni.variant_set_keyed = gdnative_variant_set_keyed;
+ gdni.variant_set_indexed = gdnative_variant_set_indexed;
+ gdni.variant_get = gdnative_variant_get;
+ gdni.variant_get_named = gdnative_variant_get_named;
+ gdni.variant_get_keyed = gdnative_variant_get_keyed;
+ gdni.variant_get_indexed = gdnative_variant_get_indexed;
+ gdni.variant_iter_init = gdnative_variant_iter_init;
+ gdni.variant_iter_next = gdnative_variant_iter_next;
+ gdni.variant_iter_get = gdnative_variant_iter_get;
+ gdni.variant_hash_compare = gdnative_variant_hash_compare;
+ gdni.variant_booleanize = gdnative_variant_booleanize;
+ gdni.variant_blend = gdnative_variant_blend;
+ gdni.variant_interpolate = gdnative_variant_interpolate;
+ gdni.variant_duplicate = gdnative_variant_duplicate;
+ gdni.variant_stringify = gdnative_variant_stringify;
+
+ gdni.variant_get_type = gdnative_variant_get_type;
+ gdni.variant_has_method = gdnative_variant_has_method;
+ gdni.variant_has_member = gdnative_variant_has_member;
+ gdni.variant_has_key = gdnative_variant_has_key;
+ gdni.variant_get_type_name = gdnative_variant_get_type_name;
+ gdni.variant_can_convert = gdnative_variant_can_convert;
+ gdni.variant_can_convert_strict = gdnative_variant_can_convert_strict;
+
+ gdni.get_variant_from_type_constructor = gdnative_get_variant_from_type_constructor;
+ gdni.get_variant_to_type_constructor = gdnative_get_type_from_variant_constructor;
+
+ // ptrcalls.
+
+ gdni.variant_get_ptr_operator_evaluator = gdnative_variant_get_ptr_operator_evaluator;
+ gdni.variant_get_ptr_builtin_method = gdnative_variant_get_ptr_builtin_method;
+ gdni.variant_get_ptr_constructor = gdnative_variant_get_ptr_constructor;
+ gdni.variant_get_ptr_destructor = gdnative_variant_get_ptr_destructor;
+ gdni.variant_construct = gdnative_variant_construct;
+ gdni.variant_get_ptr_setter = gdnative_variant_get_ptr_setter;
+ gdni.variant_get_ptr_getter = gdnative_variant_get_ptr_getter;
+ gdni.variant_get_ptr_indexed_setter = gdnative_variant_get_ptr_indexed_setter;
+ gdni.variant_get_ptr_indexed_getter = gdnative_variant_get_ptr_indexed_getter;
+ gdni.variant_get_ptr_keyed_setter = gdnative_variant_get_ptr_keyed_setter;
+ gdni.variant_get_ptr_keyed_getter = gdnative_variant_get_ptr_keyed_getter;
+ gdni.variant_get_ptr_keyed_checker = gdnative_variant_get_ptr_keyed_checker;
+ gdni.variant_get_constant_value = gdnative_variant_get_constant_value;
+ gdni.variant_get_ptr_utility_function = gdnative_variant_get_ptr_utility_function;
+
+ // extra utilities
+
+ gdni.string_new_with_latin1_chars = gdnative_string_new_with_latin1_chars;
+ gdni.string_new_with_utf8_chars = gdnative_string_new_with_utf8_chars;
+ gdni.string_new_with_utf16_chars = gdnative_string_new_with_utf16_chars;
+ gdni.string_new_with_utf32_chars = gdnative_string_new_with_utf32_chars;
+ gdni.string_new_with_wide_chars = gdnative_string_new_with_wide_chars;
+ gdni.string_new_with_latin1_chars_and_len = gdnative_string_new_with_latin1_chars_and_len;
+ gdni.string_new_with_utf8_chars_and_len = gdnative_string_new_with_utf8_chars_and_len;
+ gdni.string_new_with_utf16_chars_and_len = gdnative_string_new_with_utf16_chars_and_len;
+ gdni.string_new_with_utf32_chars_and_len = gdnative_string_new_with_utf32_chars_and_len;
+ gdni.string_new_with_wide_chars_and_len = gdnative_string_new_with_wide_chars_and_len;
+ gdni.string_to_latin1_chars = gdnative_string_to_latin1_chars;
+ gdni.string_to_utf8_chars = gdnative_string_to_utf8_chars;
+ gdni.string_to_utf16_chars = gdnative_string_to_utf16_chars;
+ gdni.string_to_utf32_chars = gdnative_string_to_utf32_chars;
+ gdni.string_to_wide_chars = gdnative_string_to_wide_chars;
+ gdni.string_operator_index = gdnative_string_operator_index;
+ gdni.string_operator_index_const = gdnative_string_operator_index_const;
+
+ /* Packed array functions */
+
+ gdni.packed_byte_array_operator_index = gdnative_packed_byte_array_operator_index;
+ gdni.packed_byte_array_operator_index_const = gdnative_packed_byte_array_operator_index_const;
+
+ gdni.packed_color_array_operator_index = gdnative_packed_color_array_operator_index;
+ gdni.packed_color_array_operator_index_const = gdnative_packed_color_array_operator_index_const;
+
+ gdni.packed_float32_array_operator_index = gdnative_packed_float32_array_operator_index;
+ gdni.packed_float32_array_operator_index_const = gdnative_packed_float32_array_operator_index_const;
+ gdni.packed_float64_array_operator_index = gdnative_packed_float64_array_operator_index;
+ gdni.packed_float64_array_operator_index_const = gdnative_packed_float64_array_operator_index_const;
+
+ gdni.packed_int32_array_operator_index = gdnative_packed_int32_array_operator_index;
+ gdni.packed_int32_array_operator_index_const = gdnative_packed_int32_array_operator_index_const;
+ gdni.packed_int64_array_operator_index = gdnative_packed_int64_array_operator_index;
+ gdni.packed_int64_array_operator_index_const = gdnative_packed_int64_array_operator_index_const;
+
+ gdni.packed_string_array_operator_index = gdnative_packed_string_array_operator_index;
+ gdni.packed_string_array_operator_index_const = gdnative_packed_string_array_operator_index_const;
+
+ gdni.packed_vector2_array_operator_index = gdnative_packed_vector2_array_operator_index;
+ gdni.packed_vector2_array_operator_index_const = gdnative_packed_vector2_array_operator_index_const;
+ gdni.packed_vector3_array_operator_index = gdnative_packed_vector3_array_operator_index;
+ gdni.packed_vector3_array_operator_index_const = gdnative_packed_vector3_array_operator_index_const;
+
+ /* OBJECT */
+
+ gdni.object_method_bind_call = gdnative_object_method_bind_call;
+ gdni.object_method_bind_ptrcall = gdnative_object_method_bind_ptrcall;
+ gdni.object_destroy = gdnative_object_destroy;
+ gdni.global_get_singleton = gdnative_global_get_singleton;
+ gdni.object_get_instance_binding = gdnative_object_get_instance_binding;
+ gdni.object_set_instance_binding = gdnative_object_set_instance_binding;
+
+ gdni.object_cast_to = gdnative_object_cast_to;
+ gdni.object_get_instance_from_id = gdnative_object_get_instance_from_id;
+ gdni.object_get_instance_id = gdnative_object_get_instance_id;
+
+ /* CLASSDB */
+
+ gdni.classdb_get_constructor = gdnative_classdb_get_constructor;
+ gdni.classdb_get_method_bind = gdnative_classdb_get_method_bind;
+ gdni.classdb_get_class_tag = gdnative_classdb_get_class_tag;
+
+ /* CLASSDB EXTENSION */
+
+ //these are filled by implementation, since it will want to keep track of registered classes
+ gdni.classdb_register_extension_class = nullptr;
+ gdni.classdb_register_extension_class_method = nullptr;
+ gdni.classdb_register_extension_class_integer_constant = nullptr;
+ gdni.classdb_register_extension_class_property = nullptr;
+ gdni.classdb_register_extension_class_property_group = nullptr;
+ gdni.classdb_register_extension_class_property_subgroup = nullptr;
+ gdni.classdb_register_extension_class_signal = nullptr;
+ gdni.classdb_unregister_extension_class = nullptr;
+}
diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h
new file mode 100644
index 0000000000..13aa273bc5
--- /dev/null
+++ b/core/extension/gdnative_interface.h
@@ -0,0 +1,479 @@
+/*************************************************************************/
+/* gdnative_interface.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GDNATIVE_INTERFACE_H
+#define GDNATIVE_INTERFACE_H
+
+/* This is a C class header, you can copy it and use it directly in your own binders.
+ * Together with the JSON file, you should be able to generate any binder.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#ifndef __cplusplus
+typedef uint32_t char32_t;
+typedef uint16_t char16_t;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* VARIANT TYPES */
+
+typedef enum {
+ GDNATIVE_VARIANT_TYPE_NIL,
+
+ /* atomic types */
+ GDNATIVE_VARIANT_TYPE_BOOL,
+ GDNATIVE_VARIANT_TYPE_INT,
+ GDNATIVE_VARIANT_TYPE_FLOAT,
+ GDNATIVE_VARIANT_TYPE_STRING,
+
+ /* math types */
+ GDNATIVE_VARIANT_TYPE_VECTOR2,
+ GDNATIVE_VARIANT_TYPE_VECTOR2I,
+ GDNATIVE_VARIANT_TYPE_RECT2,
+ GDNATIVE_VARIANT_TYPE_RECT2I,
+ GDNATIVE_VARIANT_TYPE_VECTOR3,
+ GDNATIVE_VARIANT_TYPE_VECTOR3I,
+ GDNATIVE_VARIANT_TYPE_TRANSFORM2D,
+ GDNATIVE_VARIANT_TYPE_PLANE,
+ GDNATIVE_VARIANT_TYPE_QUATERNION,
+ GDNATIVE_VARIANT_TYPE_AABB,
+ GDNATIVE_VARIANT_TYPE_BASIS,
+ GDNATIVE_VARIANT_TYPE_TRANSFORM3D,
+
+ /* misc types */
+ GDNATIVE_VARIANT_TYPE_COLOR,
+ GDNATIVE_VARIANT_TYPE_STRING_NAME,
+ GDNATIVE_VARIANT_TYPE_NODE_PATH,
+ GDNATIVE_VARIANT_TYPE_RID,
+ GDNATIVE_VARIANT_TYPE_OBJECT,
+ GDNATIVE_VARIANT_TYPE_CALLABLE,
+ GDNATIVE_VARIANT_TYPE_SIGNAL,
+ GDNATIVE_VARIANT_TYPE_DICTIONARY,
+ GDNATIVE_VARIANT_TYPE_ARRAY,
+
+ /* typed arrays */
+ GDNATIVE_VARIANT_TYPE_PACKED_BYTE_ARRAY,
+ GDNATIVE_VARIANT_TYPE_PACKED_INT32_ARRAY,
+ GDNATIVE_VARIANT_TYPE_PACKED_INT64_ARRAY,
+ GDNATIVE_VARIANT_TYPE_PACKED_FLOAT32_ARRAY,
+ GDNATIVE_VARIANT_TYPE_PACKED_FLOAT64_ARRAY,
+ GDNATIVE_VARIANT_TYPE_PACKED_STRING_ARRAY,
+ GDNATIVE_VARIANT_TYPE_PACKED_VECTOR2_ARRAY,
+ GDNATIVE_VARIANT_TYPE_PACKED_VECTOR3_ARRAY,
+ GDNATIVE_VARIANT_TYPE_PACKED_COLOR_ARRAY,
+
+ GDNATIVE_VARIANT_TYPE_VARIANT_MAX
+} GDNativeVariantType;
+
+typedef enum {
+ /* comparison */
+ GDNATIVE_VARIANT_OP_EQUAL,
+ GDNATIVE_VARIANT_OP_NOT_EQUAL,
+ GDNATIVE_VARIANT_OP_LESS,
+ GDNATIVE_VARIANT_OP_LESS_EQUAL,
+ GDNATIVE_VARIANT_OP_GREATER,
+ GDNATIVE_VARIANT_OP_GREATER_EQUAL,
+ /* mathematic */
+ GDNATIVE_VARIANT_OP_ADD,
+ GDNATIVE_VARIANT_OP_SUBTRACT,
+ GDNATIVE_VARIANT_OP_MULTIPLY,
+ GDNATIVE_VARIANT_OP_DIVIDE,
+ GDNATIVE_VARIANT_OP_NEGATE,
+ GDNATIVE_VARIANT_OP_POSITIVE,
+ GDNATIVE_VARIANT_OP_MODULE,
+ /* bitwise */
+ GDNATIVE_VARIANT_OP_SHIFT_LEFT,
+ GDNATIVE_VARIANT_OP_SHIFT_RIGHT,
+ GDNATIVE_VARIANT_OP_BIT_AND,
+ GDNATIVE_VARIANT_OP_BIT_OR,
+ GDNATIVE_VARIANT_OP_BIT_XOR,
+ GDNATIVE_VARIANT_OP_BIT_NEGATE,
+ /* logic */
+ GDNATIVE_VARIANT_OP_AND,
+ GDNATIVE_VARIANT_OP_OR,
+ GDNATIVE_VARIANT_OP_XOR,
+ GDNATIVE_VARIANT_OP_NOT,
+ /* containment */
+ GDNATIVE_VARIANT_OP_IN,
+ GDNATIVE_VARIANT_OP_MAX
+
+} GDNativeVariantOperator;
+
+typedef void *GDNativeVariantPtr;
+typedef void *GDNativeStringNamePtr;
+typedef void *GDNativeStringPtr;
+typedef void *GDNativeObjectPtr;
+typedef void *GDNativeTypePtr;
+typedef void *GDNativeMethodBindPtr;
+typedef int64_t GDNativeInt;
+typedef uint8_t GDNativeBool;
+typedef uint64_t GDObjectInstanceID;
+
+/* VARIANT DATA I/O */
+
+typedef enum {
+ GDNATIVE_CALL_OK,
+ GDNATIVE_CALL_ERROR_INVALID_METHOD,
+ GDNATIVE_CALL_ERROR_INVALID_ARGUMENT, /* expected is variant type */
+ GDNATIVE_CALL_ERROR_TOO_MANY_ARGUMENTS, /* expected is number of arguments */
+ GDNATIVE_CALL_ERROR_TOO_FEW_ARGUMENTS, /* expected is number of arguments */
+ GDNATIVE_CALL_ERROR_INSTANCE_IS_NULL,
+
+} GDNativeCallErrorType;
+
+typedef struct {
+ GDNativeCallErrorType error;
+ int32_t argument;
+ int32_t expected;
+} GDNativeCallError;
+
+typedef void (*GDNativeVariantFromTypeConstructorFunc)(GDNativeVariantPtr, GDNativeTypePtr);
+typedef void (*GDNativeTypeFromVariantConstructorFunc)(GDNativeTypePtr, GDNativeVariantPtr);
+typedef void (*GDNativePtrOperatorEvaluator)(const GDNativeTypePtr p_left, const GDNativeTypePtr p_right, GDNativeTypePtr r_result);
+typedef void (*GDNativePtrBuiltInMethod)(GDNativeTypePtr p_base, const GDNativeTypePtr *p_args, GDNativeTypePtr r_return, int p_argument_count);
+typedef void (*GDNativePtrConstructor)(GDNativeTypePtr p_base, const GDNativeTypePtr *p_args);
+typedef void (*GDNativePtrDestructor)(GDNativeTypePtr p_base);
+typedef void (*GDNativePtrSetter)(GDNativeTypePtr p_base, const GDNativeTypePtr p_value);
+typedef void (*GDNativePtrGetter)(const GDNativeTypePtr p_base, GDNativeTypePtr r_value);
+typedef void (*GDNativePtrIndexedSetter)(GDNativeTypePtr p_base, GDNativeInt p_index, const GDNativeTypePtr p_value);
+typedef void (*GDNativePtrIndexedGetter)(const GDNativeTypePtr p_base, GDNativeInt p_index, GDNativeTypePtr r_value);
+typedef void (*GDNativePtrKeyedSetter)(GDNativeTypePtr p_base, const GDNativeTypePtr p_key, const GDNativeTypePtr p_value);
+typedef void (*GDNativePtrKeyedGetter)(const GDNativeTypePtr p_base, const GDNativeTypePtr p_key, GDNativeTypePtr r_value);
+typedef uint32_t (*GDNativePtrKeyedChecker)(const GDNativeVariantPtr p_base, const GDNativeVariantPtr p_key);
+typedef void (*GDNativePtrUtilityFunction)(GDNativeTypePtr r_return, const GDNativeTypePtr *p_arguments, int p_argument_count);
+
+typedef GDNativeObjectPtr (*GDNativeClassConstructor)();
+
+typedef void *(*GDNativeInstanceBindingCreateCallback)(void *p_token, void *p_instance);
+typedef void (*GDNativeInstanceBindingFreeCallback)(void *p_token, void *p_instance, void *p_binding);
+typedef GDNativeBool (*GDNativeInstanceBindingReferenceCallback)(void *p_token, void *p_binding, GDNativeBool p_reference);
+
+struct GDNativeInstanceBindingCallbacks {
+ GDNativeInstanceBindingCreateCallback create_callback;
+ GDNativeInstanceBindingFreeCallback free_callback;
+ GDNativeInstanceBindingReferenceCallback reference_callback;
+};
+
+/* EXTENSION CLASSES */
+
+typedef void *GDExtensionClassInstancePtr;
+
+typedef GDNativeBool (*GDNativeExtensionClassSet)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, const GDNativeVariantPtr p_value);
+typedef GDNativeBool (*GDNativeExtensionClassGet)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, GDNativeVariantPtr r_ret);
+
+typedef struct {
+ uint32_t type;
+ const char *name;
+ const char *class_name;
+ uint32_t hint;
+ const char *hint_string;
+ uint32_t usage;
+} GDNativePropertyInfo;
+
+typedef const GDNativePropertyInfo *(*GDNativeExtensionClassGetPropertyList)(GDExtensionClassInstancePtr p_instance, uint32_t *r_count);
+typedef void (*GDNativeExtensionClassFreePropertyList)(GDExtensionClassInstancePtr p_instance, const GDNativePropertyInfo *p_list);
+typedef void (*GDNativeExtensionClassNotification)(GDExtensionClassInstancePtr p_instance, int32_t p_what);
+typedef const char *(*GDNativeExtensionClassToString)(GDExtensionClassInstancePtr p_instance);
+typedef void (*GDNativeExtensionClassReference)(GDExtensionClassInstancePtr p_instance);
+typedef void (*GDNativeExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance);
+typedef void (*GDNativeExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret);
+typedef GDExtensionClassInstancePtr (*GDNativeExtensionClassCreateInstance)(void *p_userdata);
+typedef void (*GDNativeExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance);
+typedef void (*GDNativeExtensionClassObjectInstance)(GDExtensionClassInstancePtr p_instance, GDNativeObjectPtr p_object_instance);
+typedef GDNativeExtensionClassCallVirtual (*GDNativeExtensionClassGetVirtual)(void *p_userdata, const char *p_name);
+
+typedef struct {
+ GDNativeExtensionClassSet set_func;
+ GDNativeExtensionClassGet get_func;
+ GDNativeExtensionClassGetPropertyList get_property_list_func;
+ GDNativeExtensionClassFreePropertyList free_property_list_func;
+ GDNativeExtensionClassNotification notification_func;
+ GDNativeExtensionClassToString to_string_func;
+ GDNativeExtensionClassReference reference_func;
+ GDNativeExtensionClassUnreference unreference_func;
+ GDNativeExtensionClassCreateInstance create_instance_func; /* this one is mandatory */
+ GDNativeExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
+ GDNativeExtensionClassObjectInstance object_instance_func; /* this one is mandatory */
+ GDNativeExtensionClassGetVirtual get_virtual_func;
+ void *class_userdata;
+} GDNativeExtensionClassCreationInfo;
+
+typedef void *GDNativeExtensionClassLibraryPtr;
+
+typedef const GDNativePropertyInfo *(*GDNativeExtensionClassGetPropertyList)(GDExtensionClassInstancePtr p_instance, uint32_t *r_count);
+
+/* Method */
+
+typedef enum {
+ GDNATIVE_EXTENSION_METHOD_FLAG_NORMAL = 1,
+ GDNATIVE_EXTENSION_METHOD_FLAG_EDITOR = 2,
+ GDNATIVE_EXTENSION_METHOD_FLAG_NOSCRIPT = 4,
+ GDNATIVE_EXTENSION_METHOD_FLAG_CONST = 8,
+ GDNATIVE_EXTENSION_METHOD_FLAG_REVERSE = 16, /* used for events */
+ GDNATIVE_EXTENSION_METHOD_FLAG_VIRTUAL = 32,
+ GDNATIVE_EXTENSION_METHOD_FLAG_FROM_SCRIPT = 64,
+ GDNATIVE_EXTENSION_METHOD_FLAG_VARARG = 128,
+ GDNATIVE_EXTENSION_METHOD_FLAG_STATIC = 256,
+ GDNATIVE_EXTENSION_METHOD_FLAGS_DEFAULT = GDNATIVE_EXTENSION_METHOD_FLAG_NORMAL,
+} GDNativeExtensionClassMethodFlags;
+
+typedef enum {
+ GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_NONE,
+ GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8,
+ GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT16,
+ GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32,
+ GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT64,
+ GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT8,
+ GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT16,
+ GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32,
+ GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64,
+ GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT,
+ GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE
+} GDNativeExtensionClassMethodArgumentMetadata;
+
+typedef void (*GDNativeExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error);
+typedef void (*GDNativeExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret);
+
+/* passing -1 as argument in the following functions refers to the return type */
+typedef GDNativeVariantType (*GDNativeExtensionClassMethodGetArgumentType)(void *p_method_userdata, int32_t p_argument);
+typedef void (*GDNativeExtensionClassMethodGetArgumentInfo)(void *p_method_userdata, int32_t p_argument, GDNativePropertyInfo *r_info);
+typedef GDNativeExtensionClassMethodArgumentMetadata (*GDNativeExtensionClassMethodGetArgumentMetadata)(void *p_method_userdata, int32_t p_argument);
+
+typedef struct {
+ const char *name;
+ void *method_userdata;
+ GDNativeExtensionClassMethodCall call_func;
+ GDNativeExtensionClassMethodPtrCall ptrcall_func;
+ uint32_t method_flags; /* GDNativeExtensionClassMethodFlags */
+ uint32_t argument_count;
+ GDNativeBool has_return_value;
+ GDNativeExtensionClassMethodGetArgumentType get_argument_type_func;
+ GDNativeExtensionClassMethodGetArgumentInfo get_argument_info_func; /* name and hint information for the argument can be omitted in release builds. Class name should always be present if it applies. */
+ GDNativeExtensionClassMethodGetArgumentMetadata get_argument_metadata_func;
+ uint32_t default_argument_count;
+ GDNativeVariantPtr *default_arguments;
+} GDNativeExtensionClassMethodInfo;
+
+/* INTERFACE */
+
+typedef struct {
+ uint32_t version_major;
+ uint32_t version_minor;
+ uint32_t version_patch;
+ const char *version_string;
+
+ /* GODOT CORE */
+ void *(*mem_alloc)(size_t p_bytes);
+ void *(*mem_realloc)(void *p_ptr, size_t p_bytes);
+ void (*mem_free)(void *p_ptr);
+
+ void (*print_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line);
+ void (*print_warning)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line);
+ void (*print_script_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line);
+
+ /* GODOT VARIANT */
+
+ /* variant general */
+ void (*variant_new_copy)(GDNativeVariantPtr r_dest, const GDNativeVariantPtr p_src);
+ void (*variant_new_nil)(GDNativeVariantPtr r_dest);
+ void (*variant_destroy)(GDNativeVariantPtr p_self);
+
+ /* variant type */
+ void (*variant_call)(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error);
+ void (*variant_call_static)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error);
+ void (*variant_evaluate)(GDNativeVariantOperator p_op, const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_return, GDNativeBool *r_valid);
+ void (*variant_set)(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid);
+ void (*variant_set_named)(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid);
+ void (*variant_set_keyed)(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid);
+ void (*variant_set_indexed)(GDNativeVariantPtr p_self, GDNativeInt p_index, const GDNativeVariantPtr p_value, GDNativeBool *r_valid, GDNativeBool *r_oob);
+ void (*variant_get)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
+ void (*variant_get_named)(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
+ void (*variant_get_keyed)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
+ void (*variant_get_indexed)(const GDNativeVariantPtr p_self, GDNativeInt p_index, GDNativeVariantPtr r_ret, GDNativeBool *r_valid, GDNativeBool *r_oob);
+ GDNativeBool (*variant_iter_init)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid);
+ GDNativeBool (*variant_iter_next)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid);
+ void (*variant_iter_get)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
+ GDNativeBool (*variant_hash_compare)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other);
+ GDNativeBool (*variant_booleanize)(const GDNativeVariantPtr p_self);
+ void (*variant_blend)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst);
+ void (*variant_interpolate)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst);
+ void (*variant_duplicate)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep);
+ void (*variant_stringify)(const GDNativeVariantPtr p_self, GDNativeStringPtr r_ret);
+
+ GDNativeVariantType (*variant_get_type)(const GDNativeVariantPtr p_self);
+ GDNativeBool (*variant_has_method)(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method);
+ GDNativeBool (*variant_has_member)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member);
+ GDNativeBool (*variant_has_key)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeBool *r_valid);
+ void (*variant_get_type_name)(GDNativeVariantType p_type, GDNativeStringPtr r_name);
+ GDNativeBool (*variant_can_convert)(GDNativeVariantType p_from, GDNativeVariantType p_to);
+ GDNativeBool (*variant_can_convert_strict)(GDNativeVariantType p_from, GDNativeVariantType p_to);
+
+ /* ptrcalls */
+ GDNativeVariantFromTypeConstructorFunc (*get_variant_from_type_constructor)(GDNativeVariantType p_type);
+ GDNativeTypeFromVariantConstructorFunc (*get_variant_to_type_constructor)(GDNativeVariantType p_type);
+ GDNativePtrOperatorEvaluator (*variant_get_ptr_operator_evaluator)(GDNativeVariantOperator p_operator, GDNativeVariantType p_type_a, GDNativeVariantType p_type_b);
+ GDNativePtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDNativeVariantType p_type, const char *p_method, GDNativeInt p_hash);
+ GDNativePtrConstructor (*variant_get_ptr_constructor)(GDNativeVariantType p_type, int32_t p_constructor);
+ GDNativePtrDestructor (*variant_get_ptr_destructor)(GDNativeVariantType p_type);
+ void (*variant_construct)(GDNativeVariantType p_type, GDNativeVariantPtr p_base, const GDNativeVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error);
+ GDNativePtrSetter (*variant_get_ptr_setter)(GDNativeVariantType p_type, const char *p_member);
+ GDNativePtrGetter (*variant_get_ptr_getter)(GDNativeVariantType p_type, const char *p_member);
+ GDNativePtrIndexedSetter (*variant_get_ptr_indexed_setter)(GDNativeVariantType p_type);
+ GDNativePtrIndexedGetter (*variant_get_ptr_indexed_getter)(GDNativeVariantType p_type);
+ GDNativePtrKeyedSetter (*variant_get_ptr_keyed_setter)(GDNativeVariantType p_type);
+ GDNativePtrKeyedGetter (*variant_get_ptr_keyed_getter)(GDNativeVariantType p_type);
+ GDNativePtrKeyedChecker (*variant_get_ptr_keyed_checker)(GDNativeVariantType p_type);
+ void (*variant_get_constant_value)(GDNativeVariantType p_type, const char *p_constant, GDNativeVariantPtr r_ret);
+ GDNativePtrUtilityFunction (*variant_get_ptr_utility_function)(const char *p_function, GDNativeInt p_hash);
+
+ /* extra utilities */
+
+ void (*string_new_with_latin1_chars)(GDNativeStringPtr r_dest, const char *p_contents);
+ void (*string_new_with_utf8_chars)(GDNativeStringPtr r_dest, const char *p_contents);
+ void (*string_new_with_utf16_chars)(GDNativeStringPtr r_dest, const char16_t *p_contents);
+ void (*string_new_with_utf32_chars)(GDNativeStringPtr r_dest, const char32_t *p_contents);
+ void (*string_new_with_wide_chars)(GDNativeStringPtr r_dest, const wchar_t *p_contents);
+ void (*string_new_with_latin1_chars_and_len)(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size);
+ void (*string_new_with_utf8_chars_and_len)(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size);
+ void (*string_new_with_utf16_chars_and_len)(GDNativeStringPtr r_dest, const char16_t *p_contents, const GDNativeInt p_size);
+ void (*string_new_with_utf32_chars_and_len)(GDNativeStringPtr r_dest, const char32_t *p_contents, const GDNativeInt p_size);
+ void (*string_new_with_wide_chars_and_len)(GDNativeStringPtr r_dest, const wchar_t *p_contents, const GDNativeInt p_size);
+ /* Information about the following functions:
+ * - The return value is the resulting encoded string length.
+ * - The length returned is in characters, not in bytes. It also does not include a trailing zero.
+ * - These functions also do not write trailing zero, If you need it, write it yourself at the position indicated by the length (and make sure to allocate it).
+ * - Passing NULL in r_text means only the length is computed (again, without including trailing zero).
+ * - p_max_write_length argument is in characters, not bytes. It will be ignored if r_text is NULL.
+ * - p_max_write_length argument does not affect the return value, it's only to cap write length.
+ */
+ GDNativeInt (*string_to_latin1_chars)(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length);
+ GDNativeInt (*string_to_utf8_chars)(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length);
+ GDNativeInt (*string_to_utf16_chars)(const GDNativeStringPtr p_self, char16_t *r_text, GDNativeInt p_max_write_length);
+ GDNativeInt (*string_to_utf32_chars)(const GDNativeStringPtr p_self, char32_t *r_text, GDNativeInt p_max_write_length);
+ GDNativeInt (*string_to_wide_chars)(const GDNativeStringPtr p_self, wchar_t *r_text, GDNativeInt p_max_write_length);
+ char32_t *(*string_operator_index)(GDNativeStringPtr p_self, GDNativeInt p_index);
+ const char32_t *(*string_operator_index_const)(const GDNativeStringPtr p_self, GDNativeInt p_index);
+
+ /* Packed array functions */
+
+ uint8_t *(*packed_byte_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedByteArray
+ const uint8_t *(*packed_byte_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedByteArray
+
+ GDNativeTypePtr (*packed_color_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedColorArray, returns Color ptr
+ GDNativeTypePtr (*packed_color_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedColorArray, returns Color ptr
+
+ float *(*packed_float32_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat32Array
+ const float *(*packed_float32_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat32Array
+ double *(*packed_float64_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat64Array
+ const double *(*packed_float64_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat64Array
+
+ int32_t *(*packed_int32_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array
+ const int32_t *(*packed_int32_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array
+ int64_t *(*packed_int64_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array
+ const int64_t *(*packed_int64_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array
+
+ GDNativeStringPtr (*packed_string_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedStringArray
+ GDNativeStringPtr (*packed_string_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedStringArray
+
+ GDNativeTypePtr (*packed_vector2_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr
+ GDNativeTypePtr (*packed_vector2_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr
+ GDNativeTypePtr (*packed_vector3_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr
+ GDNativeTypePtr (*packed_vector3_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr
+
+ /* OBJECT */
+
+ void (*object_method_bind_call)(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_ret, GDNativeCallError *r_error);
+ void (*object_method_bind_ptrcall)(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret);
+ void (*object_destroy)(GDNativeObjectPtr p_o);
+ GDNativeObjectPtr (*global_get_singleton)(const char *p_name);
+ void *(*object_get_instance_binding)(GDNativeObjectPtr p_o, void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks);
+ void (*object_set_instance_binding)(GDNativeObjectPtr p_o, void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks);
+
+ GDNativeObjectPtr (*object_cast_to)(const GDNativeObjectPtr p_object, void *p_class_tag);
+ GDNativeObjectPtr (*object_get_instance_from_id)(GDObjectInstanceID p_instance_id);
+ GDObjectInstanceID (*object_get_instance_id)(const GDNativeObjectPtr p_object);
+
+ /* CLASSDB */
+
+ GDNativeClassConstructor (*classdb_get_constructor)(const char *p_classname);
+ GDNativeMethodBindPtr (*classdb_get_method_bind)(const char *p_classname, const char *p_methodname, GDNativeInt p_hash);
+ void *(*classdb_get_class_tag)(const char *p_classname);
+
+ /* CLASSDB EXTENSION */
+
+ void (*classdb_register_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs);
+ void (*classdb_register_extension_class_method)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info);
+ void (*classdb_register_extension_class_integer_constant)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_enum_name, const char *p_constant_name, GDNativeInt p_constant_value);
+ void (*classdb_register_extension_class_property)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter);
+ void (*classdb_register_extension_class_property_group)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_group_name, const char *p_prefix);
+ void (*classdb_register_extension_class_property_subgroup)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_subgroup_name, const char *p_prefix);
+ void (*classdb_register_extension_class_signal)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count);
+ void (*classdb_unregister_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */
+} GDNativeInterface;
+
+/* INITIALIZATION */
+
+typedef enum {
+ GDNATIVE_INITIALIZATION_CORE,
+ GDNATIVE_INITIALIZATION_SERVERS,
+ GDNATIVE_INITIALIZATION_SCENE,
+ GDNATIVE_INITIALIZATION_EDITOR,
+ GDNATIVE_INITIALIZATION_DRIVER,
+ GDNATIVE_MAX_INITIALIZATION_LEVEL,
+} GDNativeInitializationLevel;
+
+typedef struct {
+ /* Minimum initialization level required.
+ * If Core or Servers, the extension needs editor or game restart to take effect */
+ GDNativeInitializationLevel minimum_initialization_level;
+ /* Up to the user to supply when initializing */
+ void *userdata;
+ /* This function will be called multiple times for each initialization level. */
+ void (*initialize)(void *userdata, GDNativeInitializationLevel p_level);
+ void (*deinitialize)(void *userdata, GDNativeInitializationLevel p_level);
+} GDNativeInitialization;
+
+/* Define a C function prototype that implements the function below and expose it to dlopen() (or similar).
+ * It will be called on initialization. The name must be an unique one specified in the .gdextension config file.
+ */
+
+typedef GDNativeBool (*GDNativeInitializationFunction)(const GDNativeInterface *p_interface, const GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization *r_initialization);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp
new file mode 100644
index 0000000000..635e53fa9c
--- /dev/null
+++ b/core/extension/native_extension.cpp
@@ -0,0 +1,440 @@
+/*************************************************************************/
+/* native_extension.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "native_extension.h"
+#include "core/config/project_settings.h"
+#include "core/io/config_file.h"
+#include "core/object/class_db.h"
+#include "core/object/method_bind.h"
+#include "core/os/os.h"
+
+const char *NativeExtension::EXTENSION_LIST_CONFIG_FILE = "res://.godot/extension_list.cfg";
+
+class NativeExtensionMethodBind : public MethodBind {
+ GDNativeExtensionClassMethodCall call_func;
+ GDNativeExtensionClassMethodPtrCall ptrcall_func;
+ GDNativeExtensionClassMethodGetArgumentType get_argument_type_func;
+ GDNativeExtensionClassMethodGetArgumentInfo get_argument_info_func;
+ GDNativeExtensionClassMethodGetArgumentMetadata get_argument_metadata_func;
+ void *method_userdata;
+ bool vararg;
+
+protected:
+ virtual Variant::Type _gen_argument_type(int p_arg) const {
+ return Variant::Type(get_argument_type_func(method_userdata, p_arg));
+ }
+ virtual PropertyInfo _gen_argument_type_info(int p_arg) const {
+ GDNativePropertyInfo pinfo;
+ get_argument_info_func(method_userdata, p_arg, &pinfo);
+ PropertyInfo ret;
+ ret.type = Variant::Type(pinfo.type);
+ ret.name = pinfo.name;
+ ret.class_name = pinfo.class_name;
+ ret.hint = PropertyHint(pinfo.hint);
+ ret.usage = pinfo.usage;
+ ret.class_name = pinfo.class_name;
+ return ret;
+ }
+
+public:
+ virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const {
+ return GodotTypeInfo::Metadata(get_argument_metadata_func(method_userdata, p_arg));
+ }
+
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ Variant ret;
+ GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance();
+ GDNativeCallError ce{ GDNATIVE_CALL_OK, 0, 0 };
+ call_func(method_userdata, extension_instance, (const GDNativeVariantPtr *)p_args, p_arg_count, (GDNativeVariantPtr)&ret, &ce);
+ r_error.error = Callable::CallError::Error(ce.error);
+ r_error.argument = ce.argument;
+ r_error.expected = ce.expected;
+ return ret;
+ }
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) {
+ ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have ptrcall support. This is most likely an engine bug.");
+ GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance();
+ ptrcall_func(method_userdata, extension_instance, (const GDNativeTypePtr *)p_args, (GDNativeTypePtr)r_ret);
+ }
+
+ virtual bool is_vararg() const {
+ return false;
+ }
+ NativeExtensionMethodBind(const GDNativeExtensionClassMethodInfo *p_method_info) {
+ method_userdata = p_method_info->method_userdata;
+ call_func = p_method_info->call_func;
+ ptrcall_func = p_method_info->ptrcall_func;
+ get_argument_type_func = p_method_info->get_argument_type_func;
+ get_argument_info_func = p_method_info->get_argument_info_func;
+ get_argument_metadata_func = p_method_info->get_argument_metadata_func;
+ set_name(p_method_info->name);
+
+ vararg = p_method_info->method_flags & GDNATIVE_EXTENSION_METHOD_FLAG_VARARG;
+
+ _set_returns(p_method_info->has_return_value);
+ _set_const(p_method_info->method_flags & GDNATIVE_EXTENSION_METHOD_FLAG_CONST);
+#ifdef DEBUG_METHODS_ENABLED
+ _generate_argument_types(p_method_info->argument_count);
+#endif
+ set_argument_count(p_method_info->argument_count);
+ }
+};
+
+static GDNativeInterface gdnative_interface;
+
+void NativeExtension::_register_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs) {
+ NativeExtension *self = (NativeExtension *)p_library;
+
+ StringName class_name = p_class_name;
+ ERR_FAIL_COND_MSG(!String(class_name).is_valid_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier.");
+ ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), "Attempt to register extension class '" + class_name + "', which appears to be already registered.");
+
+ Extension *parent_extension = nullptr;
+ StringName parent_class_name = p_parent_class_name;
+
+ if (self->extension_classes.has(parent_class_name)) {
+ parent_extension = &self->extension_classes[parent_class_name];
+ } else if (ClassDB::class_exists(parent_class_name)) {
+ if (ClassDB::get_api_type(parent_class_name) == ClassDB::API_EXTENSION || ClassDB::get_api_type(parent_class_name) == ClassDB::API_EDITOR_EXTENSION) {
+ ERR_PRINT("Unimplemented yet");
+ //inheriting from another extension
+ } else {
+ //inheriting from engine class
+ }
+ } else {
+ ERR_FAIL_MSG("Attempt to register an extension class '" + String(class_name) + "' using non-existing parent class '" + String(parent_class_name) + "'");
+ }
+
+ self->extension_classes[class_name] = Extension();
+
+ Extension *extension = &self->extension_classes[class_name];
+
+ if (parent_extension) {
+ extension->native_extension.parent = &parent_extension->native_extension;
+ parent_extension->native_extension.children.push_back(&extension->native_extension);
+ }
+
+ extension->native_extension.parent_class_name = parent_class_name;
+ extension->native_extension.class_name = class_name;
+ extension->native_extension.editor_class = self->level_initialized == INITIALIZATION_LEVEL_EDITOR;
+ extension->native_extension.set = p_extension_funcs->set_func;
+ extension->native_extension.get = p_extension_funcs->get_func;
+ extension->native_extension.get_property_list = p_extension_funcs->get_property_list_func;
+ extension->native_extension.free_property_list = p_extension_funcs->free_property_list_func;
+ extension->native_extension.notification = p_extension_funcs->notification_func;
+ extension->native_extension.to_string = p_extension_funcs->to_string_func;
+ extension->native_extension.reference = p_extension_funcs->reference_func;
+ extension->native_extension.unreference = p_extension_funcs->unreference_func;
+ extension->native_extension.class_userdata = p_extension_funcs->class_userdata;
+ extension->native_extension.create_instance = p_extension_funcs->create_instance_func;
+ extension->native_extension.set_object_instance = p_extension_funcs->object_instance_func;
+ extension->native_extension.free_instance = p_extension_funcs->free_instance_func;
+ extension->native_extension.get_virtual = p_extension_funcs->get_virtual_func;
+
+ ClassDB::register_extension_class(&extension->native_extension);
+}
+void NativeExtension::_register_extension_class_method(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info) {
+ NativeExtension *self = (NativeExtension *)p_library;
+
+ StringName class_name = p_class_name;
+ StringName method_name = p_method_info->name;
+ ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension method '" + String(method_name) + "' for unexisting class '" + class_name + "'.");
+
+ //Extension *extension = &self->extension_classes[class_name];
+
+ NativeExtensionMethodBind *method = memnew(NativeExtensionMethodBind(p_method_info));
+ method->set_instance_class(class_name);
+
+ ClassDB::bind_method_custom(class_name, method);
+}
+void NativeExtension::_register_extension_class_integer_constant(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_enum_name, const char *p_constant_name, GDNativeInt p_constant_value) {
+ NativeExtension *self = (NativeExtension *)p_library;
+
+ StringName class_name = p_class_name;
+ ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension constant '" + String(p_constant_name) + "' for unexisting class '" + class_name + "'.");
+
+ //Extension *extension = &self->extension_classes[class_name];
+
+ ClassDB::bind_integer_constant(class_name, p_enum_name, p_constant_name, p_constant_value);
+}
+
+void NativeExtension::_register_extension_class_property(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter) {
+ NativeExtension *self = (NativeExtension *)p_library;
+
+ StringName class_name = p_class_name;
+ ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property '" + String(p_info->name) + "' for unexisting class '" + class_name + "'.");
+
+ //Extension *extension = &self->extension_classes[class_name];
+ PropertyInfo pinfo;
+ pinfo.type = Variant::Type(p_info->type);
+ pinfo.name = p_info->name;
+ pinfo.class_name = p_info->class_name;
+ pinfo.hint = PropertyHint(p_info->hint);
+ pinfo.hint_string = p_info->hint_string;
+ pinfo.usage = p_info->usage;
+
+ ClassDB::add_property(class_name, pinfo, p_setter, p_getter);
+}
+
+void NativeExtension::_register_extension_class_property_group(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_group_name, const char *p_prefix) {
+ NativeExtension *self = (NativeExtension *)p_library;
+
+ StringName class_name = p_class_name;
+ ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property group '" + String(p_group_name) + "' for unexisting class '" + class_name + "'.");
+
+ ClassDB::add_property_group(class_name, p_group_name, p_prefix);
+}
+
+void NativeExtension::_register_extension_class_property_subgroup(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_subgroup_name, const char *p_prefix) {
+ NativeExtension *self = (NativeExtension *)p_library;
+
+ StringName class_name = p_class_name;
+ ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property subgroup '" + String(p_subgroup_name) + "' for unexisting class '" + class_name + "'.");
+
+ ClassDB::add_property_subgroup(class_name, p_subgroup_name, p_prefix);
+}
+
+void NativeExtension::_register_extension_class_signal(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count) {
+ NativeExtension *self = (NativeExtension *)p_library;
+
+ StringName class_name = p_class_name;
+ ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class signal '" + String(p_signal_name) + "' for unexisting class '" + class_name + "'.");
+
+ MethodInfo s;
+ s.name = p_signal_name;
+ for (int i = 0; i < p_argument_count; i++) {
+ PropertyInfo arg;
+ arg.type = Variant::Type(p_argument_info[i].type);
+ arg.name = p_argument_info[i].name;
+ arg.class_name = p_argument_info[i].class_name;
+ arg.hint = PropertyHint(p_argument_info[i].hint);
+ arg.hint_string = p_argument_info[i].hint_string;
+ arg.usage = p_argument_info[i].usage;
+ s.arguments.push_back(arg);
+ }
+ ClassDB::add_signal(class_name, s);
+}
+
+void NativeExtension::_unregister_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name) {
+ NativeExtension *self = (NativeExtension *)p_library;
+
+ StringName class_name = p_class_name;
+ ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to unregister unexisting extension class '" + class_name + "'.");
+ Extension *ext = &self->extension_classes[class_name];
+ ERR_FAIL_COND_MSG(ext->native_extension.children.size(), "Attempt to unregister class '" + class_name + "' while other extension classes inherit from it.");
+
+ ClassDB::unregister_extension_class(class_name);
+ if (ext->native_extension.parent != nullptr) {
+ ext->native_extension.parent->children.erase(&ext->native_extension);
+ }
+ self->extension_classes.erase(class_name);
+}
+
+Error NativeExtension::open_library(const String &p_path, const String &p_entry_symbol) {
+ Error err = OS::get_singleton()->open_dynamic_library(p_path, library, true);
+ if (err != OK) {
+ return err;
+ }
+
+ void *entry_funcptr = nullptr;
+
+ err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, p_entry_symbol, entry_funcptr, false);
+
+ if (err != OK) {
+ OS::get_singleton()->close_dynamic_library(library);
+ return err;
+ }
+
+ GDNativeInitializationFunction initialization_function = (GDNativeInitializationFunction)entry_funcptr;
+
+ initialization_function(&gdnative_interface, this, &initialization);
+ level_initialized = -1;
+ return OK;
+}
+
+void NativeExtension::close_library() {
+ ERR_FAIL_COND(library == nullptr);
+ OS::get_singleton()->close_dynamic_library(library);
+
+ library = nullptr;
+}
+
+bool NativeExtension::is_library_open() const {
+ return library != nullptr;
+}
+
+NativeExtension::InitializationLevel NativeExtension::get_minimum_library_initialization_level() const {
+ ERR_FAIL_COND_V(library == nullptr, INITIALIZATION_LEVEL_CORE);
+ return InitializationLevel(initialization.minimum_initialization_level);
+}
+void NativeExtension::initialize_library(InitializationLevel p_level) {
+ ERR_FAIL_COND(library == nullptr);
+ ERR_FAIL_COND(p_level <= int32_t(level_initialized));
+
+ level_initialized = int32_t(p_level);
+
+ ERR_FAIL_COND(initialization.initialize == nullptr);
+
+ initialization.initialize(initialization.userdata, GDNativeInitializationLevel(p_level));
+}
+void NativeExtension::deinitialize_library(InitializationLevel p_level) {
+ ERR_FAIL_COND(library == nullptr);
+ ERR_FAIL_COND(p_level > int32_t(level_initialized));
+
+ level_initialized = int32_t(p_level) - 1;
+ initialization.deinitialize(initialization.userdata, GDNativeInitializationLevel(p_level));
+}
+
+void NativeExtension::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("open_library", "path", "entry_symbol"), &NativeExtension::open_library);
+ ClassDB::bind_method(D_METHOD("close_library"), &NativeExtension::close_library);
+ ClassDB::bind_method(D_METHOD("is_library_open"), &NativeExtension::is_library_open);
+
+ ClassDB::bind_method(D_METHOD("get_minimum_library_initialization_level"), &NativeExtension::get_minimum_library_initialization_level);
+ ClassDB::bind_method(D_METHOD("initialize_library", "level"), &NativeExtension::initialize_library);
+
+ BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_CORE);
+ BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_SERVERS);
+ BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_SCENE);
+ BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_EDITOR);
+}
+
+NativeExtension::NativeExtension() {
+}
+
+NativeExtension::~NativeExtension() {
+ if (library != nullptr) {
+ close_library();
+ }
+}
+
+extern void gdnative_setup_interface(GDNativeInterface *p_interface);
+
+void NativeExtension::initialize_native_extensions() {
+ gdnative_setup_interface(&gdnative_interface);
+
+ gdnative_interface.classdb_register_extension_class = _register_extension_class;
+ gdnative_interface.classdb_register_extension_class_method = _register_extension_class_method;
+ gdnative_interface.classdb_register_extension_class_integer_constant = _register_extension_class_integer_constant;
+ gdnative_interface.classdb_register_extension_class_property = _register_extension_class_property;
+ gdnative_interface.classdb_register_extension_class_property_group = _register_extension_class_property_group;
+ gdnative_interface.classdb_register_extension_class_property_subgroup = _register_extension_class_property_subgroup;
+ gdnative_interface.classdb_register_extension_class_signal = _register_extension_class_signal;
+ gdnative_interface.classdb_unregister_extension_class = _unregister_extension_class;
+}
+
+RES NativeExtensionResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+ Ref<ConfigFile> config;
+ config.instantiate();
+
+ Error err = config->load(p_path);
+
+ if (r_error) {
+ *r_error = err;
+ }
+
+ if (err != OK) {
+ return RES();
+ }
+
+ if (!config->has_section_key("configuration", "entry_symbol")) {
+ if (r_error) {
+ *r_error = ERR_INVALID_DATA;
+ }
+ return RES();
+ }
+
+ String entry_symbol = config->get_value("configuration", "entry_symbol");
+
+ List<String> libraries;
+
+ config->get_section_keys("libraries", &libraries);
+
+ String library_path;
+
+ for (const String &E : libraries) {
+ Vector<String> tags = E.split(".");
+ bool all_tags_met = true;
+ for (int i = 0; i < tags.size(); i++) {
+ String tag = tags[i].strip_edges();
+ if (!OS::get_singleton()->has_feature(tag)) {
+ all_tags_met = false;
+ break;
+ }
+ }
+
+ if (all_tags_met) {
+ library_path = config->get_value("libraries", E);
+ break;
+ }
+ }
+
+ if (library_path == String()) {
+ if (r_error) {
+ *r_error = ERR_FILE_NOT_FOUND;
+ }
+ return RES();
+ }
+
+ if (!library_path.is_resource_file()) {
+ library_path = p_path.get_base_dir().plus_file(library_path);
+ }
+
+ Ref<NativeExtension> lib;
+ lib.instantiate();
+ String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path);
+ err = lib->open_library(abs_path, entry_symbol);
+
+ if (r_error) {
+ *r_error = err;
+ }
+
+ if (err != OK) {
+ return RES();
+ }
+
+ return lib;
+}
+
+void NativeExtensionResourceLoader::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("gdextension");
+}
+
+bool NativeExtensionResourceLoader::handles_type(const String &p_type) const {
+ return p_type == "NativeExtension";
+}
+
+String NativeExtensionResourceLoader::get_resource_type(const String &p_path) const {
+ String el = p_path.get_extension().to_lower();
+ if (el == "gdextension") {
+ return "NativeExtension";
+ }
+ return "";
+}
diff --git a/core/extension/native_extension.h b/core/extension/native_extension.h
new file mode 100644
index 0000000000..52e869ad4d
--- /dev/null
+++ b/core/extension/native_extension.h
@@ -0,0 +1,99 @@
+/*************************************************************************/
+/* native_extension.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef NATIVE_EXTENSION_H
+#define NATIVE_EXTENSION_H
+
+#include "core/extension/gdnative_interface.h"
+#include "core/io/resource_loader.h"
+#include "core/object/ref_counted.h"
+
+class NativeExtension : public Resource {
+ GDCLASS(NativeExtension, Resource)
+
+ void *library = nullptr; // pointer if valid,
+
+ struct Extension {
+ ObjectNativeExtension native_extension;
+ };
+
+ Map<StringName, Extension> extension_classes;
+
+ static void _register_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs);
+ static void _register_extension_class_method(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info);
+ static void _register_extension_class_integer_constant(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_enum_name, const char *p_constant_name, GDNativeInt p_constant_value);
+ static void _register_extension_class_property(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter);
+ static void _register_extension_class_property_group(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_group_name, const char *p_prefix);
+ static void _register_extension_class_property_subgroup(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_subgroup_name, const char *p_prefix);
+ static void _register_extension_class_signal(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count);
+ static void _unregister_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name);
+
+ GDNativeInitialization initialization;
+ int32_t level_initialized = -1;
+
+protected:
+ static void _bind_methods();
+
+public:
+ static const char *EXTENSION_LIST_CONFIG_FILE;
+
+ Error open_library(const String &p_path, const String &p_entry_symbol);
+ void close_library();
+
+ enum InitializationLevel {
+ INITIALIZATION_LEVEL_CORE,
+ INITIALIZATION_LEVEL_SERVERS,
+ INITIALIZATION_LEVEL_SCENE,
+ INITIALIZATION_LEVEL_EDITOR,
+ INITIALIZATION_LEVEL_DRIVER,
+ };
+
+ bool is_library_open() const;
+
+ InitializationLevel get_minimum_library_initialization_level() const;
+ void initialize_library(InitializationLevel p_level);
+ void deinitialize_library(InitializationLevel p_level);
+
+ static void initialize_native_extensions();
+ NativeExtension();
+ ~NativeExtension();
+};
+
+VARIANT_ENUM_CAST(NativeExtension::InitializationLevel)
+
+class NativeExtensionResourceLoader : public ResourceFormatLoader {
+public:
+ virtual RES load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String &p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+};
+
+#endif // NATIVEEXTENSION_H
diff --git a/core/extension/native_extension_manager.cpp b/core/extension/native_extension_manager.cpp
new file mode 100644
index 0000000000..8b7a9df4f1
--- /dev/null
+++ b/core/extension/native_extension_manager.cpp
@@ -0,0 +1,149 @@
+/*************************************************************************/
+/* native_extension_manager.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "native_extension_manager.h"
+#include "core/io/file_access.h"
+
+NativeExtensionManager::LoadStatus NativeExtensionManager::load_extension(const String &p_path) {
+ if (native_extension_map.has(p_path)) {
+ return LOAD_STATUS_ALREADY_LOADED;
+ }
+ Ref<NativeExtension> extension = ResourceLoader::load(p_path);
+ if (extension.is_null()) {
+ return LOAD_STATUS_FAILED;
+ }
+
+ if (level >= 0) { //already initialized up to some level
+ int32_t minimum_level = extension->get_minimum_library_initialization_level();
+ if (minimum_level < MIN(level, NativeExtension::INITIALIZATION_LEVEL_SCENE)) {
+ return LOAD_STATUS_NEEDS_RESTART;
+ }
+ //initialize up to current level
+ for (int32_t i = minimum_level; i < level; i++) {
+ extension->initialize_library(NativeExtension::InitializationLevel(level));
+ }
+ }
+ native_extension_map[p_path] = extension;
+ return LOAD_STATUS_OK;
+}
+
+NativeExtensionManager::LoadStatus NativeExtensionManager::reload_extension(const String &p_path) {
+ return LOAD_STATUS_OK; //TODO
+}
+NativeExtensionManager::LoadStatus NativeExtensionManager::unload_extension(const String &p_path) {
+ if (!native_extension_map.has(p_path)) {
+ return LOAD_STATUS_NOT_LOADED;
+ }
+
+ Ref<NativeExtension> extension = native_extension_map[p_path];
+
+ if (level >= 0) { //already initialized up to some level
+ int32_t minimum_level = extension->get_minimum_library_initialization_level();
+ if (minimum_level < MIN(level, NativeExtension::INITIALIZATION_LEVEL_SCENE)) {
+ return LOAD_STATUS_NEEDS_RESTART;
+ }
+ //initialize up to current level
+ for (int32_t i = level; i >= minimum_level; i--) {
+ extension->deinitialize_library(NativeExtension::InitializationLevel(level));
+ }
+ }
+ native_extension_map.erase(p_path);
+ return LOAD_STATUS_OK;
+}
+
+bool NativeExtensionManager::is_extension_loaded(const String &p_path) const {
+ return native_extension_map.has(p_path);
+}
+
+Vector<String> NativeExtensionManager::get_loaded_extensions() const {
+ Vector<String> ret;
+ for (const Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+Ref<NativeExtension> NativeExtensionManager::get_extension(const String &p_path) {
+ Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.find(p_path);
+ ERR_FAIL_COND_V(!E, Ref<NativeExtension>());
+ return E->get();
+}
+
+void NativeExtensionManager::initialize_extensions(NativeExtension::InitializationLevel p_level) {
+ ERR_FAIL_COND(int32_t(p_level) - 1 != level);
+ for (Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) {
+ E->get()->initialize_library(p_level);
+ }
+ level = p_level;
+}
+
+void NativeExtensionManager::deinitialize_extensions(NativeExtension::InitializationLevel p_level) {
+ ERR_FAIL_COND(int32_t(p_level) != level);
+ for (Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) {
+ E->get()->deinitialize_library(p_level);
+ }
+ level = int32_t(p_level) - 1;
+}
+
+void NativeExtensionManager::load_extensions() {
+ FileAccessRef f = FileAccess::open(NativeExtension::EXTENSION_LIST_CONFIG_FILE, FileAccess::READ);
+ while (f && !f->eof_reached()) {
+ String s = f->get_line().strip_edges();
+ if (s != String()) {
+ LoadStatus err = load_extension(s);
+ ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, "Error loading extension: " + s);
+ }
+ }
+}
+
+NativeExtensionManager *NativeExtensionManager::get_singleton() {
+ return singleton;
+}
+void NativeExtensionManager::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("load_extension", "path"), &NativeExtensionManager::load_extension);
+ ClassDB::bind_method(D_METHOD("reload_extension", "path"), &NativeExtensionManager::reload_extension);
+ ClassDB::bind_method(D_METHOD("unload_extension", "path"), &NativeExtensionManager::unload_extension);
+ ClassDB::bind_method(D_METHOD("is_extension_loaded", "path"), &NativeExtensionManager::is_extension_loaded);
+
+ ClassDB::bind_method(D_METHOD("get_loaded_extensions"), &NativeExtensionManager::get_loaded_extensions);
+ ClassDB::bind_method(D_METHOD("get_extension", "path"), &NativeExtensionManager::get_extension);
+
+ BIND_ENUM_CONSTANT(LOAD_STATUS_OK);
+ BIND_ENUM_CONSTANT(LOAD_STATUS_FAILED);
+ BIND_ENUM_CONSTANT(LOAD_STATUS_ALREADY_LOADED);
+ BIND_ENUM_CONSTANT(LOAD_STATUS_NOT_LOADED);
+ BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART);
+}
+
+NativeExtensionManager *NativeExtensionManager::singleton = nullptr;
+
+NativeExtensionManager::NativeExtensionManager() {
+ ERR_FAIL_COND(singleton != nullptr);
+ singleton = this;
+}
diff --git a/core/extension/native_extension_manager.h b/core/extension/native_extension_manager.h
new file mode 100644
index 0000000000..89ccd155fe
--- /dev/null
+++ b/core/extension/native_extension_manager.h
@@ -0,0 +1,74 @@
+/*************************************************************************/
+/* native_extension_manager.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef NATIVE_EXTENSION_MANAGER_H
+#define NATIVE_EXTENSION_MANAGER_H
+
+#include "core/extension/native_extension.h"
+
+class NativeExtensionManager : public Object {
+ GDCLASS(NativeExtensionManager, Object);
+
+ int32_t level = -1;
+ Map<String, Ref<NativeExtension>> native_extension_map;
+
+ static void _bind_methods();
+
+ static NativeExtensionManager *singleton;
+
+public:
+ enum LoadStatus {
+ LOAD_STATUS_OK,
+ LOAD_STATUS_FAILED,
+ LOAD_STATUS_ALREADY_LOADED,
+ LOAD_STATUS_NOT_LOADED,
+ LOAD_STATUS_NEEDS_RESTART,
+ };
+
+ LoadStatus load_extension(const String &p_path);
+ LoadStatus reload_extension(const String &p_path);
+ LoadStatus unload_extension(const String &p_path);
+ bool is_extension_loaded(const String &p_path) const;
+ Vector<String> get_loaded_extensions() const;
+ Ref<NativeExtension> get_extension(const String &p_path);
+
+ void initialize_extensions(NativeExtension::InitializationLevel p_level);
+ void deinitialize_extensions(NativeExtension::InitializationLevel p_level);
+
+ static NativeExtensionManager *get_singleton();
+
+ void load_extensions();
+
+ NativeExtensionManager();
+};
+
+VARIANT_ENUM_CAST(NativeExtensionManager::LoadStatus)
+
+#endif // NATIVEEXTENSIONMANAGER_H
diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt
index 884fb9550c..f136d83496 100644
--- a/core/input/gamecontrollerdb.txt
+++ b/core/input/gamecontrollerdb.txt
@@ -4,19 +4,24 @@
# Windows
03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,
03000000c82d00002038000000000000,8bitdo,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
+03000000c82d00000951000000000000,8BitDo Dogbone Modkit,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,
03000000c82d000011ab000000000000,8BitDo F30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00001038000000000000,8BitDo F30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000090000000000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00005106000000000000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,platform:Windows,
+03000000c82d00000151000000000000,8BitDo M30 ModKit,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00008010000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,
+03000000c82d00000451000000000000,8BitDo N30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,start:b11,platform:Windows,
03000000c82d00000190000000000000,8BitDo N30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
+03000000c82d00000360000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
+03000000c82d00002867000000000000,8BitDo S30 Modkit,a:b0,b:b1,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00000130000000000000,8BitDo SF30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000060000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000061000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
@@ -24,19 +29,22 @@
03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00003028000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
-03000000c82d00000351000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d000020ab000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00004028000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00006228000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
+03000000c82d00000351000000000000,8BitDo SN30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
+03000000c82d00000121000000000000,8BitDo SN30 Pro for Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00000260000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000261000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000031000000000000,8BitDo Wireless Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,
+03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
+03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
030000008f0e00001200000000000000,Acme GA-02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
03000000c01100000355000011010000,ACRUX USB GAME PAD,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000fa190000f0ff000000000000,Acteck AGJ-3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@@ -49,7 +57,9 @@
030000006f0e00001901000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000006f0e00001a01000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000d62000001d57000000000000,Airflo PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
-03000000869800002400000000007801,Astro C40 TR,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
+03000000491900001904000000000000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,
+03000000710100001904000000000000,Amazon Luna Controller,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b8,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b4,rightstick:b7,rightx:a3,righty:a4,start:b6,x:b3,y:b2,platform:Windows,
+03000000ef0500000300000000000000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,
03000000d6200000e557000000000000,Batarang,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,
030000006f0e00003201000000000000,Battlefield 4 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@@ -65,7 +75,7 @@
03000000808300000300000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,
030000006b1400000055000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,
-03000000120c0000210e000000000000,Brook Mars,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,
+03000000120c0000210e000000000000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,
03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,
03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@@ -106,7 +116,6 @@
03000000ac0500004d04000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
-030000006f0e00000102000000007801,GameStop Xbox 360 Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000009b2800003200000000000000,GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
030000009b2800006000000000000000,GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
@@ -157,6 +166,7 @@
030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows,
030000007e0500000720000000000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,
030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,
+03000000bd12000003c0000010010000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000bd12000003c0000000000000,JY-P70UR,a:b1,b:b0,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b4,x:b3,y:b2,platform:Windows,
03000000242f00002d00000000000000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000242f00008a00000000000000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,
@@ -184,6 +194,7 @@
03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000002a0600001024000000000000,Matricom,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,
+030000009f000000adbb000000000000,MaxJoypad Virtual Controller,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,
03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,
03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,
@@ -202,6 +213,8 @@
03000000efbe0000edfe000000000000,Monect Virtual Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,
03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
030000006b140000010c000000000000,NACON GC-400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
+03000000921200004b46000000000000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,
+03000000790000004518000000000000,NEXILUX GAMECUBE Controller Adapter,platform:Windows,a:b1,b:b0,x:b2,y:b3,start:b9,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,
030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Windows,
03000000152000000182000000000000,NGDS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,
03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,
@@ -217,6 +230,7 @@
030000006f0e00000901000000000000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,
030000004c050000da0c000000000000,PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
+030000004c0500003713000000000000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,
03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000d62000009f31000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@@ -241,6 +255,7 @@
030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
+03000000ff000000cb01000000000000,PSP,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,
03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,
03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,
@@ -292,7 +307,6 @@
030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
03000000a30c00002500000000000000,Sega Genesis Mini 3B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows,
03000000a30c00002400000000000000,Sega Mega Drive Mini 6B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
-030000005e0400008e02000000007801,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,
03000000341a00000908000000000000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
030000008f0e00000800000000000000,SpeedLink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@@ -337,8 +351,7 @@
030000006f0e00000702000000000000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:a3,righty:a4,start:b4,x:b2,y:b3,platform:Windows,
030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
-030000005e040000ff02000000007801,Xbox One Elite Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
-030000005e040000130b000000000000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
+030000005e040000130b000000000000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
03000000ac0500005b05000000000000,Xiaoji Gamesir-G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@@ -362,6 +375,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00000190000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00001290000001000000,8BitDo SN30 Gamepad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
+03000000c82d00004028000000010000,8Bitdo SN30 GamePad,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,platform:Mac OS X,
03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000260000001000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
@@ -371,21 +385,28 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000a00500003232000009010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
+03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
+03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
+03000000ef0500000300000000020000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X,
+03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Mac OS X,
+03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,
03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,
03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
-03000000120c0000200e000000010000,Brook Mars,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Mac OS X,
-03000000120c0000210e000000010000,Brook Mars,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,
+03000000120c0000200e000000010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
+03000000120c0000210e000000010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,
03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,
+03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X,
03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000006f0e00000102000000000000,GameStop Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000007d0400000540000001010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
-030000008f0e00000300000007010000,GreenAsia Inc. USB Joystick,a:b2,b:b3,x:b0,y:b1,back:b8,start:b9,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b5,righttrigger:b7,platform:Mac OS X,
+03000000280400000140000000020000,Gravis Gamepad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
+030000008f0e00000300000007010000,GreenAsia Inc. USB Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Mac OS X,
030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -416,11 +437,13 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000380700008433000000010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000380700008483000000010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
+03000000790000000600000007010000,Marvo GT-004,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,
03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000242f00007300000000020000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Mac OS X,
0300000079000000d218000026010000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000d620000010a7000003010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,
+03000000790000000018000000010000,Mayflash Wii U Pro Controller Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,
03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,
03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000005e0400002700000001010000,Microsoft SideWinder Plug & Play Game Pad,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,leftx:a0,lefty:a1,righttrigger:b5,x:b2,y:b3,platform:Mac OS X,
@@ -428,15 +451,19 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c62400002b89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000632500007505000000020000,NEOGEO mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X,
+03000000921200004b46000003020000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Mac OS X,
030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000d620000011a7000000020000,Nintendo Switch Core (Plus) Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
+03000000d620000011a7000010050000,Nintendo Switch PowerA Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000550900001472000025050000,NVIDIA Controller v01.04,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X,
030000006f0e00000901000002010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,
030000004c050000da0c000000010000,Playstation Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
+030000004c0500003713000000010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
+03000000100800000300000006010000,PS2 Adapter,a:b2,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,
030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,
030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -487,9 +514,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,
050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,
030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
+030000006f0e00000104000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e040000050b000003090000,Xbox Elite Wireless Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
+03000000c62400003a54000000000000,Xbox One PowerA Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
@@ -545,18 +574,28 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
030000007c1800000006000010010000,Alienware Dual Compatible Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Linux,
05000000491900000204000021000000,Amazon Fire Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+03000000491900001904000011010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,
+05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,
03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
+03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
+03000000a30c00002800000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,
05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,
+03000000503200000110000000000000,Atari Classic Controller,a:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,x:b1,platform:Linux,
+05000000503200000110000000000000,Atari Classic Controller,a:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,x:b1,platform:Linux,
+03000000503200000210000000000000,Atari Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
+05000000503200000210000000000000,Atari Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
03000000120c00000500000010010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,
+03000000ef0500000300000000010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,
03000000c62400001b89000011010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
03000000c21100000791000011010000,Be1 GC101 Controller 1.03 mode,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000c31100000791000011010000,Be1 GC101 GAMEPAD 1.03 mode,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000005e0400008e02000003030000,Be1 GC101 Xbox 360 Controller mode,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,
-03000000120c0000200e000011010000,Brook Mars,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,
-03000000120c0000210e000011010000,Brook Mars,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux,
+03000000120c0000200e000011010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
+03000000120c0000210e000011010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
+03000000120c0000f70e000011010000,Brook Universal Fighting Board,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000ffff0000ffff000000010000,Chinese-made Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Linux,
@@ -621,6 +660,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux,
030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,
050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,
+03000000bd12000003c0000010010000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000242f00002d00000011010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000242f00008a00000011010000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,
030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
@@ -650,8 +690,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
+03000000242f0000f700000001010000,Magic-S Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000120c00000500000000010000,Manta Dualshock 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,
-03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,
+03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux,
03000000790000004318000010010000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,
03000000242f00007300000011010000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,
0300000079000000d218000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
@@ -681,15 +722,18 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000250900006688000000010000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,
030000006b140000010c000010010000,NACON GC-400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
+03000000790000004518000010010000,NEXILUX GAMECUBE Controller Adapter,a:b1,b:b0,x:b2,y:b3,start:b9,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,platform:Linux,
030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Linux,
+060000007e0500003713000000000000,Nintendo 3DS,a:b0,b:b1,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,
060000007e0500000820000000000000,Nintendo Combined Joy-Cons (joycond),a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,platform:Linux,
-03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b0,y:b3,platform:Linux,
-050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b9,b:b8,x:b7,y:b10,back:b5,start:b0,leftstick:b6,leftshoulder:b2,rightshoulder:b4,leftx:a1,lefty:a0~,platform:Linux,
-050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,x:b0,y:b3,back:b9,start:b8,leftstick:b10,leftshoulder:b4,rightshoulder:b6,leftx:a1~,lefty:a0~,platform:Linux,
+03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b2,y:b3,platform:Linux,
+050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b9,b:b8,back:b5,leftshoulder:b2,leftstick:b6,leftx:a1,lefty:a0~,rightshoulder:b4,start:b0,x:b7,y:b10,platform:Linux,
+030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
-030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
+050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,back:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0~,rightshoulder:b6,start:b8,x:b0,y:b3,platform:Linux,
+050000007e0500001720000001000000,Nintendo Switch SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,
050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,
05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,
@@ -717,8 +761,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000006f0e0000a802000023020000,PDP Wired Controller for Xbox One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
030000006f0e00008501000011010000,PDP Wired Fight Pad Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
0500000049190000030400001b010000,PG-9099,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
-05000000491900000204000000000000,PG-9118,a:b73,b:b74,back:b83,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b79,leftstick:b86,lefttrigger:b81,leftx:a0,lefty:a1,rightshoulder:b80,rightstick:b87,righttrigger:b82,rightx:a2,righty:a3,start:b84,x:b76,y:b77,platform:Linux,
+05000000491900000204000000000000,PG-9118,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000004c050000da0c000011010000,Playstation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
+030000004c0500003713000011010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
@@ -734,6 +779,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
030000006f0e00001402000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000008f0e00000300000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
+050000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,
050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,
050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
@@ -755,7 +801,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
030000004c050000e60c000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
+03000000ff000000cb01000010010000,PSP,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,
03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,
+030000009b2800004200000001010000,Raphnet Technologies Dual NES to USB v2.0,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux,
030000009b2800003200000001010000,Raphnet Technologies GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,
030000009b2800006000000001010000,Raphnet Technologies GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,
030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,
@@ -792,7 +840,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
03000000a306000020f6000011010000,Saitek PS2700 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux,
-03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux,
03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000632500007505000010010000,SHANWAN PS3/PC Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000bc2000000055000010010000,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
@@ -800,17 +847,15 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000632500002305000010010000,ShanWan USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000341a00000908000010010000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
030000004c050000e60c000011810000,Sony DualSense,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
-030000004c050000e60c000000006800,Sony DualSense,a:b0,b:b1,x:b2,y:b3,back:b4,guide:b5,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b9,rightshoulder:b10,dpup:b11,dpdown:b12,dpleft:b13,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Linux,
050000004c050000e60c000000810000,Sony DualSense ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
-030000004c050000e60c000000016800,Sony DualSense ,a:b0,b:b1,x:b2,y:b3,back:b4,guide:b5,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b9,rightshoulder:b10,dpup:b11,dpdown:b12,dpleft:b13,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Linux,
03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,
030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000d11800000094000011010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
-03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,
+03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,
03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
@@ -874,6 +919,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+030000005e040000120b000005050000,XBox Series pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400008e02000000010000,xbox360 Wireless EasySMX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
03000000ac0500005b05000010010000,Xiaoji Gamesir-G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
@@ -900,7 +946,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000003512000020ab000000780f00,8BitDo SNES30 Gamepad,a:b21,b:b20,back:b30,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b26,rightshoulder:b27,start:b31,x:b24,y:b23,platform:Android,
05000000c82d000018900000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android,
05000000c82d000030320000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android,
-38383337343564366131323064613561,Brook Mars,a:b1,b:b19,x:b0,y:b2,leftshoulder:b3,rightshoulder:b20,lefttrigger:b9,righttrigger:b10,back:b17,start:b18,leftx:a0,lefty:a1,rightx:a2,righty:a3,leftstick:b15,rightstick:b6,platform:Android,
+38383337343564366131323064613561,Brook Mars,a:b1,b:b19,back:b17,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,
05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android,
0500000031366332860c44aadfff0f00,GS Gamepad,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
@@ -916,9 +962,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000005509000014720000df7f3f00,NVIDIA Controller v01.04,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android,
050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
030000004c050000cc09000000006800,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
+050000004c050000c405000000783f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,
050000004c050000c4050000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
-050000004c050000cc090000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,
+050000004c050000cc090000fffe3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000004c050000cc090000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,
050000004c050000e60c0000fffe3f00,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,
@@ -933,9 +980,14 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000004f0400000ed00000fffe3f00,ThrustMaster eSwap PRO Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android,
30306539356238653637313730656134,Wireless HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android,
+050000005e0400008e02000000783f00,Xbox 360 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
+050000005e040000000b000000783f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,
+050000005e040000e002000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
+050000005e040000ea02000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000005e040000fd020000ff7f3f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000005e040000e00200000ffe3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android,
050000005e040000fd020000ffff3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
+050000005e040000120b000000783f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,
050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000005e04000091020000ff073f00,Xbox Wireless Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,
@@ -953,13 +1005,17 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS,
4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS,
050000004c050000cc090000df070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,
+050000004c050000cc090000df870001,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,
050000004c050000cc090000ff070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,
050000004c050000cc090000ff870001,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,touchpad:b11,x:b2,y:b3,platform:iOS,
050000004c050000cc090000ff876d01,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,
+050000004c050000e60c0000df870000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,touchpad:b10,x:b2,y:b3,platform:iOS,
+050000004c050000e60c0000ff870000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,touchpad:b11,x:b2,y:b3,platform:iOS,
05000000ac0500000300000000006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS,
05000000ac0500000300000043006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS,
05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,
05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,
+050000005e040000050b0000df070001,Xbox Elite Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b10,paddle2:b12,paddle3:b11,paddle4:b13,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,
050000005e040000050b0000ff070001,Xbox Elite Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,
050000005e040000e0020000df070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,
050000005e040000e0020000ff070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,
diff --git a/core/input/godotcontrollerdb.txt b/core/input/godotcontrollerdb.txt
index db612f04d2..5985b121c9 100644
--- a/core/input/godotcontrollerdb.txt
+++ b/core/input/godotcontrollerdb.txt
@@ -29,6 +29,8 @@ Linux0e8f3013,HuiJia USB GamePad,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftshould
Windows0e8f3013,HuiJia USB GamePad,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftshoulder:b6,rightshoulder:b7,dpup:-a1,dpleft:-a0,dpdown:+a1,dpright:+a0,platform:Javascript
MacOSX0e8f3013,HuiJia USB GamePad,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftshoulder:b6,rightshoulder:b7,dpup:-a4,dpleft:-a3,dpdown:+a4,dpright:+a3,platform:Javascript
Linux046dc216,046d-c216-Logitech Logitech Dual Action,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:-a5,dpleft:-a4,dpdown:+a5,dpright:+a4,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Javascript
+Linux20d6a713,Bensussen Deutsch & Associates Inc.(BDA) NSW Wired controller,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:-a5,dpleft:-a4,dpdown:+a5,dpright:+a4,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Javascript
+Linux054c05c4,Sony Computer Entertainment Wireless Controller,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Javascript
# UWP
__UWP_GAMEPAD__,Xbox Controller,a:b2,b:b3,x:b4,y:b5,start:b0,back:b1,leftstick:b12,rightstick:b13,leftshoulder:b10,rightshoulder:b11,dpup:b6,dpdown:b7,dpleft:b8,dpright:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:UWP,
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 6eafec087d..9195f7d8b5 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -85,7 +85,7 @@ Input *Input::get_singleton() {
}
void Input::set_mouse_mode(MouseMode p_mode) {
- ERR_FAIL_INDEX((int)p_mode, 4);
+ ERR_FAIL_INDEX((int)p_mode, 5);
set_mouse_mode_func(p_mode);
}
@@ -101,7 +101,7 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action", "exact_match"), &Input::is_action_just_pressed, DEFVAL(false));
ClassDB::bind_method(D_METHOD("is_action_just_released", "action", "exact_match"), &Input::is_action_just_released, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact_match"), &Input::get_action_strength, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action", "exact_match"), &Input::get_action_strength, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action", "exact_match"), &Input::get_action_raw_strength, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_axis", "negative_action", "positive_action"), &Input::get_axis);
ClassDB::bind_method(D_METHOD("get_vector", "negative_x", "positive_x", "negative_y", "positive_y", "deadzone"), &Input::get_vector, DEFVAL(-1.0f));
ClassDB::bind_method(D_METHOD("add_joy_mapping", "mapping", "update_existing"), &Input::add_joy_mapping, DEFVAL(false));
@@ -138,6 +138,7 @@ void Input::_bind_methods() {
BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN);
BIND_ENUM_CONSTANT(MOUSE_MODE_CAPTURED);
BIND_ENUM_CONSTANT(MOUSE_MODE_CONFINED);
+ BIND_ENUM_CONSTANT(MOUSE_MODE_CONFINED_HIDDEN);
BIND_ENUM_CONSTANT(CURSOR_ARROW);
BIND_ENUM_CONSTANT(CURSOR_IBEAM);
@@ -162,25 +163,23 @@ void Input::_bind_methods() {
void Input::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
#ifdef TOOLS_ENABLED
-
- const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\"";
+ const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
String pf = p_function;
if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" ||
pf == "is_action_just_pressed" || pf == "is_action_just_released" ||
- pf == "get_action_strength" || pf == "get_axis" || pf == "get_vector")) {
+ pf == "get_action_strength" || pf == "get_action_raw_strength" ||
+ pf == "get_axis" || pf == "get_vector")) {
List<PropertyInfo> pinfo;
ProjectSettings::get_singleton()->get_property_list(&pinfo);
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- const PropertyInfo &pi = E->get();
-
+ for (const PropertyInfo &pi : pinfo) {
if (!pi.name.begins_with("input/")) {
continue;
}
String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length());
- r_options->push_back(quote_style + name + quote_style);
+ r_options->push_back(name.quote(quote_style));
}
}
#endif
@@ -221,12 +220,12 @@ Input::SpeedTrack::SpeedTrack() {
reset();
}
-bool Input::is_key_pressed(int p_keycode) const {
+bool Input::is_key_pressed(Key p_keycode) const {
_THREAD_SAFE_METHOD_
return keys_pressed.has(p_keycode);
}
-bool Input::is_mouse_button_pressed(int p_button) const {
+bool Input::is_mouse_button_pressed(MouseButton p_button) const {
_THREAD_SAFE_METHOD_
return (mouse_button_mask & (1 << (p_button - 1))) != 0;
}
@@ -235,24 +234,18 @@ static int _combine_device(int p_value, int p_device) {
return p_value | (p_device << 20);
}
-bool Input::is_joy_button_pressed(int p_device, int p_button) const {
+bool Input::is_joy_button_pressed(int p_device, JoyButton p_button) const {
_THREAD_SAFE_METHOD_
return joy_buttons_pressed.has(_combine_device(p_button, p_device));
}
bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const {
-#ifdef DEBUG_ENABLED
- bool has_action = InputMap::get_singleton()->has_action(p_action);
- ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
-#endif
+ ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
return action_state.has(p_action) && action_state[p_action].pressed && (p_exact ? action_state[p_action].exact : true);
}
bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const {
-#ifdef DEBUG_ENABLED
- bool has_action = InputMap::get_singleton()->has_action(p_action);
- ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
-#endif
+ ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
const Map<StringName, Action>::Element *E = action_state.find(p_action);
if (!E) {
return false;
@@ -270,10 +263,7 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con
}
bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const {
-#ifdef DEBUG_ENABLED
- bool has_action = InputMap::get_singleton()->has_action(p_action);
- ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
-#endif
+ ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
const Map<StringName, Action>::Element *E = action_state.find(p_action);
if (!E) {
return false;
@@ -291,10 +281,7 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co
}
float Input::get_action_strength(const StringName &p_action, bool p_exact) const {
-#ifdef DEBUG_ENABLED
- bool has_action = InputMap::get_singleton()->has_action(p_action);
- ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
-#endif
+ ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action));
const Map<StringName, Action>::Element *E = action_state.find(p_action);
if (!E) {
return 0.0f;
@@ -351,7 +338,7 @@ Vector2 Input::get_vector(const StringName &p_negative_x, const StringName &p_po
return vector;
}
-float Input::get_joy_axis(int p_device, int p_axis) const {
+float Input::get_joy_axis(int p_device, JoyAxis p_axis) const {
_THREAD_SAFE_METHOD_
int c = _combine_device(p_axis, p_device);
if (_joy_axis.has(c)) {
@@ -432,12 +419,12 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, S
joy_buttons_pressed.erase(c);
}
for (int i = 0; i < JOY_AXIS_MAX; i++) {
- set_joy_axis(p_idx, i, 0.0f);
+ set_joy_axis(p_idx, (JoyAxis)i, 0.0f);
}
}
joy_names[p_idx] = js;
- emit_signal("joy_connection_changed", p_idx, p_connected);
+ emit_signal(SNAME("joy_connection_changed"), p_idx, p_connected);
}
Vector3 Input::get_gravity() const {
@@ -460,10 +447,6 @@ Vector3 Input::get_gyroscope() const {
return gyroscope;
}
-void Input::parse_input_event(const Ref<InputEvent> &p_event) {
- _parse_input_event_impl(p_event, false);
-}
-
void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) {
// Notes on mouse-touch emulation:
// - Emulated mouse events are parsed, that is, re-routed to this method, so they make the same effects
@@ -472,8 +455,6 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
// - Emulated touch events are handed right to the main loop (i.e., the SceneTree) because they don't
// require additional handling by this class.
- _THREAD_SAFE_METHOD_
-
Ref<InputEventKey> k = p_event;
if (k.is_valid() && !k->is_echo() && k->get_keycode() != 0) {
if (k->is_pressed()) {
@@ -487,9 +468,9 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
if (mb.is_valid()) {
if (mb->is_pressed()) {
- mouse_button_mask |= (1 << (mb->get_button_index() - 1));
+ mouse_button_mask |= (MouseButton)(1 << (mb->get_button_index() - 1));
} else {
- mouse_button_mask &= ~(1 << (mb->get_button_index() - 1));
+ mouse_button_mask &= (MouseButton) ~(1 << (mb->get_button_index() - 1));
}
Point2 pos = mb->get_global_position();
@@ -499,7 +480,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && mb->get_button_index() == 1) {
Ref<InputEventScreenTouch> touch_event;
- touch_event.instance();
+ touch_event.instantiate();
touch_event->set_pressed(mb->is_pressed());
touch_event->set_position(mb->get_position());
event_dispatch_function(touch_event);
@@ -516,7 +497,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && mm->get_button_mask() & 1) {
Ref<InputEventScreenDrag> drag_event;
- drag_event.instance();
+ drag_event.instantiate();
drag_event->set_position(mm->get_position());
drag_event->set_relative(mm->get_relative());
@@ -554,7 +535,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
if (translate) {
Ref<InputEventMouseButton> button_event;
- button_event.instance();
+ button_event.instantiate();
button_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE);
button_event->set_position(st->get_position());
@@ -562,9 +543,9 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
button_event->set_pressed(st->is_pressed());
button_event->set_button_index(MOUSE_BUTTON_LEFT);
if (st->is_pressed()) {
- button_event->set_button_mask(mouse_button_mask | (1 << (MOUSE_BUTTON_LEFT - 1)));
+ button_event->set_button_mask(MouseButton(mouse_button_mask | MOUSE_BUTTON_MASK_LEFT));
} else {
- button_event->set_button_mask(mouse_button_mask & ~(1 << (MOUSE_BUTTON_LEFT - 1)));
+ button_event->set_button_mask(MouseButton(mouse_button_mask & ~MOUSE_BUTTON_MASK_LEFT));
}
_parse_input_event_impl(button_event, true);
@@ -581,7 +562,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
if (emulate_mouse_from_touch && sd->get_index() == mouse_from_touch_index) {
Ref<InputEventMouseMotion> motion_event;
- motion_event.instance();
+ motion_event.instantiate();
motion_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE);
motion_event->set_position(sd->get_position());
@@ -643,7 +624,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
}
}
-void Input::set_joy_axis(int p_device, int p_axis, float p_value) {
+void Input::set_joy_axis(int p_device, JoyAxis p_axis, float p_value) {
_THREAD_SAFE_METHOD_
int c = _combine_device(p_axis, p_device);
_joy_axis[c] = p_value;
@@ -786,14 +767,14 @@ void Input::ensure_touch_mouse_raised() {
mouse_from_touch_index = -1;
Ref<InputEventMouseButton> button_event;
- button_event.instance();
+ button_event.instantiate();
button_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE);
button_event->set_position(mouse_pos);
button_event->set_global_position(mouse_pos);
button_event->set_pressed(false);
button_event->set_button_index(MOUSE_BUTTON_LEFT);
- button_event->set_button_mask(mouse_button_mask & ~(1 << (MOUSE_BUTTON_LEFT - 1)));
+ button_event->set_button_mask(MouseButton(mouse_button_mask & ~MOUSE_BUTTON_MASK_LEFT));
_parse_input_event_impl(button_event, true);
}
@@ -820,7 +801,7 @@ void Input::set_default_cursor_shape(CursorShape p_shape) {
// The default shape is set in Viewport::_gui_input_event. To instantly
// see the shape in the viewport we need to trigger a mouse motion event.
Ref<InputEventMouseMotion> mm;
- mm.instance();
+ mm.instantiate();
mm->set_position(mouse_pos);
mm->set_global_position(mouse_pos);
parse_input_event(mm);
@@ -838,25 +819,37 @@ void Input::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, co
set_custom_mouse_cursor_func(p_cursor, p_shape, p_hotspot);
}
-void Input::accumulate_input_event(const Ref<InputEvent> &p_event) {
+void Input::parse_input_event(const Ref<InputEvent> &p_event) {
+ _THREAD_SAFE_METHOD_
+
ERR_FAIL_COND(p_event.is_null());
- if (!use_accumulated_input) {
- parse_input_event(p_event);
- return;
+ if (use_accumulated_input) {
+ if (buffered_events.is_empty() || !buffered_events.back()->get()->accumulate(p_event)) {
+ buffered_events.push_back(p_event);
+ }
+ } else if (use_input_buffering) {
+ buffered_events.push_back(p_event);
+ } else {
+ _parse_input_event_impl(p_event, false);
}
- if (!accumulated_events.is_empty() && accumulated_events.back()->get()->accumulate(p_event)) {
- return; //event was accumulated, exit
+}
+
+void Input::flush_buffered_events() {
+ _THREAD_SAFE_METHOD_
+
+ while (buffered_events.front()) {
+ _parse_input_event_impl(buffered_events.front()->get(), false);
+ buffered_events.pop_front();
}
+}
- accumulated_events.push_back(p_event);
+bool Input::is_using_input_buffering() {
+ return use_input_buffering;
}
-void Input::flush_accumulated_events() {
- while (accumulated_events.front()) {
- parse_input_event(accumulated_events.front()->get());
- accumulated_events.pop_front();
- }
+void Input::set_use_input_buffering(bool p_enable) {
+ use_input_buffering = p_enable;
}
void Input::set_use_accumulated_input(bool p_enable) {
@@ -864,7 +857,7 @@ void Input::set_use_accumulated_input(bool p_enable) {
}
void Input::release_pressed_events() {
- flush_accumulated_events(); // this is needed to release actions strengths
+ flush_buffered_events(); // this is needed to release actions strengths
keys_pressed.clear();
joy_buttons_pressed.clear();
@@ -881,7 +874,7 @@ void Input::set_event_dispatch_function(EventDispatchFunc p_function) {
event_dispatch_function = p_function;
}
-void Input::joy_button(int p_device, int p_button, bool p_pressed) {
+void Input::joy_button(int p_device, JoyButton p_button, bool p_pressed) {
_THREAD_SAFE_METHOD_;
Joypad &joy = joy_names[p_device];
//printf("got button %i, mapping is %i\n", p_button, joy.mapping);
@@ -897,17 +890,17 @@ void Input::joy_button(int p_device, int p_button, bool p_pressed) {
JoyEvent map = _get_mapped_button_event(map_db[joy.mapping], p_button);
if (map.type == TYPE_BUTTON) {
- _button_event(p_device, map.index, p_pressed);
+ _button_event(p_device, (JoyButton)map.index, p_pressed);
return;
}
if (map.type == TYPE_AXIS) {
- _axis_event(p_device, map.index, p_pressed ? map.value : 0.0);
+ _axis_event(p_device, (JoyAxis)map.index, p_pressed ? map.value : 0.0);
}
// no event?
}
-void Input::joy_axis(int p_device, int p_axis, const JoyAxisValue &p_value) {
+void Input::joy_axis(int p_device, JoyAxis p_axis, const JoyAxisValue &p_value) {
_THREAD_SAFE_METHOD_;
ERR_FAIL_INDEX(p_axis, JOY_AXIS_MAX);
@@ -948,7 +941,7 @@ void Input::joy_axis(int p_device, int p_axis, const JoyAxisValue &p_value) {
// Button already pressed or released; so ignore.
return;
}
- _button_event(p_device, map.index, pressed);
+ _button_event(p_device, (JoyButton)map.index, pressed);
// Ensure opposite D-Pad button is also released.
switch (map.index) {
@@ -980,7 +973,7 @@ void Input::joy_axis(int p_device, int p_axis, const JoyAxisValue &p_value) {
}
if (map.type == TYPE_AXIS) {
- _axis_event(p_device, map.index, map.value);
+ _axis_event(p_device, (JoyAxis)map.index, map.value);
return;
}
//printf("invalid mapping\n");
@@ -992,24 +985,24 @@ void Input::joy_hat(int p_device, int p_val) {
JoyEvent map[HAT_MAX];
- map[HAT_UP].type = TYPE_BUTTON;
- map[HAT_UP].index = JOY_BUTTON_DPAD_UP;
- map[HAT_UP].value = 0;
+ map[HatDir::HAT_UP].type = TYPE_BUTTON;
+ map[HatDir::HAT_UP].index = JOY_BUTTON_DPAD_UP;
+ map[HatDir::HAT_UP].value = 0;
- map[HAT_RIGHT].type = TYPE_BUTTON;
- map[HAT_RIGHT].index = JOY_BUTTON_DPAD_RIGHT;
- map[HAT_RIGHT].value = 0;
+ map[HatDir::HAT_RIGHT].type = TYPE_BUTTON;
+ map[HatDir::HAT_RIGHT].index = JOY_BUTTON_DPAD_RIGHT;
+ map[HatDir::HAT_RIGHT].value = 0;
- map[HAT_DOWN].type = TYPE_BUTTON;
- map[HAT_DOWN].index = JOY_BUTTON_DPAD_DOWN;
- map[HAT_DOWN].value = 0;
+ map[HatDir::HAT_DOWN].type = TYPE_BUTTON;
+ map[HatDir::HAT_DOWN].index = JOY_BUTTON_DPAD_DOWN;
+ map[HatDir::HAT_DOWN].value = 0;
- map[HAT_LEFT].type = TYPE_BUTTON;
- map[HAT_LEFT].index = JOY_BUTTON_DPAD_LEFT;
- map[HAT_LEFT].value = 0;
+ map[HatDir::HAT_LEFT].type = TYPE_BUTTON;
+ map[HatDir::HAT_LEFT].index = JOY_BUTTON_DPAD_LEFT;
+ map[HatDir::HAT_LEFT].value = 0;
if (joy.mapping != -1) {
- _get_mapped_hat_events(map_db[joy.mapping], 0, map);
+ _get_mapped_hat_events(map_db[joy.mapping], (HatDir)0, map);
}
int cur_val = joy_names[p_device].hat_current;
@@ -1017,10 +1010,10 @@ void Input::joy_hat(int p_device, int p_val) {
for (int hat_direction = 0, hat_mask = 1; hat_direction < HAT_MAX; hat_direction++, hat_mask <<= 1) {
if ((p_val & hat_mask) != (cur_val & hat_mask)) {
if (map[hat_direction].type == TYPE_BUTTON) {
- _button_event(p_device, map[hat_direction].index, p_val & hat_mask);
+ _button_event(p_device, (JoyButton)map[hat_direction].index, p_val & hat_mask);
}
if (map[hat_direction].type == TYPE_AXIS) {
- _axis_event(p_device, map[hat_direction].index, (p_val & hat_mask) ? map[hat_direction].value : 0.0);
+ _axis_event(p_device, (JoyAxis)map[hat_direction].index, (p_val & hat_mask) ? map[hat_direction].value : 0.0);
}
}
}
@@ -1028,9 +1021,9 @@ void Input::joy_hat(int p_device, int p_val) {
joy_names[p_device].hat_current = p_val;
}
-void Input::_button_event(int p_device, int p_index, bool p_pressed) {
+void Input::_button_event(int p_device, JoyButton p_index, bool p_pressed) {
Ref<InputEventJoypadButton> ievent;
- ievent.instance();
+ ievent.instantiate();
ievent->set_device(p_device);
ievent->set_button_index(p_index);
ievent->set_pressed(p_pressed);
@@ -1038,9 +1031,9 @@ void Input::_button_event(int p_device, int p_index, bool p_pressed) {
parse_input_event(ievent);
}
-void Input::_axis_event(int p_device, int p_axis, float p_value) {
+void Input::_axis_event(int p_device, JoyAxis p_axis, float p_value) {
Ref<InputEventJoypadMotion> ievent;
- ievent.instance();
+ ievent.instantiate();
ievent->set_device(p_device);
ievent->set_axis(p_axis);
ievent->set_axis_value(p_value);
@@ -1048,7 +1041,7 @@ void Input::_axis_event(int p_device, int p_axis, float p_value) {
parse_input_event(ievent);
}
-Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, int p_button) {
+Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button) {
JoyEvent event;
event.type = TYPE_MAX;
@@ -1084,7 +1077,7 @@ Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping,
return event;
}
-Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, int p_axis, float p_value) {
+Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value) {
JoyEvent event;
event.type = TYPE_MAX;
@@ -1155,23 +1148,23 @@ Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, i
return event;
}
-void Input::_get_mapped_hat_events(const JoyDeviceMapping &mapping, int p_hat, JoyEvent r_events[]) {
+void Input::_get_mapped_hat_events(const JoyDeviceMapping &mapping, HatDir p_hat, JoyEvent r_events[]) {
for (int i = 0; i < mapping.bindings.size(); i++) {
const JoyBinding binding = mapping.bindings[i];
if (binding.inputType == TYPE_HAT && binding.input.hat.hat == p_hat) {
int hat_direction;
switch (binding.input.hat.hat_mask) {
- case HAT_MASK_UP:
- hat_direction = HAT_UP;
+ case HatMask::HAT_MASK_UP:
+ hat_direction = HatDir::HAT_UP;
break;
- case HAT_MASK_RIGHT:
- hat_direction = HAT_RIGHT;
+ case HatMask::HAT_MASK_RIGHT:
+ hat_direction = HatDir::HAT_RIGHT;
break;
- case HAT_MASK_DOWN:
- hat_direction = HAT_DOWN;
+ case HatMask::HAT_MASK_DOWN:
+ hat_direction = HatDir::HAT_DOWN;
break;
- case HAT_MASK_LEFT:
- hat_direction = HAT_LEFT;
+ case HatMask::HAT_MASK_LEFT:
+ hat_direction = HatDir::HAT_LEFT;
break;
default:
ERR_PRINT_ONCE("Joypad button mapping error.");
@@ -1299,11 +1292,11 @@ void Input::parse_mapping(String p_mapping) {
switch (input[0]) {
case 'b':
binding.inputType = TYPE_BUTTON;
- binding.input.button = input.substr(1).to_int();
+ binding.input.button = (JoyButton)input.substr(1).to_int();
break;
case 'a':
binding.inputType = TYPE_AXIS;
- binding.input.axis.axis = input.substr(1).to_int();
+ binding.input.axis.axis = (JoyAxis)input.substr(1).to_int();
binding.input.axis.range = input_range;
binding.input.axis.invert = invert_axis;
break;
@@ -1311,7 +1304,7 @@ void Input::parse_mapping(String p_mapping) {
ERR_CONTINUE_MSG(input.length() != 4 || input[2] != '.',
String(entry[idx] + "\nInvalid hat input: " + input));
binding.inputType = TYPE_HAT;
- binding.input.hat.hat = input.substr(1, 1).to_int();
+ binding.input.hat.hat = (HatDir)input.substr(1, 1).to_int();
binding.input.hat.hat_mask = static_cast<HatMask>(input.substr(3).to_int());
break;
default:
diff --git a/core/input/input.h b/core/input/input.h
index 99b45db325..6819fc8eb0 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -33,6 +33,7 @@
#include "core/input/input_event.h"
#include "core/object/object.h"
+#include "core/os/keyboard.h"
#include "core/os/thread_safe.h"
class Input : public Object {
@@ -46,7 +47,8 @@ public:
MOUSE_MODE_VISIBLE,
MOUSE_MODE_HIDDEN,
MOUSE_MODE_CAPTURED,
- MOUSE_MODE_CONFINED
+ MOUSE_MODE_CONFINED,
+ MOUSE_MODE_CONFINED_HIDDEN,
};
#undef CursorShape
@@ -71,22 +73,6 @@ public:
CURSOR_MAX
};
- enum HatMask {
- HAT_MASK_CENTER = 0,
- HAT_MASK_UP = 1,
- HAT_MASK_RIGHT = 2,
- HAT_MASK_DOWN = 4,
- HAT_MASK_LEFT = 8,
- };
-
- enum HatDir {
- HAT_UP,
- HAT_RIGHT,
- HAT_DOWN,
- HAT_LEFT,
- HAT_MAX,
- };
-
enum {
JOYPADS_MAX = 16,
};
@@ -125,6 +111,7 @@ private:
bool emulate_touch_from_mouse = false;
bool emulate_mouse_from_touch = false;
+ bool use_input_buffering = false;
bool use_accumulated_input = false;
int mouse_from_touch_index = -1;
@@ -148,7 +135,7 @@ private:
bool connected = false;
bool last_buttons[JOY_BUTTON_MAX] = { false };
float last_axis[JOY_AXIS_MAX] = { 0.0f };
- int last_hat = HAT_MASK_CENTER;
+ int last_hat = HatMask::HAT_MASK_CENTER;
int mapping = -1;
int hat_current = 0;
};
@@ -182,16 +169,16 @@ private:
struct JoyBinding {
JoyType inputType;
union {
- int button;
+ JoyButton button;
struct {
- int axis;
+ JoyAxis axis;
JoyAxisRange range;
bool invert;
} axis;
struct {
- int hat;
+ HatDir hat;
HatMask hat_mask;
} hat;
@@ -217,17 +204,17 @@ private:
Vector<JoyDeviceMapping> map_db;
- JoyEvent _get_mapped_button_event(const JoyDeviceMapping &mapping, int p_button);
- JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, int p_axis, float p_value);
- void _get_mapped_hat_events(const JoyDeviceMapping &mapping, int p_hat, JoyEvent r_events[HAT_MAX]);
+ JoyEvent _get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button);
+ JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value);
+ void _get_mapped_hat_events(const JoyDeviceMapping &mapping, HatDir p_hat, JoyEvent r_events[HAT_MAX]);
JoyButton _get_output_button(String output);
JoyAxis _get_output_axis(String output);
- void _button_event(int p_device, int p_index, bool p_pressed);
- void _axis_event(int p_device, int p_axis, float p_value);
+ void _button_event(int p_device, JoyButton p_index, bool p_pressed);
+ void _axis_event(int p_device, JoyAxis p_axis, float p_value);
void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated);
- List<Ref<InputEvent>> accumulated_events;
+ List<Ref<InputEvent>> buffered_events;
friend class DisplayServer;
@@ -259,9 +246,9 @@ public:
static Input *get_singleton();
- bool is_key_pressed(int p_keycode) const;
- bool is_mouse_button_pressed(int p_button) const;
- bool is_joy_button_pressed(int p_device, int p_button) const;
+ bool is_key_pressed(Key p_keycode) const;
+ bool is_mouse_button_pressed(MouseButton p_button) const;
+ bool is_joy_button_pressed(int p_device, JoyButton p_button) const;
bool is_action_pressed(const StringName &p_action, bool p_exact = false) const;
bool is_action_just_pressed(const StringName &p_action, bool p_exact = false) const;
bool is_action_just_released(const StringName &p_action, bool p_exact = false) const;
@@ -271,7 +258,7 @@ public:
float get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const;
Vector2 get_vector(const StringName &p_negative_x, const StringName &p_positive_x, const StringName &p_negative_y, const StringName &p_positive_y, float p_deadzone = -1.0f) const;
- float get_joy_axis(int p_device, int p_axis) const;
+ float get_joy_axis(int p_device, JoyAxis p_axis) const;
String get_joy_name(int p_idx);
Array get_connected_joypads();
Vector2 get_joy_vibration_strength(int p_device);
@@ -298,7 +285,7 @@ public:
void set_accelerometer(const Vector3 &p_accel);
void set_magnetometer(const Vector3 &p_magnetometer);
void set_gyroscope(const Vector3 &p_gyroscope);
- void set_joy_axis(int p_device, int p_axis, float p_value);
+ void set_joy_axis(int p_device, JoyAxis p_axis, float p_value);
void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0);
void stop_joy_vibration(int p_device);
@@ -324,8 +311,8 @@ public:
void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
void parse_mapping(String p_mapping);
- void joy_button(int p_device, int p_button, bool p_pressed);
- void joy_axis(int p_device, int p_axis, const JoyAxisValue &p_value);
+ void joy_button(int p_device, JoyButton p_button, bool p_pressed);
+ void joy_axis(int p_device, JoyAxis p_axis, const JoyAxisValue &p_value);
void joy_hat(int p_device, int p_val);
void add_joy_mapping(String p_mapping, bool p_update_existing = false);
@@ -337,8 +324,9 @@ public:
String get_joy_guid(int p_device) const;
void set_fallback_mapping(String p_guid);
- void accumulate_input_event(const Ref<InputEvent> &p_event);
- void flush_accumulated_events();
+ void flush_buffered_events();
+ bool is_using_input_buffering();
+ void set_use_input_buffering(bool p_enable);
void set_use_accumulated_input(bool p_enable);
void release_pressed_events();
diff --git a/core/input/input_enums.h b/core/input/input_enums.h
new file mode 100644
index 0000000000..4479a85bfe
--- /dev/null
+++ b/core/input/input_enums.h
@@ -0,0 +1,126 @@
+/*************************************************************************/
+/* input_enums.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef INPUT_ENUMS_H
+#define INPUT_ENUMS_H
+
+enum HatDir {
+ HAT_UP = 0,
+ HAT_RIGHT = 1,
+ HAT_DOWN = 2,
+ HAT_LEFT = 3,
+ HAT_MAX = 4,
+};
+
+enum HatMask {
+ HAT_MASK_CENTER = 0,
+ HAT_MASK_UP = 1,
+ HAT_MASK_RIGHT = 2,
+ HAT_MASK_DOWN = 4,
+ HAT_MASK_LEFT = 8,
+};
+
+enum JoyAxis {
+ JOY_AXIS_INVALID = -1,
+ JOY_AXIS_LEFT_X = 0,
+ JOY_AXIS_LEFT_Y = 1,
+ JOY_AXIS_RIGHT_X = 2,
+ JOY_AXIS_RIGHT_Y = 3,
+ JOY_AXIS_TRIGGER_LEFT = 4,
+ JOY_AXIS_TRIGGER_RIGHT = 5,
+ JOY_AXIS_SDL_MAX = 6,
+ JOY_AXIS_MAX = 10, // OpenVR supports up to 5 Joysticks making a total of 10 axes.
+};
+
+enum JoyButton {
+ JOY_BUTTON_INVALID = -1,
+ JOY_BUTTON_A = 0,
+ JOY_BUTTON_B = 1,
+ JOY_BUTTON_X = 2,
+ JOY_BUTTON_Y = 3,
+ JOY_BUTTON_BACK = 4,
+ JOY_BUTTON_GUIDE = 5,
+ JOY_BUTTON_START = 6,
+ JOY_BUTTON_LEFT_STICK = 7,
+ JOY_BUTTON_RIGHT_STICK = 8,
+ JOY_BUTTON_LEFT_SHOULDER = 9,
+ JOY_BUTTON_RIGHT_SHOULDER = 10,
+ JOY_BUTTON_DPAD_UP = 11,
+ JOY_BUTTON_DPAD_DOWN = 12,
+ JOY_BUTTON_DPAD_LEFT = 13,
+ JOY_BUTTON_DPAD_RIGHT = 14,
+ JOY_BUTTON_MISC1 = 15,
+ JOY_BUTTON_PADDLE1 = 16,
+ JOY_BUTTON_PADDLE2 = 17,
+ JOY_BUTTON_PADDLE3 = 18,
+ JOY_BUTTON_PADDLE4 = 19,
+ JOY_BUTTON_TOUCHPAD = 20,
+ JOY_BUTTON_SDL_MAX = 21,
+ JOY_BUTTON_MAX = 36, // Android supports up to 36 buttons.
+};
+
+enum MIDIMessage {
+ MIDI_MESSAGE_NONE = 0,
+ MIDI_MESSAGE_NOTE_OFF = 0x8,
+ MIDI_MESSAGE_NOTE_ON = 0x9,
+ MIDI_MESSAGE_AFTERTOUCH = 0xA,
+ MIDI_MESSAGE_CONTROL_CHANGE = 0xB,
+ MIDI_MESSAGE_PROGRAM_CHANGE = 0xC,
+ MIDI_MESSAGE_CHANNEL_PRESSURE = 0xD,
+ MIDI_MESSAGE_PITCH_BEND = 0xE,
+};
+
+enum MouseButton {
+ MOUSE_BUTTON_NONE = 0,
+ MOUSE_BUTTON_LEFT = 1,
+ MOUSE_BUTTON_RIGHT = 2,
+ MOUSE_BUTTON_MIDDLE = 3,
+ MOUSE_BUTTON_WHEEL_UP = 4,
+ MOUSE_BUTTON_WHEEL_DOWN = 5,
+ MOUSE_BUTTON_WHEEL_LEFT = 6,
+ MOUSE_BUTTON_WHEEL_RIGHT = 7,
+ MOUSE_BUTTON_XBUTTON1 = 8,
+ MOUSE_BUTTON_XBUTTON2 = 9,
+ MOUSE_BUTTON_MASK_LEFT = (1 << (MOUSE_BUTTON_LEFT - 1)),
+ MOUSE_BUTTON_MASK_RIGHT = (1 << (MOUSE_BUTTON_RIGHT - 1)),
+ MOUSE_BUTTON_MASK_MIDDLE = (1 << (MOUSE_BUTTON_MIDDLE - 1)),
+ MOUSE_BUTTON_MASK_XBUTTON1 = (1 << (MOUSE_BUTTON_XBUTTON1 - 1)),
+ MOUSE_BUTTON_MASK_XBUTTON2 = (1 << (MOUSE_BUTTON_XBUTTON2 - 1)),
+};
+
+inline MouseButton &operator|=(MouseButton &a, MouseButton b) {
+ return (MouseButton &)((int &)a |= (int)b);
+}
+
+inline MouseButton &operator&=(MouseButton &a, MouseButton b) {
+ return (MouseButton &)((int &)a &= (int)b);
+}
+
+#endif // INPUT_ENUMS_H
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index 6f063c217f..50b2099236 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -31,6 +31,7 @@
#include "input_event.h"
#include "core/input/input_map.h"
+#include "core/input/shortcut.h"
#include "core/os/keyboard.h"
const int InputEvent::DEVICE_ID_TOUCH_MOUSE = -1;
@@ -38,6 +39,7 @@ const int InputEvent::DEVICE_ID_INTERNAL = -2;
void InputEvent::set_device(int p_device) {
device = p_device;
+ emit_changed();
}
int InputEvent::get_device() const {
@@ -88,7 +90,7 @@ bool InputEvent::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, f
return false;
}
-bool InputEvent::shortcut_match(const Ref<InputEvent> &p_event) const {
+bool InputEvent::is_match(const Ref<InputEvent> &p_event, bool p_exact_match) const {
return false;
}
@@ -110,7 +112,7 @@ void InputEvent::_bind_methods() {
ClassDB::bind_method(D_METHOD("as_text"), &InputEvent::as_text);
- ClassDB::bind_method(D_METHOD("shortcut_match", "event"), &InputEvent::shortcut_match);
+ ClassDB::bind_method(D_METHOD("is_match", "event", "exact_match"), &InputEvent::is_match, DEFVAL(true));
ClassDB::bind_method(D_METHOD("is_action_type"), &InputEvent::is_action_type);
@@ -131,6 +133,7 @@ void InputEventFromWindow::_bind_methods() {
void InputEventFromWindow::set_window_id(int64_t p_id) {
window_id = p_id;
+ emit_changed();
}
int64_t InputEventFromWindow::get_window_id() const {
@@ -141,6 +144,7 @@ int64_t InputEventFromWindow::get_window_id() const {
void InputEventWithModifiers::set_store_command(bool p_enabled) {
store_command = p_enabled;
+ emit_changed();
}
bool InputEventWithModifiers::is_storing_command() const {
@@ -149,6 +153,7 @@ bool InputEventWithModifiers::is_storing_command() const {
void InputEventWithModifiers::set_shift_pressed(bool p_enabled) {
shift_pressed = p_enabled;
+ emit_changed();
}
bool InputEventWithModifiers::is_shift_pressed() const {
@@ -157,6 +162,7 @@ bool InputEventWithModifiers::is_shift_pressed() const {
void InputEventWithModifiers::set_alt_pressed(bool p_enabled) {
alt_pressed = p_enabled;
+ emit_changed();
}
bool InputEventWithModifiers::is_alt_pressed() const {
@@ -165,6 +171,7 @@ bool InputEventWithModifiers::is_alt_pressed() const {
void InputEventWithModifiers::set_ctrl_pressed(bool p_enabled) {
ctrl_pressed = p_enabled;
+ emit_changed();
}
bool InputEventWithModifiers::is_ctrl_pressed() const {
@@ -173,6 +180,7 @@ bool InputEventWithModifiers::is_ctrl_pressed() const {
void InputEventWithModifiers::set_meta_pressed(bool p_enabled) {
meta_pressed = p_enabled;
+ emit_changed();
}
bool InputEventWithModifiers::is_meta_pressed() const {
@@ -181,6 +189,7 @@ bool InputEventWithModifiers::is_meta_pressed() const {
void InputEventWithModifiers::set_command_pressed(bool p_enabled) {
command_pressed = p_enabled;
+ emit_changed();
}
bool InputEventWithModifiers::is_command_pressed() const {
@@ -194,6 +203,23 @@ void InputEventWithModifiers::set_modifiers_from_event(const InputEventWithModif
set_meta_pressed(event->is_meta_pressed());
}
+uint32_t InputEventWithModifiers::get_modifiers_mask() const {
+ uint32_t mask = 0;
+ if (is_ctrl_pressed()) {
+ mask |= KEY_MASK_CTRL;
+ }
+ if (is_shift_pressed()) {
+ mask |= KEY_MASK_SHIFT;
+ }
+ if (is_alt_pressed()) {
+ mask |= KEY_MASK_ALT;
+ }
+ if (is_meta_pressed()) {
+ mask |= KEY_MASK_META;
+ }
+ return mask;
+}
+
String InputEventWithModifiers::as_text() const {
Vector<String> mod_names;
@@ -274,30 +300,34 @@ void InputEventWithModifiers::_validate_property(PropertyInfo &property) const {
void InputEventKey::set_pressed(bool p_pressed) {
pressed = p_pressed;
+ emit_changed();
}
bool InputEventKey::is_pressed() const {
return pressed;
}
-void InputEventKey::set_keycode(uint32_t p_keycode) {
+void InputEventKey::set_keycode(Key p_keycode) {
keycode = p_keycode;
+ emit_changed();
}
-uint32_t InputEventKey::get_keycode() const {
+Key InputEventKey::get_keycode() const {
return keycode;
}
-void InputEventKey::set_physical_keycode(uint32_t p_keycode) {
+void InputEventKey::set_physical_keycode(Key p_keycode) {
physical_keycode = p_keycode;
+ emit_changed();
}
-uint32_t InputEventKey::get_physical_keycode() const {
+Key InputEventKey::get_physical_keycode() const {
return physical_keycode;
}
void InputEventKey::set_unicode(uint32_t p_unicode) {
unicode = p_unicode;
+ emit_changed();
}
uint32_t InputEventKey::get_unicode() const {
@@ -306,6 +336,7 @@ uint32_t InputEventKey::get_unicode() const {
void InputEventKey::set_echo(bool p_enable) {
echo = p_enable;
+ emit_changed();
}
bool InputEventKey::is_echo() const {
@@ -313,39 +344,11 @@ bool InputEventKey::is_echo() const {
}
uint32_t InputEventKey::get_keycode_with_modifiers() const {
- uint32_t sc = keycode;
- if (is_ctrl_pressed()) {
- sc |= KEY_MASK_CTRL;
- }
- if (is_alt_pressed()) {
- sc |= KEY_MASK_ALT;
- }
- if (is_shift_pressed()) {
- sc |= KEY_MASK_SHIFT;
- }
- if (is_meta_pressed()) {
- sc |= KEY_MASK_META;
- }
-
- return sc;
+ return keycode | get_modifiers_mask();
}
uint32_t InputEventKey::get_physical_keycode_with_modifiers() const {
- uint32_t sc = physical_keycode;
- if (is_ctrl_pressed()) {
- sc |= KEY_MASK_CTRL;
- }
- if (is_alt_pressed()) {
- sc |= KEY_MASK_ALT;
- }
- if (is_shift_pressed()) {
- sc |= KEY_MASK_SHIFT;
- }
- if (is_meta_pressed()) {
- sc |= KEY_MASK_META;
- }
-
- return sc;
+ return physical_keycode | get_modifiers_mask();
}
String InputEventKey::as_text() const {
@@ -384,9 +387,9 @@ String InputEventKey::to_string() {
return vformat("InputEventKey: keycode=%s, mods=%s, physical=%s, pressed=%s, echo=%s", kc, mods, physical, p, e);
}
-Ref<InputEventKey> InputEventKey::create_reference(uint32_t p_keycode) {
+Ref<InputEventKey> InputEventKey::create_reference(Key p_keycode) {
Ref<InputEventKey> ie;
- ie.instance();
+ ie.instantiate();
ie->set_keycode(p_keycode & KEY_CODE_MASK);
ie->set_unicode(p_keycode & KEY_CODE_MASK);
@@ -428,10 +431,11 @@ bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool *p_pressed
match = get_keycode() == key->get_keycode() && (!key->is_pressed() || (code & event_code) == code);
}
if (match) {
+ bool pressed = key->is_pressed();
if (p_pressed != nullptr) {
- *p_pressed = key->is_pressed();
+ *p_pressed = pressed;
}
- float strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f;
+ float strength = pressed ? 1.0f : 0.0f;
if (p_strength != nullptr) {
*p_strength = strength;
}
@@ -442,16 +446,14 @@ bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool *p_pressed
return match;
}
-bool InputEventKey::shortcut_match(const Ref<InputEvent> &p_event) const {
+bool InputEventKey::is_match(const Ref<InputEvent> &p_event, bool p_exact_match) const {
Ref<InputEventKey> key = p_event;
if (key.is_null()) {
return false;
}
- uint32_t code = get_keycode_with_modifiers();
- uint32_t event_code = key->get_keycode_with_modifiers();
-
- return code == event_code;
+ return keycode == key->keycode &&
+ (!p_exact_match || get_modifiers_mask() == key->get_modifiers_mask());
}
void InputEventKey::_bind_methods() {
@@ -482,6 +484,7 @@ void InputEventKey::_bind_methods() {
void InputEventMouse::set_button_mask(int p_mask) {
button_mask = p_mask;
+ emit_changed();
}
int InputEventMouse::get_button_mask() const {
@@ -529,11 +532,12 @@ float InputEventMouseButton::get_factor() const {
return factor;
}
-void InputEventMouseButton::set_button_index(int p_index) {
+void InputEventMouseButton::set_button_index(MouseButton p_index) {
button_index = p_index;
+ emit_changed();
}
-int InputEventMouseButton::get_button_index() const {
+MouseButton InputEventMouseButton::get_button_index() const {
return button_index;
}
@@ -558,7 +562,7 @@ Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, co
Vector2 l = p_xform.xform(get_position() + p_local_ofs);
Ref<InputEventMouseButton> mb;
- mb.instance();
+ mb.instantiate();
mb->set_device(get_device());
mb->set_window_id(get_window_id());
@@ -584,10 +588,11 @@ bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event, bool *p
bool match = mb->button_index == button_index;
if (match) {
+ bool pressed = mb->is_pressed();
if (p_pressed != nullptr) {
- *p_pressed = mb->is_pressed();
+ *p_pressed = pressed;
}
- float strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f;
+ float strength = pressed ? 1.0f : 0.0f;
if (p_strength != nullptr) {
*p_strength = strength;
}
@@ -599,6 +604,16 @@ bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event, bool *p
return match;
}
+bool InputEventMouseButton::is_match(const Ref<InputEvent> &p_event, bool p_exact_match) const {
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_null()) {
+ return false;
+ }
+
+ return button_index == mb->button_index &&
+ (!p_exact_match || get_modifiers_mask() == mb->get_modifiers_mask());
+}
+
static const char *_mouse_button_descriptions[9] = {
TTRC("Left Mouse Button"),
TTRC("Right Mouse Button"),
@@ -734,7 +749,7 @@ Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, co
Vector2 s = p_xform.basis_xform(get_speed());
Ref<InputEventMouseMotion> mm;
- mm.instance();
+ mm.instantiate();
mm->set_device(get_device());
mm->set_window_id(get_window_id());
@@ -848,16 +863,18 @@ void InputEventMouseMotion::_bind_methods() {
///////////////////////////////////
-void InputEventJoypadMotion::set_axis(int p_axis) {
+void InputEventJoypadMotion::set_axis(JoyAxis p_axis) {
axis = p_axis;
+ emit_changed();
}
-int InputEventJoypadMotion::get_axis() const {
+JoyAxis InputEventJoypadMotion::get_axis() const {
return axis;
}
void InputEventJoypadMotion::set_axis_value(float p_value) {
axis_value = p_value;
+ emit_changed();
}
float InputEventJoypadMotion::get_axis_value() const {
@@ -904,6 +921,16 @@ bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool *
return match;
}
+bool InputEventJoypadMotion::is_match(const Ref<InputEvent> &p_event, bool p_exact_match) const {
+ Ref<InputEventJoypadMotion> jm = p_event;
+ if (jm.is_null()) {
+ return false;
+ }
+
+ return axis == jm->axis &&
+ (!p_exact_match || ((axis_value < 0) == (jm->axis_value < 0)));
+}
+
static const char *_joy_axis_descriptions[JOY_AXIS_MAX] = {
TTRC("Left Stick X-Axis, Joystick 0 X-Axis"),
TTRC("Left Stick Y-Axis, Joystick 0 Y-Axis"),
@@ -940,11 +967,12 @@ void InputEventJoypadMotion::_bind_methods() {
///////////////////////////////////
-void InputEventJoypadButton::set_button_index(int p_index) {
+void InputEventJoypadButton::set_button_index(JoyButton p_index) {
button_index = p_index;
+ emit_changed();
}
-int InputEventJoypadButton::get_button_index() const {
+JoyButton InputEventJoypadButton::get_button_index() const {
return button_index;
}
@@ -972,10 +1000,11 @@ bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool *
bool match = button_index == jb->button_index;
if (match) {
+ bool pressed = jb->is_pressed();
if (p_pressed != nullptr) {
- *p_pressed = jb->is_pressed();
+ *p_pressed = pressed;
}
- float strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f;
+ float strength = pressed ? 1.0f : 0.0f;
if (p_strength != nullptr) {
*p_strength = strength;
}
@@ -987,7 +1016,7 @@ bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool *
return match;
}
-bool InputEventJoypadButton::shortcut_match(const Ref<InputEvent> &p_event) const {
+bool InputEventJoypadButton::is_match(const Ref<InputEvent> &p_event, bool p_exact_match) const {
Ref<InputEventJoypadButton> button = p_event;
if (button.is_null()) {
return false;
@@ -1023,7 +1052,7 @@ static const char *_joy_button_descriptions[JOY_BUTTON_SDL_MAX] = {
String InputEventJoypadButton::as_text() const {
String text = "Joypad Button " + itos(button_index);
- if (button_index < JOY_BUTTON_SDL_MAX) {
+ if (button_index >= 0 && button_index < JOY_BUTTON_SDL_MAX) {
text += vformat(" (%s)", _joy_button_descriptions[button_index]);
}
@@ -1039,9 +1068,9 @@ String InputEventJoypadButton::to_string() {
return vformat("InputEventJoypadButton: button_index=%d, pressed=%s, pressure=%.2f", button_index, p, pressure);
}
-Ref<InputEventJoypadButton> InputEventJoypadButton::create_reference(int p_btn_index) {
+Ref<InputEventJoypadButton> InputEventJoypadButton::create_reference(JoyButton p_btn_index) {
Ref<InputEventJoypadButton> ie;
- ie.instance();
+ ie.instantiate();
ie->set_button_index(p_btn_index);
return ie;
@@ -1090,7 +1119,7 @@ bool InputEventScreenTouch::is_pressed() const {
Ref<InputEvent> InputEventScreenTouch::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const {
Ref<InputEventScreenTouch> st;
- st.instance();
+ st.instantiate();
st->set_device(get_device());
st->set_window_id(get_window_id());
st->set_index(index);
@@ -1163,7 +1192,7 @@ Vector2 InputEventScreenDrag::get_speed() const {
Ref<InputEvent> InputEventScreenDrag::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const {
Ref<InputEventScreenDrag> sd;
- sd.instance();
+ sd.instantiate();
sd->set_device(get_device());
sd->set_window_id(get_window_id());
@@ -1184,6 +1213,22 @@ String InputEventScreenDrag::to_string() {
return vformat("InputEventScreenDrag: index=%d, position=(%s), relative=(%s), speed=(%s)", index, String(get_position()), String(get_relative()), String(get_speed()));
}
+bool InputEventScreenDrag::accumulate(const Ref<InputEvent> &p_event) {
+ Ref<InputEventScreenDrag> drag = p_event;
+ if (drag.is_null())
+ return false;
+
+ if (get_index() != drag->get_index()) {
+ return false;
+ }
+
+ set_position(drag->get_position());
+ set_speed(drag->get_speed());
+ relative += drag->get_relative();
+
+ return true;
+}
+
void InputEventScreenDrag::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_index", "index"), &InputEventScreenDrag::set_index);
ClassDB::bind_method(D_METHOD("get_index"), &InputEventScreenDrag::get_index);
@@ -1229,7 +1274,7 @@ float InputEventAction::get_strength() const {
return strength;
}
-bool InputEventAction::shortcut_match(const Ref<InputEvent> &p_event) const {
+bool InputEventAction::is_match(const Ref<InputEvent> &p_event, bool p_exact_match) const {
if (p_event.is_null()) {
return false;
}
@@ -1249,10 +1294,11 @@ bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool *p_pres
bool match = action == act->action;
if (match) {
+ bool pressed = act->pressed;
if (p_pressed != nullptr) {
- *p_pressed = act->pressed;
+ *p_pressed = pressed;
}
- float strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f;
+ float strength = pressed ? 1.0f : 0.0f;
if (p_strength != nullptr) {
*p_strength = strength;
}
@@ -1318,7 +1364,7 @@ real_t InputEventMagnifyGesture::get_factor() const {
Ref<InputEvent> InputEventMagnifyGesture::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const {
Ref<InputEventMagnifyGesture> ev;
- ev.instance();
+ ev.instantiate();
ev->set_device(get_device());
ev->set_window_id(get_window_id());
@@ -1358,7 +1404,7 @@ Vector2 InputEventPanGesture::get_delta() const {
Ref<InputEvent> InputEventPanGesture::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const {
Ref<InputEventPanGesture> ev;
- ev.instance();
+ ev.instantiate();
ev->set_device(get_device());
ev->set_window_id(get_window_id());
@@ -1396,11 +1442,11 @@ int InputEventMIDI::get_channel() const {
return channel;
}
-void InputEventMIDI::set_message(const int p_message) {
+void InputEventMIDI::set_message(const MIDIMessage p_message) {
message = p_message;
}
-int InputEventMIDI::get_message() const {
+MIDIMessage InputEventMIDI::get_message() const {
return message;
}
@@ -1487,3 +1533,33 @@ void InputEventMIDI::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_number"), "set_controller_number", "get_controller_number");
ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_value"), "set_controller_value", "get_controller_value");
}
+
+///////////////////////////////////
+
+void InputEventShortcut::set_shortcut(Ref<Shortcut> p_shortcut) {
+ shortcut = p_shortcut;
+ emit_changed();
+}
+
+Ref<Shortcut> InputEventShortcut::get_shortcut() {
+ return shortcut;
+}
+
+void InputEventShortcut::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_shortcut", "shortcut"), &InputEventShortcut::set_shortcut);
+ ClassDB::bind_method(D_METHOD("get_shortcut"), &InputEventShortcut::get_shortcut);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "Shortcut"), "set_shortcut", "get_shortcut");
+}
+
+bool InputEventShortcut::is_pressed() const {
+ return true;
+}
+
+String InputEventShortcut::as_text() const {
+ return vformat(RTR("Input Event with Shortcut=%s"), shortcut->get_as_text());
+}
+
+String InputEventShortcut::to_string() {
+ return vformat("InputEventShortcut: shortcut=%s", shortcut->get_as_text());
+}
diff --git a/core/input/input_event.h b/core/input/input_event.h
index eed0d79326..3fc8078a09 100644
--- a/core/input/input_event.h
+++ b/core/input/input_event.h
@@ -31,8 +31,10 @@
#ifndef INPUT_EVENT_H
#define INPUT_EVENT_H
+#include "core/input/input_enums.h"
#include "core/io/resource.h"
#include "core/math/transform_2d.h"
+#include "core/os/keyboard.h"
#include "core/string/ustring.h"
#include "core/typedefs.h"
@@ -41,71 +43,7 @@
* The events are pretty obvious.
*/
-enum MouseButton {
- MOUSE_BUTTON_LEFT = 1,
- MOUSE_BUTTON_RIGHT = 2,
- MOUSE_BUTTON_MIDDLE = 3,
- MOUSE_BUTTON_WHEEL_UP = 4,
- MOUSE_BUTTON_WHEEL_DOWN = 5,
- MOUSE_BUTTON_WHEEL_LEFT = 6,
- MOUSE_BUTTON_WHEEL_RIGHT = 7,
- MOUSE_BUTTON_XBUTTON1 = 8,
- MOUSE_BUTTON_XBUTTON2 = 9,
- MOUSE_BUTTON_MASK_LEFT = (1 << (MOUSE_BUTTON_LEFT - 1)),
- MOUSE_BUTTON_MASK_RIGHT = (1 << (MOUSE_BUTTON_RIGHT - 1)),
- MOUSE_BUTTON_MASK_MIDDLE = (1 << (MOUSE_BUTTON_MIDDLE - 1)),
- MOUSE_BUTTON_MASK_XBUTTON1 = (1 << (MOUSE_BUTTON_XBUTTON1 - 1)),
- MOUSE_BUTTON_MASK_XBUTTON2 = (1 << (MOUSE_BUTTON_XBUTTON2 - 1))
-};
-
-enum JoyButton {
- JOY_BUTTON_INVALID = -1,
- JOY_BUTTON_A = 0,
- JOY_BUTTON_B = 1,
- JOY_BUTTON_X = 2,
- JOY_BUTTON_Y = 3,
- JOY_BUTTON_BACK = 4,
- JOY_BUTTON_GUIDE = 5,
- JOY_BUTTON_START = 6,
- JOY_BUTTON_LEFT_STICK = 7,
- JOY_BUTTON_RIGHT_STICK = 8,
- JOY_BUTTON_LEFT_SHOULDER = 9,
- JOY_BUTTON_RIGHT_SHOULDER = 10,
- JOY_BUTTON_DPAD_UP = 11,
- JOY_BUTTON_DPAD_DOWN = 12,
- JOY_BUTTON_DPAD_LEFT = 13,
- JOY_BUTTON_DPAD_RIGHT = 14,
- JOY_BUTTON_MISC1 = 15,
- JOY_BUTTON_PADDLE1 = 16,
- JOY_BUTTON_PADDLE2 = 17,
- JOY_BUTTON_PADDLE3 = 18,
- JOY_BUTTON_PADDLE4 = 19,
- JOY_BUTTON_TOUCHPAD = 20,
- JOY_BUTTON_SDL_MAX = 21,
- JOY_BUTTON_MAX = 36, // Android supports up to 36 buttons.
-};
-
-enum JoyAxis {
- JOY_AXIS_INVALID = -1,
- JOY_AXIS_LEFT_X = 0,
- JOY_AXIS_LEFT_Y = 1,
- JOY_AXIS_RIGHT_X = 2,
- JOY_AXIS_RIGHT_Y = 3,
- JOY_AXIS_TRIGGER_LEFT = 4,
- JOY_AXIS_TRIGGER_RIGHT = 5,
- JOY_AXIS_SDL_MAX = 6,
- JOY_AXIS_MAX = 10, // OpenVR supports up to 5 Joysticks making a total of 10 axes.
-};
-
-enum MIDIMessage {
- MIDI_MESSAGE_NOTE_OFF = 0x8,
- MIDI_MESSAGE_NOTE_ON = 0x9,
- MIDI_MESSAGE_AFTERTOUCH = 0xA,
- MIDI_MESSAGE_CONTROL_CHANGE = 0xB,
- MIDI_MESSAGE_PROGRAM_CHANGE = 0xC,
- MIDI_MESSAGE_CHANNEL_PRESSURE = 0xD,
- MIDI_MESSAGE_PITCH_BEND = 0xE,
-};
+class Shortcut;
/**
* Input Modifier Status
@@ -142,7 +80,8 @@ public:
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const;
virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const;
- virtual bool shortcut_match(const Ref<InputEvent> &p_event) const;
+ virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const;
+
virtual bool is_action_type() const;
virtual bool accumulate(const Ref<InputEvent> &p_event) { return false; }
@@ -212,6 +151,8 @@ public:
void set_modifiers_from_event(const InputEventWithModifiers *event);
+ uint32_t get_modifiers_mask() const;
+
virtual String as_text() const override;
virtual String to_string() override;
@@ -223,8 +164,8 @@ class InputEventKey : public InputEventWithModifiers {
bool pressed = false; /// otherwise release
- uint32_t keycode = 0; ///< check keyboard.h , KeyCode enum, without modifier masks
- uint32_t physical_keycode = 0;
+ Key keycode = KEY_NONE; // Key enum, without modifier masks.
+ Key physical_keycode = KEY_NONE;
uint32_t unicode = 0; ///unicode
bool echo = false; /// true if this is an echo key
@@ -236,11 +177,11 @@ public:
void set_pressed(bool p_pressed);
virtual bool is_pressed() const override;
- void set_keycode(uint32_t p_keycode);
- uint32_t get_keycode() const;
+ void set_keycode(Key p_keycode);
+ Key get_keycode() const;
- void set_physical_keycode(uint32_t p_keycode);
- uint32_t get_physical_keycode() const;
+ void set_physical_keycode(Key p_keycode);
+ Key get_physical_keycode() const;
void set_unicode(uint32_t p_unicode);
uint32_t get_unicode() const;
@@ -252,14 +193,14 @@ public:
uint32_t get_physical_keycode_with_modifiers() const;
virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override;
- virtual bool shortcut_match(const Ref<InputEvent> &p_event) const override;
+ virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override;
virtual bool is_action_type() const override { return true; }
virtual String as_text() const override;
virtual String to_string() override;
- static Ref<InputEventKey> create_reference(uint32_t p_keycode_with_modifier_masks);
+ static Ref<InputEventKey> create_reference(Key p_keycode_with_modifier_masks);
InputEventKey() {}
};
@@ -292,7 +233,7 @@ class InputEventMouseButton : public InputEventMouse {
GDCLASS(InputEventMouseButton, InputEventMouse);
float factor = 1;
- int button_index = 0;
+ MouseButton button_index = MOUSE_BUTTON_NONE;
bool pressed = false; //otherwise released
bool double_click = false; //last even less than double click time
@@ -303,8 +244,8 @@ public:
void set_factor(float p_factor);
float get_factor() const;
- void set_button_index(int p_index);
- int get_button_index() const;
+ void set_button_index(MouseButton p_index);
+ MouseButton get_button_index() const;
void set_pressed(bool p_pressed);
virtual bool is_pressed() const override;
@@ -313,7 +254,9 @@ public:
bool is_double_click() const;
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
+
virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override;
+ virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override;
virtual bool is_action_type() const override { return true; }
virtual String as_text() const override;
@@ -357,15 +300,15 @@ public:
class InputEventJoypadMotion : public InputEvent {
GDCLASS(InputEventJoypadMotion, InputEvent);
- int axis = 0; ///< Joypad axis
+ JoyAxis axis = (JoyAxis)0; ///< Joypad axis
float axis_value = 0; ///< -1 to 1
protected:
static void _bind_methods();
public:
- void set_axis(int p_axis);
- int get_axis() const;
+ void set_axis(JoyAxis p_axis);
+ JoyAxis get_axis() const;
void set_axis_value(float p_value);
float get_axis_value() const;
@@ -373,6 +316,7 @@ public:
virtual bool is_pressed() const override;
virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override;
+ virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override;
virtual bool is_action_type() const override { return true; }
virtual String as_text() const override;
@@ -384,15 +328,15 @@ public:
class InputEventJoypadButton : public InputEvent {
GDCLASS(InputEventJoypadButton, InputEvent);
- int button_index = 0;
+ JoyButton button_index = (JoyButton)0;
bool pressed = false;
float pressure = 0; //0 to 1
protected:
static void _bind_methods();
public:
- void set_button_index(int p_index);
- int get_button_index() const;
+ void set_button_index(JoyButton p_index);
+ JoyButton get_button_index() const;
void set_pressed(bool p_pressed);
virtual bool is_pressed() const override;
@@ -401,13 +345,14 @@ public:
float get_pressure() const;
virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override;
- virtual bool shortcut_match(const Ref<InputEvent> &p_event) const override;
+ virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override;
virtual bool is_action_type() const override { return true; }
+
virtual String as_text() const override;
virtual String to_string() override;
- static Ref<InputEventJoypadButton> create_reference(int p_btn_index);
+ static Ref<InputEventJoypadButton> create_reference(JoyButton p_btn_index);
InputEventJoypadButton() {}
};
@@ -465,6 +410,8 @@ public:
virtual String as_text() const override;
virtual String to_string() override;
+ virtual bool accumulate(const Ref<InputEvent> &p_event) override;
+
InputEventScreenDrag() {}
};
@@ -491,9 +438,10 @@ public:
virtual bool is_action(const StringName &p_action) const;
virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override;
+ virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override;
- virtual bool shortcut_match(const Ref<InputEvent> &p_event) const override;
virtual bool is_action_type() const override { return true; }
+
virtual String as_text() const override;
virtual String to_string() override;
@@ -553,7 +501,7 @@ class InputEventMIDI : public InputEvent {
GDCLASS(InputEventMIDI, InputEvent);
int channel = 0;
- int message = 0;
+ MIDIMessage message = MIDI_MESSAGE_NONE;
int pitch = 0;
int velocity = 0;
int instrument = 0;
@@ -568,8 +516,8 @@ public:
void set_channel(const int p_channel);
int get_channel() const;
- void set_message(const int p_message);
- int get_message() const;
+ void set_message(const MIDIMessage p_message);
+ MIDIMessage get_message() const;
void set_pitch(const int p_pitch);
int get_pitch() const;
@@ -595,4 +543,21 @@ public:
InputEventMIDI() {}
};
+class InputEventShortcut : public InputEvent {
+ GDCLASS(InputEventShortcut, InputEvent);
+
+ Ref<Shortcut> shortcut;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_shortcut(Ref<Shortcut> p_shortcut);
+ Ref<Shortcut> get_shortcut();
+ virtual bool is_pressed() const override;
+
+ virtual String as_text() const override;
+ virtual String to_string() override;
+};
+
#endif // INPUT_EVENT_H
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index 878ce820fb..c6db7be53a 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -45,6 +45,7 @@ void InputMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action);
ClassDB::bind_method(D_METHOD("action_set_deadzone", "action", "deadzone"), &InputMap::action_set_deadzone);
+ ClassDB::bind_method(D_METHOD("action_get_deadzone", "action"), &InputMap::action_get_deadzone);
ClassDB::bind_method(D_METHOD("action_add_event", "action", "event"), &InputMap::action_add_event);
ClassDB::bind_method(D_METHOD("action_has_event", "action", "event"), &InputMap::action_has_event);
ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event);
@@ -58,17 +59,17 @@ void InputMap::_bind_methods() {
* Returns an nonexistent action error message with a suggestion of the closest
* matching action name (if possible).
*/
-String InputMap::_suggest_actions(const StringName &p_action) const {
+String InputMap::suggest_actions(const StringName &p_action) const {
List<StringName> actions = get_actions();
StringName closest_action;
float closest_similarity = 0.0;
// Find the most action with the most similar name.
- for (List<StringName>::Element *E = actions.front(); E; E = E->next()) {
- const float similarity = String(E->get()).similarity(p_action);
+ for (const StringName &action : actions) {
+ const float similarity = String(action).similarity(p_action);
if (similarity > closest_similarity) {
- closest_action = E->get();
+ closest_action = action;
closest_similarity = similarity;
}
}
@@ -92,7 +93,7 @@ void InputMap::add_action(const StringName &p_action, float p_deadzone) {
}
void InputMap::erase_action(const StringName &p_action) {
- ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action));
input_map.erase(p_action);
}
@@ -104,8 +105,8 @@ Array InputMap::_get_actions() {
return ret;
}
- for (const List<StringName>::Element *E = actions.front(); E; E = E->next()) {
- ret.push_back(E->get());
+ for (const StringName &E : actions) {
+ ret.push_back(E);
}
return ret;
@@ -128,16 +129,11 @@ List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Re
ERR_FAIL_COND_V(!p_event.is_valid(), nullptr);
for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) {
- const Ref<InputEvent> e = E->get();
-
- //if (e.type != Ref<InputEvent>::KEY && e.device != p_event.device) -- unsure about the KEY comparison, why is this here?
- // continue;
-
- int device = e->get_device();
+ int device = E->get()->get_device();
if (device == ALL_DEVICES || device == p_event->get_device()) {
- if (p_exact_match && e->shortcut_match(p_event)) {
+ if (p_exact_match && E->get()->is_match(p_event, true)) {
return E;
- } else if (!p_exact_match && e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) {
+ } else if (!p_exact_match && E->get()->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) {
return E;
}
}
@@ -151,20 +147,20 @@ bool InputMap::has_action(const StringName &p_action) const {
}
float InputMap::action_get_deadzone(const StringName &p_action) {
- ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, _suggest_actions(p_action));
+ ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, suggest_actions(p_action));
return input_map[p_action].deadzone;
}
void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) {
- ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action));
input_map[p_action].deadzone = p_deadzone;
}
void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
ERR_FAIL_COND_MSG(p_event.is_null(), "It's not a reference to a valid InputEvent object.");
- ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action));
if (_find_event(input_map[p_action], p_event, true)) {
return; // Already added.
}
@@ -173,12 +169,12 @@ void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent
}
bool InputMap::action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
- ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, _suggest_actions(p_action));
+ ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, suggest_actions(p_action));
return (_find_event(input_map[p_action], p_event, true) != nullptr);
}
void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
- ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action));
List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event, true);
if (E) {
@@ -190,7 +186,7 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEve
}
void InputMap::action_erase_events(const StringName &p_action) {
- ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action));
input_map[p_action].inputs.clear();
}
@@ -222,15 +218,16 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName
bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const {
OrderedHashMap<StringName, Action>::Element E = input_map.find(p_action);
- ERR_FAIL_COND_V_MSG(!E, false, _suggest_actions(p_action));
+ ERR_FAIL_COND_V_MSG(!E, false, suggest_actions(p_action));
Ref<InputEventAction> input_event_action = p_event;
if (input_event_action.is_valid()) {
+ bool pressed = input_event_action->is_pressed();
if (p_pressed != nullptr) {
- *p_pressed = input_event_action->is_pressed();
+ *p_pressed = pressed;
}
if (p_strength != nullptr) {
- *p_strength = (p_pressed != nullptr && *p_pressed) ? input_event_action->get_strength() : 0.0f;
+ *p_strength = pressed ? input_event_action->get_strength() : 0.0f;
}
return input_event_action->get_action() == p_action;
}
@@ -265,9 +262,7 @@ void InputMap::load_from_project_settings() {
List<PropertyInfo> pinfo;
ProjectSettings::get_singleton()->get_property_list(&pinfo);
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- const PropertyInfo &pi = E->get();
-
+ for (const PropertyInfo &pi : pinfo) {
if (!pi.name.begins_with("input/")) {
continue;
}
@@ -322,39 +317,40 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
{ "ui_text_dedent", TTRC("Dedent") },
{ "ui_text_backspace", TTRC("Backspace") },
{ "ui_text_backspace_word", TTRC("Backspace Word") },
- { "ui_text_backspace_word.OSX", TTRC("Backspace Word") },
+ { "ui_text_backspace_word.macos", TTRC("Backspace Word") },
{ "ui_text_backspace_all_to_left", TTRC("Backspace all to Left") },
- { "ui_text_backspace_all_to_left.OSX", TTRC("Backspace all to Left") },
+ { "ui_text_backspace_all_to_left.macos", TTRC("Backspace all to Left") },
{ "ui_text_delete", TTRC("Delete") },
{ "ui_text_delete_word", TTRC("Delete Word") },
- { "ui_text_delete_word.OSX", TTRC("Delete Word") },
+ { "ui_text_delete_word.macos", TTRC("Delete Word") },
{ "ui_text_delete_all_to_right", TTRC("Delete all to Right") },
- { "ui_text_delete_all_to_right.OSX", TTRC("Delete all to Right") },
+ { "ui_text_delete_all_to_right.macos", TTRC("Delete all to Right") },
{ "ui_text_caret_left", TTRC("Caret Left") },
{ "ui_text_caret_word_left", TTRC("Caret Word Left") },
- { "ui_text_caret_word_left.OSX", TTRC("Caret Word Left") },
+ { "ui_text_caret_word_left.macos", TTRC("Caret Word Left") },
{ "ui_text_caret_right", TTRC("Caret Right") },
{ "ui_text_caret_word_right", TTRC("Caret Word Right") },
- { "ui_text_caret_word_right.OSX", TTRC("Caret Word Right") },
+ { "ui_text_caret_word_right.macos", TTRC("Caret Word Right") },
{ "ui_text_caret_up", TTRC("Caret Up") },
{ "ui_text_caret_down", TTRC("Caret Down") },
{ "ui_text_caret_line_start", TTRC("Caret Line Start") },
- { "ui_text_caret_line_start.OSX", TTRC("Caret Line Start") },
+ { "ui_text_caret_line_start.macos", TTRC("Caret Line Start") },
{ "ui_text_caret_line_end", TTRC("Caret Line End") },
- { "ui_text_caret_line_end.OSX", TTRC("Caret Line End") },
+ { "ui_text_caret_line_end.macos", TTRC("Caret Line End") },
{ "ui_text_caret_page_up", TTRC("Caret Page Up") },
{ "ui_text_caret_page_down", TTRC("Caret Page Down") },
{ "ui_text_caret_document_start", TTRC("Caret Document Start") },
- { "ui_text_caret_document_start.OSX", TTRC("Caret Document Start") },
+ { "ui_text_caret_document_start.macos", TTRC("Caret Document Start") },
{ "ui_text_caret_document_end", TTRC("Caret Document End") },
- { "ui_text_caret_document_end.OSX", TTRC("Caret Document End") },
+ { "ui_text_caret_document_end.macos", TTRC("Caret Document End") },
{ "ui_text_scroll_up", TTRC("Scroll Up") },
- { "ui_text_scroll_up.OSX", TTRC("Scroll Up") },
+ { "ui_text_scroll_up.macos", TTRC("Scroll Up") },
{ "ui_text_scroll_down", TTRC("Scroll Down") },
- { "ui_text_scroll_down.OSX", TTRC("Scroll Down") },
+ { "ui_text_scroll_down.macos", TTRC("Scroll Down") },
{ "ui_text_select_all", TTRC("Select All") },
- { "ui_text_select_word_under_caret", TTRC("Select Word Under Caret") },
+ { "ui_text_select_word_under_caret", TTRC("Select Word Under Caret") },
{ "ui_text_toggle_insert_mode", TTRC("Toggle Insert Mode") },
+ { "ui_text_submit", TTRC("Text Submitted") },
{ "ui_graph_duplicate", TTRC("Duplicate Nodes") },
{ "ui_graph_delete", TTRC("Delete Nodes") },
{ "ui_filedialog_up_one_level", TTRC("Go Up One Level") },
@@ -511,6 +507,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
// Text Backspace and Delete
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE));
+ inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_SHIFT));
default_builtin_cache.insert("ui_text_backspace", inputs);
inputs = List<Ref<InputEvent>>();
@@ -519,14 +516,14 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_ALT));
- default_builtin_cache.insert("ui_text_backspace_word.OSX", inputs);
+ default_builtin_cache.insert("ui_text_backspace_word.macos", inputs);
inputs = List<Ref<InputEvent>>();
default_builtin_cache.insert("ui_text_backspace_all_to_left", inputs);
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_CMD));
- default_builtin_cache.insert("ui_text_backspace_all_to_left.OSX", inputs);
+ default_builtin_cache.insert("ui_text_backspace_all_to_left.macos", inputs);
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_DELETE));
@@ -538,14 +535,14 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_DELETE | KEY_MASK_ALT));
- default_builtin_cache.insert("ui_text_delete_word.OSX", inputs);
+ default_builtin_cache.insert("ui_text_delete_word.macos", inputs);
inputs = List<Ref<InputEvent>>();
default_builtin_cache.insert("ui_text_delete_all_to_right", inputs);
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_DELETE | KEY_MASK_CMD));
- default_builtin_cache.insert("ui_text_delete_all_to_right.OSX", inputs);
+ default_builtin_cache.insert("ui_text_delete_all_to_right.macos", inputs);
// Text Caret Movement Left/Right
@@ -559,7 +556,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_LEFT | KEY_MASK_ALT));
- default_builtin_cache.insert("ui_text_caret_word_left.OSX", inputs);
+ default_builtin_cache.insert("ui_text_caret_word_left.macos", inputs);
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_RIGHT));
@@ -571,7 +568,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_RIGHT | KEY_MASK_ALT));
- default_builtin_cache.insert("ui_text_caret_word_right.OSX", inputs);
+ default_builtin_cache.insert("ui_text_caret_word_right.macos", inputs);
// Text Caret Movement Up/Down
@@ -592,7 +589,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_A | KEY_MASK_CTRL));
inputs.push_back(InputEventKey::create_reference(KEY_LEFT | KEY_MASK_CMD));
- default_builtin_cache.insert("ui_text_caret_line_start.OSX", inputs);
+ default_builtin_cache.insert("ui_text_caret_line_start.macos", inputs);
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_END));
@@ -601,7 +598,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_E | KEY_MASK_CTRL));
inputs.push_back(InputEventKey::create_reference(KEY_RIGHT | KEY_MASK_CMD));
- default_builtin_cache.insert("ui_text_caret_line_end.OSX", inputs);
+ default_builtin_cache.insert("ui_text_caret_line_end.macos", inputs);
// Text Caret Movement Page Up/Down
@@ -621,7 +618,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_UP | KEY_MASK_CMD));
- default_builtin_cache.insert("ui_text_caret_document_start.OSX", inputs);
+ default_builtin_cache.insert("ui_text_caret_document_start.macos", inputs);
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_END | KEY_MASK_CMD));
@@ -629,7 +626,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_DOWN | KEY_MASK_CMD));
- default_builtin_cache.insert("ui_text_caret_document_end.OSX", inputs);
+ default_builtin_cache.insert("ui_text_caret_document_end.macos", inputs);
// Text Scrolling
@@ -639,7 +636,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_UP | KEY_MASK_CMD | KEY_MASK_ALT));
- default_builtin_cache.insert("ui_text_scroll_up.OSX", inputs);
+ default_builtin_cache.insert("ui_text_scroll_up.macos", inputs);
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_DOWN | KEY_MASK_CMD));
@@ -647,7 +644,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_DOWN | KEY_MASK_CMD | KEY_MASK_ALT));
- default_builtin_cache.insert("ui_text_scroll_down.OSX", inputs);
+ default_builtin_cache.insert("ui_text_scroll_down.macos", inputs);
// Text Misc
@@ -667,6 +664,11 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs.push_back(InputEventKey::create_reference(KEY_MENU));
default_builtin_cache.insert("ui_menu", inputs);
+ inputs = List<Ref<InputEvent>>();
+ inputs.push_back(InputEventKey::create_reference(KEY_ENTER));
+ inputs.push_back(InputEventKey::create_reference(KEY_KP_ENTER));
+ default_builtin_cache.insert("ui_text_submit", inputs);
+
// ///// UI Graph Shortcuts /////
inputs = List<Ref<InputEvent>>();
@@ -700,12 +702,12 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
void InputMap::load_default() {
OrderedHashMap<String, List<Ref<InputEvent>>> builtins = get_builtins();
- // List of Builtins which have an override for OSX.
- Vector<String> osx_builtins;
+ // List of Builtins which have an override for macOS.
+ Vector<String> macos_builtins;
for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) {
- if (String(E.key()).ends_with(".OSX")) {
- // Strip .OSX from name: some_input_name.OSX -> some_input_name
- osx_builtins.push_back(String(E.key()).split(".")[0]);
+ if (String(E.key()).ends_with(".macos")) {
+ // Strip .macos from name: some_input_name.macos -> some_input_name
+ macos_builtins.push_back(String(E.key()).split(".")[0]);
}
}
@@ -715,13 +717,13 @@ void InputMap::load_default() {
String override_for = fullname.split(".").size() > 1 ? fullname.split(".")[1] : "";
#ifdef APPLE_STYLE_KEYS
- if (osx_builtins.has(name) && override_for != "OSX") {
- // Name has osx builtin but this particular one is for non-osx systems - so skip.
+ if (macos_builtins.has(name) && override_for != "macos") {
+ // Name has `macos` builtin but this particular one is for non-macOS systems - so skip.
continue;
}
#else
- if (override_for == "OSX") {
- // Override for OSX - not needed on non-osx platforms.
+ if (override_for == "macos") {
+ // Override for macOS - not needed on non-macOS platforms.
continue;
}
#endif
@@ -744,3 +746,7 @@ InputMap::InputMap() {
ERR_FAIL_COND_MSG(singleton, "Singleton in InputMap already exist.");
singleton = this;
}
+
+InputMap::~InputMap() {
+ singleton = nullptr;
+}
diff --git a/core/input/input_map.h b/core/input/input_map.h
index 0e0567464a..c724fdb142 100644
--- a/core/input/input_map.h
+++ b/core/input/input_map.h
@@ -61,7 +61,6 @@ private:
Array _action_get_events(const StringName &p_action);
Array _get_actions();
- String _suggest_actions(const StringName &p_action) const;
protected:
static void _bind_methods();
@@ -89,11 +88,14 @@ public:
void load_from_project_settings();
void load_default();
+ String suggest_actions(const StringName &p_action) const;
+
String get_builtin_display_name(const String &p_name) const;
// Use an Ordered Map so insertion order is preserved. We want the elements to be 'grouped' somewhat.
const OrderedHashMap<String, List<Ref<InputEvent>>> &get_builtins();
InputMap();
+ ~InputMap();
};
#endif // INPUT_MAP_H
diff --git a/core/input/shortcut.cpp b/core/input/shortcut.cpp
new file mode 100644
index 0000000000..d0cb08724e
--- /dev/null
+++ b/core/input/shortcut.cpp
@@ -0,0 +1,76 @@
+/*************************************************************************/
+/* shortcut.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "shortcut.h"
+#include "core/os/keyboard.h"
+
+void Shortcut::set_event(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND_MSG(Object::cast_to<InputEventShortcut>(*p_event), "Cannot set a shortcut event to an instance of InputEventShortcut.");
+ event = p_event;
+ emit_changed();
+}
+
+Ref<InputEvent> Shortcut::get_event() const {
+ return event;
+}
+
+bool Shortcut::matches_event(const Ref<InputEvent> &p_event) const {
+ Ref<InputEventShortcut> ies = p_event;
+ if (ies.is_valid()) {
+ if (ies->get_shortcut().ptr() == this) {
+ return true;
+ }
+ }
+ return event.is_valid() && event->is_match(p_event, true);
+}
+
+String Shortcut::get_as_text() const {
+ if (event.is_valid()) {
+ return event->as_text();
+ } else {
+ return "None";
+ }
+}
+
+bool Shortcut::has_valid_event() const {
+ return event.is_valid();
+}
+
+void Shortcut::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_event", "event"), &Shortcut::set_event);
+ ClassDB::bind_method(D_METHOD("get_event"), &Shortcut::get_event);
+
+ ClassDB::bind_method(D_METHOD("has_valid_event"), &Shortcut::has_valid_event);
+
+ ClassDB::bind_method(D_METHOD("matches_event", "event"), &Shortcut::matches_event);
+ ClassDB::bind_method(D_METHOD("get_as_text"), &Shortcut::get_as_text);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), "set_event", "get_event");
+}
diff --git a/core/input/shortcut.h b/core/input/shortcut.h
new file mode 100644
index 0000000000..249dd1971f
--- /dev/null
+++ b/core/input/shortcut.h
@@ -0,0 +1,54 @@
+/*************************************************************************/
+/* shortcut.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SHORTCUT_H
+#define SHORTCUT_H
+
+#include "core/input/input_event.h"
+#include "core/io/resource.h"
+
+class Shortcut : public Resource {
+ GDCLASS(Shortcut, Resource);
+
+ Ref<InputEvent> event;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_event(const Ref<InputEvent> &p_shortcut);
+ Ref<InputEvent> get_event() const;
+ bool matches_event(const Ref<InputEvent> &p_event) const;
+ bool has_valid_event() const;
+
+ String get_as_text() const;
+};
+
+#endif // SHORTCUT_H
diff --git a/core/io/compression.cpp b/core/io/compression.cpp
index 6de626db99..790b6febc0 100644
--- a/core/io/compression.cpp
+++ b/core/io/compression.cpp
@@ -134,8 +134,9 @@ int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p
if (p_dst_max_size < 16) {
uint8_t dst[16];
- ret_size = fastlz_decompress(p_src, p_src_size, dst, 16);
+ fastlz_decompress(p_src, p_src_size, dst, 16);
memcpy(p_dst, dst, p_dst_max_size);
+ ret_size = p_dst_max_size;
} else {
ret_size = fastlz_decompress(p_src, p_src_size, p_dst, p_dst_max_size);
}
@@ -238,7 +239,10 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s
case Z_DATA_ERROR:
case Z_MEM_ERROR:
case Z_STREAM_ERROR:
- WARN_PRINT(strm.msg);
+ case Z_BUF_ERROR:
+ if (strm.msg) {
+ WARN_PRINT(strm.msg);
+ }
(void)inflateEnd(&strm);
p_dst_vect->resize(0);
return ret;
diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp
index 10f68f3cef..49fa73dab2 100644
--- a/core/io/config_file.cpp
+++ b/core/io/config_file.cpp
@@ -40,8 +40,8 @@ PackedStringArray ConfigFile::_get_sections() const {
PackedStringArray arr;
arr.resize(s.size());
int idx = 0;
- for (const List<String>::Element *E = s.front(); E; E = E->next()) {
- arr.set(idx++, E->get());
+ for (const String &E : s) {
+ arr.set(idx++, E);
}
return arr;
@@ -53,8 +53,8 @@ PackedStringArray ConfigFile::_get_section_keys(const String &p_section) const {
PackedStringArray arr;
arr.resize(s.size());
int idx = 0;
- for (const List<String>::Element *E = s.front(); E; E = E->next()) {
- arr.set(idx++, E->get());
+ for (const String &E : s) {
+ arr.set(idx++, E);
}
return arr;
@@ -188,7 +188,7 @@ Error ConfigFile::_internal_save(FileAccess *file) {
for (OrderedHashMap<String, Variant>::Element F = E.get().front(); F; F = F.next()) {
String vstr;
VariantWriter::write_to_string(F.get(), vstr);
- file->store_string(F.key() + "=" + vstr + "\n");
+ file->store_string(F.key().property_name_encode() + "=" + vstr + "\n");
}
}
@@ -315,6 +315,8 @@ void ConfigFile::_bind_methods() {
ClassDB::bind_method(D_METHOD("parse", "data"), &ConfigFile::parse);
ClassDB::bind_method(D_METHOD("save", "path"), &ConfigFile::save);
+ BIND_METHOD_ERR_RETURN_DOC("load", ERR_FILE_CANT_OPEN);
+
ClassDB::bind_method(D_METHOD("load_encrypted", "path", "key"), &ConfigFile::load_encrypted);
ClassDB::bind_method(D_METHOD("load_encrypted_pass", "path", "password"), &ConfigFile::load_encrypted_pass);
diff --git a/core/io/config_file.h b/core/io/config_file.h
index 1b28257c60..dbba43ace5 100644
--- a/core/io/config_file.h
+++ b/core/io/config_file.h
@@ -31,13 +31,13 @@
#ifndef CONFIG_FILE_H
#define CONFIG_FILE_H
-#include "core/object/reference.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
+#include "core/object/ref_counted.h"
#include "core/templates/ordered_hash_map.h"
#include "core/variant/variant_parser.h"
-class ConfigFile : public Reference {
- GDCLASS(ConfigFile, Reference);
+class ConfigFile : public RefCounted {
+ GDCLASS(ConfigFile, RefCounted);
OrderedHashMap<String, OrderedHashMap<String, Variant>> values;
diff --git a/core/os/dir_access.cpp b/core/io/dir_access.cpp
index 39ae475c12..3bff0a3fd5 100644
--- a/core/os/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -31,7 +31,7 @@
#include "dir_access.h"
#include "core/config/project_settings.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include "core/os/memory.h"
#include "core/os/os.h"
@@ -93,8 +93,8 @@ static Error _erase_recursive(DirAccess *da) {
da->list_dir_end();
- for (List<String>::Element *E = dirs.front(); E; E = E->next()) {
- Error err = da->change_dir(E->get());
+ for (const String &E : dirs) {
+ Error err = da->change_dir(E);
if (err == OK) {
err = _erase_recursive(da);
if (err) {
@@ -105,7 +105,7 @@ static Error _erase_recursive(DirAccess *da) {
if (err) {
return err;
}
- err = da->remove(da->get_current_dir().plus_file(E->get()));
+ err = da->remove(da->get_current_dir().plus_file(E));
if (err) {
return err;
}
@@ -114,8 +114,8 @@ static Error _erase_recursive(DirAccess *da) {
}
}
- for (List<String>::Element *E = files.front(); E; E = E->next()) {
- Error err = da->remove(da->get_current_dir().plus_file(E->get()));
+ for (const String &E : files) {
+ Error err = da->remove(da->get_current_dir().plus_file(E));
if (err) {
return err;
}
@@ -135,7 +135,7 @@ Error DirAccess::make_dir_recursive(String p_dir) {
String full_dir;
- if (p_dir.is_rel_path()) {
+ if (p_dir.is_relative_path()) {
//append current
full_dir = get_current_dir().plus_file(p_dir);
@@ -345,7 +345,7 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag
dirs.push_back(n);
} else {
const String &rel_path = n;
- if (!n.is_rel_path()) {
+ if (!n.is_relative_path()) {
list_dir_end();
return ERR_BUG;
}
@@ -362,16 +362,15 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag
list_dir_end();
- for (List<String>::Element *E = dirs.front(); E; E = E->next()) {
- String rel_path = E->get();
+ for (const String &rel_path : dirs) {
String target_dir = p_to + rel_path;
if (!p_target_da->dir_exists(target_dir)) {
Error err = p_target_da->make_dir(target_dir);
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + target_dir + "'.");
}
- Error err = change_dir(E->get());
- ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + E->get() + "'.");
+ Error err = change_dir(rel_path);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + rel_path + "'.");
err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags, p_copy_links);
if (err) {
diff --git a/core/os/dir_access.h b/core/io/dir_access.h
index 16154a4850..16154a4850 100644
--- a/core/os/dir_access.h
+++ b/core/io/dir_access.h
diff --git a/core/io/dtls_server.cpp b/core/io/dtls_server.cpp
index 288b2efe0e..655fb18535 100644
--- a/core/io/dtls_server.cpp
+++ b/core/io/dtls_server.cpp
@@ -31,7 +31,7 @@
#include "dtls_server.h"
#include "core/config/project_settings.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
DTLSServer *(*DTLSServer::_create)() = nullptr;
bool DTLSServer::available = false;
diff --git a/core/io/dtls_server.h b/core/io/dtls_server.h
index 92b6caf508..02a32533e1 100644
--- a/core/io/dtls_server.h
+++ b/core/io/dtls_server.h
@@ -34,8 +34,8 @@
#include "core/io/net_socket.h"
#include "core/io/packet_peer_dtls.h"
-class DTLSServer : public Reference {
- GDCLASS(DTLSServer, Reference);
+class DTLSServer : public RefCounted {
+ GDCLASS(DTLSServer, RefCounted);
protected:
static DTLSServer *(*_create)();
diff --git a/core/os/file_access.cpp b/core/io/file_access.cpp
index d1b940190a..e6e79dff8a 100644
--- a/core/os/file_access.cpp
+++ b/core/io/file_access.cpp
@@ -164,7 +164,7 @@ uint16_t FileAccess::get_16() const {
a = get_8();
b = get_8();
- if (endian_swap) {
+ if (big_endian) {
SWAP(a, b);
}
@@ -182,7 +182,7 @@ uint32_t FileAccess::get_32() const {
a = get_16();
b = get_16();
- if (endian_swap) {
+ if (big_endian) {
SWAP(a, b);
}
@@ -200,7 +200,7 @@ uint64_t FileAccess::get_64() const {
a = get_32();
b = get_32();
- if (endian_swap) {
+ if (big_endian) {
SWAP(a, b);
}
@@ -316,52 +316,53 @@ String FileAccess::get_line() const {
}
Vector<String> FileAccess::get_csv_line(const String &p_delim) const {
- ERR_FAIL_COND_V(p_delim.length() != 1, Vector<String>());
+ ERR_FAIL_COND_V_MSG(p_delim.length() != 1, Vector<String>(), "Only single character delimiters are supported to parse CSV lines.");
+ ERR_FAIL_COND_V_MSG(p_delim[0] == '"', Vector<String>(), "The double quotation mark character (\") is not supported as a delimiter for CSV lines.");
- String l;
+ String line;
+
+ // CSV can support entries with line breaks as long as they are enclosed
+ // in double quotes. So our "line" might be more than a single line in the
+ // text file.
int qc = 0;
do {
if (eof_reached()) {
break;
}
-
- l += get_line() + "\n";
+ line += get_line() + "\n";
qc = 0;
- for (int i = 0; i < l.length(); i++) {
- if (l[i] == '"') {
+ for (int i = 0; i < line.length(); i++) {
+ if (line[i] == '"') {
qc++;
}
}
-
} while (qc % 2);
- l = l.substr(0, l.length() - 1);
+ // Remove the extraneous newline we've added above.
+ line = line.substr(0, line.length() - 1);
Vector<String> strings;
bool in_quote = false;
String current;
- for (int i = 0; i < l.length(); i++) {
- char32_t c = l[i];
- char32_t s[2] = { 0, 0 };
-
+ for (int i = 0; i < line.length(); i++) {
+ char32_t c = line[i];
+ // A delimiter ends the current entry, unless it's in a quoted string.
if (!in_quote && c == p_delim[0]) {
strings.push_back(current);
current = String();
} else if (c == '"') {
- if (l[i + 1] == '"' && in_quote) {
- s[0] = '"';
- current += s;
+ // Doubled quotes are escapes for intentional quotes in the string.
+ if (line[i + 1] == '"' && in_quote) {
+ current += '"';
i++;
} else {
in_quote = !in_quote;
}
} else {
- s[0] = c;
- current += s;
+ current += c;
}
}
-
strings.push_back(current);
return strings;
@@ -401,7 +402,7 @@ void FileAccess::store_16(uint16_t p_dest) {
a = p_dest & 0xFF;
b = p_dest >> 8;
- if (endian_swap) {
+ if (big_endian) {
SWAP(a, b);
}
@@ -415,7 +416,7 @@ void FileAccess::store_32(uint32_t p_dest) {
a = p_dest & 0xFFFF;
b = p_dest >> 16;
- if (endian_swap) {
+ if (big_endian) {
SWAP(a, b);
}
@@ -429,7 +430,7 @@ void FileAccess::store_64(uint64_t p_dest) {
a = p_dest & 0xFFFFFFFF;
b = p_dest >> 32;
- if (endian_swap) {
+ if (big_endian) {
SWAP(a, b);
}
@@ -551,6 +552,7 @@ void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_
}
void FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) {
+ ERR_FAIL_COND(!p_src && p_length > 0);
for (uint64_t i = 0; i < p_length; i++) {
store_8(p_src[i]);
}
diff --git a/core/os/file_access.h b/core/io/file_access.h
index 1b9fb2f422..5804aa2c47 100644
--- a/core/os/file_access.h
+++ b/core/io/file_access.h
@@ -52,7 +52,7 @@ public:
typedef void (*FileCloseFailNotify)(const String &);
typedef FileAccess *(*CreateFunc)();
- bool endian_swap = false;
+ bool big_endian = false;
bool real_is_double = false;
virtual uint32_t _get_unix_permissions(const String &p_file) = 0;
@@ -115,13 +115,13 @@ public:
virtual Vector<String> get_csv_line(const String &p_delim = ",") const;
virtual String get_as_utf8_string() const;
- /**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac)
+ /**
+ * Use this for files WRITTEN in _big_ endian machines (ie, amiga/mac)
* It's not about the current CPU type but file formats.
- * this flags get reset to false (little endian) on each open
+ * This flag gets reset to `false` (little endian) on each open.
*/
-
- virtual void set_endian_swap(bool p_swap) { endian_swap = p_swap; }
- inline bool get_endian_swap() const { return endian_swap; }
+ virtual void set_big_endian(bool p_big_endian) { big_endian = p_big_endian; }
+ inline bool is_big_endian() const { return big_endian; }
virtual Error get_error() const = 0; ///< get last error
diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h
index 19e4f241dd..3389e020e3 100644
--- a/core/io/file_access_compressed.h
+++ b/core/io/file_access_compressed.h
@@ -32,7 +32,7 @@
#define FILE_ACCESS_COMPRESSED_H
#include "core/io/compression.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
class FileAccessCompressed : public FileAccess {
Compression::Mode cmode = Compression::MODE_ZSTD;
diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp
index b9514c8c8b..9e316291e8 100644
--- a/core/io/file_access_encrypted.cpp
+++ b/core/io/file_access_encrypted.cpp
@@ -257,6 +257,7 @@ Error FileAccessEncrypted::get_error() const {
void FileAccessEncrypted::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode.");
+ ERR_FAIL_COND(!p_src && p_length > 0);
if (pos < get_length()) {
for (uint64_t i = 0; i < p_length; i++) {
diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h
index 00f14099f9..decffae696 100644
--- a/core/io/file_access_encrypted.h
+++ b/core/io/file_access_encrypted.h
@@ -31,7 +31,7 @@
#ifndef FILE_ACCESS_ENCRYPTED_H
#define FILE_ACCESS_ENCRYPTED_H
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#define ENCRYPTED_HEADER_MAGIC 0x43454447
diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp
index 0114ab1765..627fd2bf9c 100644
--- a/core/io/file_access_memory.cpp
+++ b/core/io/file_access_memory.cpp
@@ -31,7 +31,7 @@
#include "file_access_memory.h"
#include "core/config/project_settings.h"
-#include "core/os/dir_access.h"
+#include "core/io/dir_access.h"
#include "core/templates/map.h"
static Map<String, Vector<uint8_t>> *files = nullptr;
@@ -168,6 +168,7 @@ void FileAccessMemory::store_8(uint8_t p_byte) {
}
void FileAccessMemory::store_buffer(const uint8_t *p_src, uint64_t p_length) {
+ ERR_FAIL_COND(!p_src && p_length > 0);
uint64_t left = length - pos;
uint64_t write = MIN(p_length, left);
if (write < p_length) {
diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h
index 4157531d01..14135bd68c 100644
--- a/core/io/file_access_memory.h
+++ b/core/io/file_access_memory.h
@@ -31,7 +31,7 @@
#ifndef FILE_ACCESS_MEMORY_H
#define FILE_ACCESS_MEMORY_H
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
class FileAccessMemory : public FileAccess {
uint8_t *data = nullptr;
diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp
index 63a8f9c5b6..9ee3876c2f 100644
--- a/core/io/file_access_network.cpp
+++ b/core/io/file_access_network.cpp
@@ -210,7 +210,7 @@ FileAccessNetworkClient *FileAccessNetworkClient::singleton = nullptr;
FileAccessNetworkClient::FileAccessNetworkClient() {
singleton = this;
- client.instance();
+ client.instantiate();
}
FileAccessNetworkClient::~FileAccessNetworkClient() {
diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h
index 94b66c2480..1d9d761fbb 100644
--- a/core/io/file_access_network.h
+++ b/core/io/file_access_network.h
@@ -31,8 +31,8 @@
#ifndef FILE_ACCESS_NETWORK_H
#define FILE_ACCESS_NETWORK_H
+#include "core/io/file_access.h"
#include "core/io/stream_peer_tcp.h"
-#include "core/os/file_access.h"
#include "core/os/semaphore.h"
#include "core/os/thread.h"
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index e9983ece47..7b43daf9c0 100644
--- a/core/io/file_access_pack.cpp
+++ b/core/io/file_access_pack.cpp
@@ -320,9 +320,9 @@ uint64_t FileAccessPack::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
return to_read;
}
-void FileAccessPack::set_endian_swap(bool p_swap) {
- FileAccess::set_endian_swap(p_swap);
- f->set_endian_swap(p_swap);
+void FileAccessPack::set_big_endian(bool p_big_endian) {
+ FileAccess::set_big_endian(p_big_endian);
+ f->set_big_endian(p_big_endian);
}
Error FileAccessPack::get_error() const {
diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h
index 9747e865c8..2f0ee62723 100644
--- a/core/io/file_access_pack.h
+++ b/core/io/file_access_pack.h
@@ -31,8 +31,8 @@
#ifndef FILE_ACCESS_PACK_H
#define FILE_ACCESS_PACK_H
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/string/print_string.h"
#include "core/templates/list.h"
#include "core/templates/map.h"
@@ -171,7 +171,7 @@ public:
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const;
- virtual void set_endian_swap(bool p_swap);
+ virtual void set_big_endian(bool p_big_endian);
virtual Error get_error() const;
diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp
index b8383fd865..b5c882e9ce 100644
--- a/core/io/file_access_zip.cpp
+++ b/core/io/file_access_zip.cpp
@@ -32,7 +32,7 @@
#include "file_access_zip.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
ZipArchive *ZipArchive::instance = nullptr;
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 0cf870e7e7..5c1352c1b6 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -30,9 +30,6 @@
#include "http_client.h"
-#include "core/io/stream_peer_ssl.h"
-#include "core/version.h"
-
const char *HTTPClient::_methods[METHOD_MAX] = {
"GET",
"HEAD",
@@ -45,698 +42,23 @@ const char *HTTPClient::_methods[METHOD_MAX] = {
"PATCH"
};
-#ifndef JAVASCRIPT_ENABLED
-Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) {
- close();
-
- conn_port = p_port;
- conn_host = p_host;
-
- ssl = p_ssl;
- ssl_verify_host = p_verify_host;
-
- String host_lower = conn_host.to_lower();
- if (host_lower.begins_with("http://")) {
- conn_host = conn_host.substr(7, conn_host.length() - 7);
- } else if (host_lower.begins_with("https://")) {
- ssl = true;
- conn_host = conn_host.substr(8, conn_host.length() - 8);
- }
-
- ERR_FAIL_COND_V(conn_host.length() < HOST_MIN_LEN, ERR_INVALID_PARAMETER);
-
- if (conn_port < 0) {
- if (ssl) {
- conn_port = PORT_HTTPS;
- } else {
- conn_port = PORT_HTTP;
- }
- }
-
- connection = tcp_connection;
-
- if (conn_host.is_valid_ip_address()) {
- // Host contains valid IP
- Error err = tcp_connection->connect_to_host(IPAddress(conn_host), p_port);
- if (err) {
- status = STATUS_CANT_CONNECT;
- return err;
- }
-
- status = STATUS_CONNECTING;
- } else {
- // Host contains hostname and needs to be resolved to IP
- resolving = IP::get_singleton()->resolve_hostname_queue_item(conn_host);
- status = STATUS_RESOLVING;
+HTTPClient *HTTPClient::create() {
+ if (_create) {
+ return _create();
}
-
- return OK;
+ return nullptr;
}
-void HTTPClient::set_connection(const Ref<StreamPeer> &p_connection) {
- ERR_FAIL_COND_MSG(p_connection.is_null(), "Connection is not a reference to a valid StreamPeer object.");
-
- if (ssl) {
- ERR_FAIL_NULL_MSG(Object::cast_to<StreamPeerSSL>(p_connection.ptr()),
- "Connection is not a reference to a valid StreamPeerSSL object.");
- }
-
- if (connection == p_connection) {
- return;
- }
-
- close();
- connection = p_connection;
- status = STATUS_CONNECTED;
+Error HTTPClient::_request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body) {
+ int size = p_body.size();
+ return request(p_method, p_url, p_headers, size > 0 ? p_body.ptr() : nullptr, size);
}
-Ref<StreamPeer> HTTPClient::get_connection() const {
- return connection;
+Error HTTPClient::_request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body) {
+ int size = p_body.length();
+ return request(p_method, p_url, p_headers, size > 0 ? (const uint8_t *)p_body.utf8().get_data() : nullptr, size);
}
-static bool _check_request_url(HTTPClient::Method p_method, const String &p_url) {
- switch (p_method) {
- case HTTPClient::METHOD_CONNECT: {
- // Authority in host:port format, as in RFC7231
- int pos = p_url.find_char(':');
- return 0 < pos && pos < p_url.length() - 1;
- }
- case HTTPClient::METHOD_OPTIONS: {
- if (p_url == "*") {
- return true;
- }
- [[fallthrough]];
- }
- default:
- // Absolute path or absolute URL
- return p_url.begins_with("/") || p_url.begins_with("http://") || p_url.begins_with("https://");
- }
-}
-
-Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body) {
- ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(!_check_request_url(p_method, p_url), ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA);
-
- String request = String(_methods[p_method]) + " " + p_url + " HTTP/1.1\r\n";
- bool add_host = true;
- bool add_clen = p_body.size() > 0;
- bool add_uagent = true;
- bool add_accept = true;
- for (int i = 0; i < p_headers.size(); i++) {
- request += p_headers[i] + "\r\n";
- if (add_host && p_headers[i].findn("Host:") == 0) {
- add_host = false;
- }
- if (add_clen && p_headers[i].findn("Content-Length:") == 0) {
- add_clen = false;
- }
- if (add_uagent && p_headers[i].findn("User-Agent:") == 0) {
- add_uagent = false;
- }
- if (add_accept && p_headers[i].findn("Accept:") == 0) {
- add_accept = false;
- }
- }
- if (add_host) {
- if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) {
- // Don't append the standard ports
- request += "Host: " + conn_host + "\r\n";
- } else {
- request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n";
- }
- }
- if (add_clen) {
- request += "Content-Length: " + itos(p_body.size()) + "\r\n";
- // Should it add utf8 encoding?
- }
- if (add_uagent) {
- request += "User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")\r\n";
- }
- if (add_accept) {
- request += "Accept: */*\r\n";
- }
- request += "\r\n";
- CharString cs = request.utf8();
-
- Vector<uint8_t> data;
- data.resize(cs.length());
- {
- uint8_t *data_write = data.ptrw();
- for (int i = 0; i < cs.length(); i++) {
- data_write[i] = cs[i];
- }
- }
-
- data.append_array(p_body);
-
- const uint8_t *r = data.ptr();
- Error err = connection->put_data(&r[0], data.size());
-
- if (err) {
- close();
- status = STATUS_CONNECTION_ERROR;
- return err;
- }
-
- status = STATUS_REQUESTING;
- head_request = p_method == METHOD_HEAD;
-
- return OK;
-}
-
-Error HTTPClient::request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body) {
- ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(!_check_request_url(p_method, p_url), ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA);
-
- String request = String(_methods[p_method]) + " " + p_url + " HTTP/1.1\r\n";
- bool add_host = true;
- bool add_uagent = true;
- bool add_accept = true;
- bool add_clen = p_body.length() > 0;
- for (int i = 0; i < p_headers.size(); i++) {
- request += p_headers[i] + "\r\n";
- if (add_host && p_headers[i].findn("Host:") == 0) {
- add_host = false;
- }
- if (add_clen && p_headers[i].findn("Content-Length:") == 0) {
- add_clen = false;
- }
- if (add_uagent && p_headers[i].findn("User-Agent:") == 0) {
- add_uagent = false;
- }
- if (add_accept && p_headers[i].findn("Accept:") == 0) {
- add_accept = false;
- }
- }
- if (add_host) {
- if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) {
- // Don't append the standard ports
- request += "Host: " + conn_host + "\r\n";
- } else {
- request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n";
- }
- }
- if (add_clen) {
- request += "Content-Length: " + itos(p_body.utf8().length()) + "\r\n";
- // Should it add utf8 encoding?
- }
- if (add_uagent) {
- request += "User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")\r\n";
- }
- if (add_accept) {
- request += "Accept: */*\r\n";
- }
- request += "\r\n";
- request += p_body;
-
- CharString cs = request.utf8();
- Error err = connection->put_data((const uint8_t *)cs.ptr(), cs.length());
- if (err) {
- close();
- status = STATUS_CONNECTION_ERROR;
- return err;
- }
-
- status = STATUS_REQUESTING;
- head_request = p_method == METHOD_HEAD;
-
- return OK;
-}
-
-bool HTTPClient::has_response() const {
- return response_headers.size() != 0;
-}
-
-bool HTTPClient::is_response_chunked() const {
- return chunked;
-}
-
-int HTTPClient::get_response_code() const {
- return response_num;
-}
-
-Error HTTPClient::get_response_headers(List<String> *r_response) {
- if (!response_headers.size()) {
- return ERR_INVALID_PARAMETER;
- }
-
- for (int i = 0; i < response_headers.size(); i++) {
- r_response->push_back(response_headers[i]);
- }
-
- response_headers.clear();
-
- return OK;
-}
-
-void HTTPClient::close() {
- if (tcp_connection->get_status() != StreamPeerTCP::STATUS_NONE) {
- tcp_connection->disconnect_from_host();
- }
-
- connection.unref();
- status = STATUS_DISCONNECTED;
- head_request = false;
- if (resolving != IP::RESOLVER_INVALID_ID) {
- IP::get_singleton()->erase_resolve_item(resolving);
- resolving = IP::RESOLVER_INVALID_ID;
- }
-
- response_headers.clear();
- response_str.clear();
- body_size = -1;
- body_left = 0;
- chunk_left = 0;
- chunk_trailer_part = false;
- read_until_eof = false;
- response_num = 0;
- handshaking = false;
-}
-
-Error HTTPClient::poll() {
- switch (status) {
- case STATUS_RESOLVING: {
- ERR_FAIL_COND_V(resolving == IP::RESOLVER_INVALID_ID, ERR_BUG);
-
- IP::ResolverStatus rstatus = IP::get_singleton()->get_resolve_item_status(resolving);
- switch (rstatus) {
- case IP::RESOLVER_STATUS_WAITING:
- return OK; // Still resolving
-
- case IP::RESOLVER_STATUS_DONE: {
- IPAddress host = IP::get_singleton()->get_resolve_item_address(resolving);
- Error err = tcp_connection->connect_to_host(host, conn_port);
- IP::get_singleton()->erase_resolve_item(resolving);
- resolving = IP::RESOLVER_INVALID_ID;
- if (err) {
- status = STATUS_CANT_CONNECT;
- return err;
- }
-
- status = STATUS_CONNECTING;
- } break;
- case IP::RESOLVER_STATUS_NONE:
- case IP::RESOLVER_STATUS_ERROR: {
- IP::get_singleton()->erase_resolve_item(resolving);
- resolving = IP::RESOLVER_INVALID_ID;
- close();
- status = STATUS_CANT_RESOLVE;
- return ERR_CANT_RESOLVE;
- } break;
- }
- } break;
- case STATUS_CONNECTING: {
- StreamPeerTCP::Status s = tcp_connection->get_status();
- switch (s) {
- case StreamPeerTCP::STATUS_CONNECTING: {
- return OK;
- } break;
- case StreamPeerTCP::STATUS_CONNECTED: {
- if (ssl) {
- Ref<StreamPeerSSL> ssl;
- if (!handshaking) {
- // Connect the StreamPeerSSL and start handshaking
- ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
- ssl->set_blocking_handshake_enabled(false);
- Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host);
- if (err != OK) {
- close();
- status = STATUS_SSL_HANDSHAKE_ERROR;
- return ERR_CANT_CONNECT;
- }
- connection = ssl;
- handshaking = true;
- } else {
- // We are already handshaking, which means we can use your already active SSL connection
- ssl = static_cast<Ref<StreamPeerSSL>>(connection);
- if (ssl.is_null()) {
- close();
- status = STATUS_SSL_HANDSHAKE_ERROR;
- return ERR_CANT_CONNECT;
- }
-
- ssl->poll(); // Try to finish the handshake
- }
-
- if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) {
- // Handshake has been successful
- handshaking = false;
- status = STATUS_CONNECTED;
- return OK;
- } else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) {
- // Handshake has failed
- close();
- status = STATUS_SSL_HANDSHAKE_ERROR;
- return ERR_CANT_CONNECT;
- }
- // ... we will need to poll more for handshake to finish
- } else {
- status = STATUS_CONNECTED;
- }
- return OK;
- } break;
- case StreamPeerTCP::STATUS_ERROR:
- case StreamPeerTCP::STATUS_NONE: {
- close();
- status = STATUS_CANT_CONNECT;
- return ERR_CANT_CONNECT;
- } break;
- }
- } break;
- case STATUS_BODY:
- case STATUS_CONNECTED: {
- // Check if we are still connected
- if (ssl) {
- Ref<StreamPeerSSL> tmp = connection;
- tmp->poll();
- if (tmp->get_status() != StreamPeerSSL::STATUS_CONNECTED) {
- status = STATUS_CONNECTION_ERROR;
- return ERR_CONNECTION_ERROR;
- }
- } else if (tcp_connection->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
- status = STATUS_CONNECTION_ERROR;
- return ERR_CONNECTION_ERROR;
- }
- // Connection established, requests can now be made
- return OK;
- } break;
- case STATUS_REQUESTING: {
- while (true) {
- uint8_t byte;
- int rec = 0;
- Error err = _get_http_data(&byte, 1, rec);
- if (err != OK) {
- close();
- status = STATUS_CONNECTION_ERROR;
- return ERR_CONNECTION_ERROR;
- }
-
- if (rec == 0) {
- return OK; // Still requesting, keep trying!
- }
-
- response_str.push_back(byte);
- int rs = response_str.size();
- if (
- (rs >= 2 && response_str[rs - 2] == '\n' && response_str[rs - 1] == '\n') ||
- (rs >= 4 && response_str[rs - 4] == '\r' && response_str[rs - 3] == '\n' && response_str[rs - 2] == '\r' && response_str[rs - 1] == '\n')) {
- // End of response, parse.
- response_str.push_back(0);
- String response;
- response.parse_utf8((const char *)response_str.ptr());
- Vector<String> responses = response.split("\n");
- body_size = -1;
- chunked = false;
- body_left = 0;
- chunk_left = 0;
- chunk_trailer_part = false;
- read_until_eof = false;
- response_str.clear();
- response_headers.clear();
- response_num = RESPONSE_OK;
-
- // Per the HTTP 1.1 spec, keep-alive is the default.
- // Not following that specification breaks standard implementations.
- // Broken web servers should be fixed.
- bool keep_alive = true;
-
- for (int i = 0; i < responses.size(); i++) {
- String header = responses[i].strip_edges();
- String s = header.to_lower();
- if (s.length() == 0) {
- continue;
- }
- if (s.begins_with("content-length:")) {
- body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int();
- body_left = body_size;
-
- } else if (s.begins_with("transfer-encoding:")) {
- String encoding = header.substr(header.find(":") + 1, header.length()).strip_edges();
- if (encoding == "chunked") {
- chunked = true;
- }
- } else if (s.begins_with("connection: close")) {
- keep_alive = false;
- }
-
- if (i == 0 && responses[i].begins_with("HTTP")) {
- String num = responses[i].get_slicec(' ', 1);
- response_num = num.to_int();
- } else {
- response_headers.push_back(header);
- }
- }
-
- // This is a HEAD request, we won't receive anything.
- if (head_request) {
- body_size = 0;
- body_left = 0;
- }
-
- if (body_size != -1 || chunked) {
- status = STATUS_BODY;
- } else if (!keep_alive) {
- read_until_eof = true;
- status = STATUS_BODY;
- } else {
- status = STATUS_CONNECTED;
- }
- return OK;
- }
- }
- } break;
- case STATUS_DISCONNECTED: {
- return ERR_UNCONFIGURED;
- } break;
- case STATUS_CONNECTION_ERROR:
- case STATUS_SSL_HANDSHAKE_ERROR: {
- return ERR_CONNECTION_ERROR;
- } break;
- case STATUS_CANT_CONNECT: {
- return ERR_CANT_CONNECT;
- } break;
- case STATUS_CANT_RESOLVE: {
- return ERR_CANT_RESOLVE;
- } break;
- }
-
- return OK;
-}
-
-int HTTPClient::get_response_body_length() const {
- return body_size;
-}
-
-PackedByteArray HTTPClient::read_response_body_chunk() {
- ERR_FAIL_COND_V(status != STATUS_BODY, PackedByteArray());
-
- PackedByteArray ret;
- Error err = OK;
-
- if (chunked) {
- while (true) {
- if (chunk_trailer_part) {
- // We need to consume the trailer part too or keep-alive will break
- uint8_t b;
- int rec = 0;
- err = _get_http_data(&b, 1, rec);
-
- if (rec == 0) {
- break;
- }
-
- chunk.push_back(b);
- int cs = chunk.size();
- if ((cs >= 2 && chunk[cs - 2] == '\r' && chunk[cs - 1] == '\n')) {
- if (cs == 2) {
- // Finally over
- chunk_trailer_part = false;
- status = STATUS_CONNECTED;
- chunk.clear();
- break;
- } else {
- // We do not process nor return the trailer data
- chunk.clear();
- }
- }
- } else if (chunk_left == 0) {
- // Reading length
- uint8_t b;
- int rec = 0;
- err = _get_http_data(&b, 1, rec);
-
- if (rec == 0) {
- break;
- }
-
- chunk.push_back(b);
-
- if (chunk.size() > 32) {
- ERR_PRINT("HTTP Invalid chunk hex len");
- status = STATUS_CONNECTION_ERROR;
- break;
- }
-
- if (chunk.size() > 2 && chunk[chunk.size() - 2] == '\r' && chunk[chunk.size() - 1] == '\n') {
- int len = 0;
- for (int i = 0; i < chunk.size() - 2; i++) {
- char c = chunk[i];
- int v = 0;
- if (c >= '0' && c <= '9') {
- v = c - '0';
- } else if (c >= 'a' && c <= 'f') {
- v = c - 'a' + 10;
- } else if (c >= 'A' && c <= 'F') {
- v = c - 'A' + 10;
- } else {
- ERR_PRINT("HTTP Chunk len not in hex!!");
- status = STATUS_CONNECTION_ERROR;
- break;
- }
- len <<= 4;
- len |= v;
- if (len > (1 << 24)) {
- ERR_PRINT("HTTP Chunk too big!! >16mb");
- status = STATUS_CONNECTION_ERROR;
- break;
- }
- }
-
- if (len == 0) {
- // End reached!
- chunk_trailer_part = true;
- chunk.clear();
- break;
- }
-
- chunk_left = len + 2;
- chunk.resize(chunk_left);
- }
- } else {
- int rec = 0;
- err = _get_http_data(&chunk.write[chunk.size() - chunk_left], chunk_left, rec);
- if (rec == 0) {
- break;
- }
- chunk_left -= rec;
-
- if (chunk_left == 0) {
- if (chunk[chunk.size() - 2] != '\r' || chunk[chunk.size() - 1] != '\n') {
- ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)");
- status = STATUS_CONNECTION_ERROR;
- break;
- }
-
- ret.resize(chunk.size() - 2);
- uint8_t *w = ret.ptrw();
- memcpy(w, chunk.ptr(), chunk.size() - 2);
- chunk.clear();
- }
-
- break;
- }
- }
-
- } else {
- int to_read = !read_until_eof ? MIN(body_left, read_chunk_size) : read_chunk_size;
- ret.resize(to_read);
- int _offset = 0;
- while (to_read > 0) {
- int rec = 0;
- {
- uint8_t *w = ret.ptrw();
- err = _get_http_data(w + _offset, to_read, rec);
- }
- if (rec <= 0) { // Ended up reading less
- ret.resize(_offset);
- break;
- } else {
- _offset += rec;
- to_read -= rec;
- if (!read_until_eof) {
- body_left -= rec;
- }
- }
- if (err != OK) {
- break;
- }
- }
- }
-
- if (err != OK) {
- close();
-
- if (err == ERR_FILE_EOF) {
- status = STATUS_DISCONNECTED; // Server disconnected
- } else {
- status = STATUS_CONNECTION_ERROR;
- }
- } else if (body_left == 0 && !chunked && !read_until_eof) {
- status = STATUS_CONNECTED;
- }
-
- return ret;
-}
-
-HTTPClient::Status HTTPClient::get_status() const {
- return status;
-}
-
-void HTTPClient::set_blocking_mode(bool p_enable) {
- blocking = p_enable;
-}
-
-bool HTTPClient::is_blocking_mode_enabled() const {
- return blocking;
-}
-
-Error HTTPClient::_get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
- if (blocking) {
- // We can't use StreamPeer.get_data, since when reaching EOF we will get an
- // error without knowing how many bytes we received.
- Error err = ERR_FILE_EOF;
- int read = 0;
- int left = p_bytes;
- r_received = 0;
- while (left > 0) {
- err = connection->get_partial_data(p_buffer + r_received, left, read);
- if (err == OK) {
- r_received += read;
- } else if (err == ERR_FILE_EOF) {
- r_received += read;
- return err;
- } else {
- return err;
- }
- left -= read;
- }
- return err;
- } else {
- return connection->get_partial_data(p_buffer, p_bytes, r_received);
- }
-}
-
-void HTTPClient::set_read_chunk_size(int p_size) {
- ERR_FAIL_COND(p_size < 256 || p_size > (1 << 24));
- read_chunk_size = p_size;
-}
-
-int HTTPClient::get_read_chunk_size() const {
- return read_chunk_size;
-}
-
-HTTPClient::HTTPClient() {
- tcp_connection.instance();
-}
-
-HTTPClient::~HTTPClient() {}
-
-#endif // #ifndef JAVASCRIPT_ENABLED
-
String HTTPClient::query_string_from_dict(const Dictionary &p_dict) {
String query = "";
Array keys = p_dict.keys();
@@ -771,8 +93,7 @@ Dictionary HTTPClient::_get_response_headers_as_dictionary() {
List<String> rh;
get_response_headers(&rh);
Dictionary ret;
- for (const List<String>::Element *E = rh.front(); E; E = E->next()) {
- const String &s = E->get();
+ for (const String &s : rh) {
int sp = s.find(":");
if (sp == -1) {
continue;
@@ -791,8 +112,8 @@ PackedStringArray HTTPClient::_get_response_headers() {
PackedStringArray ret;
ret.resize(rh.size());
int idx = 0;
- for (const List<String>::Element *E = rh.front(); E; E = E->next()) {
- ret.set(idx++, E->get());
+ for (const String &E : rh) {
+ ret.set(idx++, E);
}
return ret;
@@ -802,8 +123,8 @@ void HTTPClient::_bind_methods() {
ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port", "use_ssl", "verify_host"), &HTTPClient::connect_to_host, DEFVAL(-1), DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("set_connection", "connection"), &HTTPClient::set_connection);
ClassDB::bind_method(D_METHOD("get_connection"), &HTTPClient::get_connection);
- ClassDB::bind_method(D_METHOD("request_raw", "method", "url", "headers", "body"), &HTTPClient::request_raw);
- ClassDB::bind_method(D_METHOD("request", "method", "url", "headers", "body"), &HTTPClient::request, DEFVAL(String()));
+ ClassDB::bind_method(D_METHOD("request_raw", "method", "url", "headers", "body"), &HTTPClient::_request_raw);
+ ClassDB::bind_method(D_METHOD("request", "method", "url", "headers", "body"), &HTTPClient::_request, DEFVAL(String()));
ClassDB::bind_method(D_METHOD("close"), &HTTPClient::close);
ClassDB::bind_method(D_METHOD("has_response"), &HTTPClient::has_response);
@@ -825,7 +146,7 @@ void HTTPClient::_bind_methods() {
ClassDB::bind_method(D_METHOD("query_string_from_dict", "fields"), &HTTPClient::query_string_from_dict);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_mode_enabled"), "set_blocking_mode", "is_blocking_mode_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "connection", PROPERTY_HINT_RESOURCE_TYPE, "StreamPeer", 0), "set_connection", "get_connection");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "connection", PROPERTY_HINT_RESOURCE_TYPE, "StreamPeer", PROPERTY_USAGE_NONE), "set_connection", "get_connection");
ADD_PROPERTY(PropertyInfo(Variant::INT, "read_chunk_size", PROPERTY_HINT_RANGE, "256,16777216"), "set_read_chunk_size", "get_read_chunk_size");
BIND_ENUM_CONSTANT(METHOD_GET);
diff --git a/core/io/http_client.h b/core/io/http_client.h
index ec4b86b26f..718c3a905e 100644
--- a/core/io/http_client.h
+++ b/core/io/http_client.h
@@ -34,10 +34,10 @@
#include "core/io/ip.h"
#include "core/io/stream_peer.h"
#include "core/io/stream_peer_tcp.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
-class HTTPClient : public Reference {
- GDCLASS(HTTPClient, Reference);
+class HTTPClient : public RefCounted {
+ GDCLASS(HTTPClient, RefCounted);
public:
enum ResponseCode {
@@ -142,7 +142,7 @@ public:
};
-private:
+protected:
static const char *_methods[METHOD_MAX];
static const int HOST_MIN_LEN = 4;
@@ -152,79 +152,48 @@ private:
};
-#ifndef JAVASCRIPT_ENABLED
- Status status = STATUS_DISCONNECTED;
- IP::ResolverID resolving = IP::RESOLVER_INVALID_ID;
- int conn_port = -1;
- String conn_host;
- bool ssl = false;
- bool ssl_verify_host = false;
- bool blocking = false;
- bool handshaking = false;
- bool head_request = false;
-
- Vector<uint8_t> response_str;
-
- bool chunked = false;
- Vector<uint8_t> chunk;
- int chunk_left = 0;
- bool chunk_trailer_part = false;
- int body_size = -1;
- int body_left = 0;
- bool read_until_eof = false;
-
- Ref<StreamPeerTCP> tcp_connection;
- Ref<StreamPeer> connection;
-
- int response_num = 0;
- Vector<String> response_headers;
- // 64 KiB by default (favors fast download speeds at the cost of memory usage).
- int read_chunk_size = 65536;
-
- Error _get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received);
-
-#else
-#include "platform/javascript/http_client.h.inc"
-#endif
-
PackedStringArray _get_response_headers();
Dictionary _get_response_headers_as_dictionary();
+ Error _request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body);
+ Error _request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body = String());
+
+ static HTTPClient *(*_create)();
static void _bind_methods();
public:
- Error connect_to_host(const String &p_host, int p_port = -1, bool p_ssl = false, bool p_verify_host = true);
+ static HTTPClient *create();
- void set_connection(const Ref<StreamPeer> &p_connection);
- Ref<StreamPeer> get_connection() const;
+ String query_string_from_dict(const Dictionary &p_dict);
- Error request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body);
- Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body = String());
+ virtual Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) = 0;
+ virtual Error connect_to_host(const String &p_host, int p_port = -1, bool p_ssl = false, bool p_verify_host = true) = 0;
- void close();
+ virtual void set_connection(const Ref<StreamPeer> &p_connection) = 0;
+ virtual Ref<StreamPeer> get_connection() const = 0;
- Status get_status() const;
+ virtual void close() = 0;
- bool has_response() const;
- bool is_response_chunked() const;
- int get_response_code() const;
- Error get_response_headers(List<String> *r_response);
- int get_response_body_length() const;
+ virtual Status get_status() const = 0;
- PackedByteArray read_response_body_chunk(); // Can't get body as partial text because of most encodings UTF8, gzip, etc.
+ virtual bool has_response() const = 0;
+ virtual bool is_response_chunked() const = 0;
+ virtual int get_response_code() const = 0;
+ virtual Error get_response_headers(List<String> *r_response) = 0;
+ virtual int get_response_body_length() const = 0;
- void set_blocking_mode(bool p_enable); // Useful mostly if running in a thread
- bool is_blocking_mode_enabled() const;
+ virtual PackedByteArray read_response_body_chunk() = 0; // Can't get body as partial text because of most encodings UTF8, gzip, etc.
- void set_read_chunk_size(int p_size);
- int get_read_chunk_size() const;
+ virtual void set_blocking_mode(bool p_enable) = 0; // Useful mostly if running in a thread
+ virtual bool is_blocking_mode_enabled() const = 0;
- Error poll();
+ virtual void set_read_chunk_size(int p_size) = 0;
+ virtual int get_read_chunk_size() const = 0;
- String query_string_from_dict(const Dictionary &p_dict);
+ virtual Error poll() = 0;
- HTTPClient();
- ~HTTPClient();
+ HTTPClient() {}
+ virtual ~HTTPClient() {}
};
VARIANT_ENUM_CAST(HTTPClient::ResponseCode)
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
new file mode 100644
index 0000000000..b3d35b3603
--- /dev/null
+++ b/core/io/http_client_tcp.cpp
@@ -0,0 +1,687 @@
+/*************************************************************************/
+/* http_client_tcp.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef JAVASCRIPT_ENABLED
+
+#include "http_client_tcp.h"
+
+#include "core/io/stream_peer_ssl.h"
+#include "core/version.h"
+
+HTTPClient *HTTPClientTCP::_create_func() {
+ return memnew(HTTPClientTCP);
+}
+
+Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) {
+ close();
+
+ conn_port = p_port;
+ conn_host = p_host;
+
+ ip_candidates.clear();
+
+ ssl = p_ssl;
+ ssl_verify_host = p_verify_host;
+
+ String host_lower = conn_host.to_lower();
+ if (host_lower.begins_with("http://")) {
+ conn_host = conn_host.substr(7, conn_host.length() - 7);
+ } else if (host_lower.begins_with("https://")) {
+ ssl = true;
+ conn_host = conn_host.substr(8, conn_host.length() - 8);
+ }
+
+ ERR_FAIL_COND_V(conn_host.length() < HOST_MIN_LEN, ERR_INVALID_PARAMETER);
+
+ if (conn_port < 0) {
+ if (ssl) {
+ conn_port = PORT_HTTPS;
+ } else {
+ conn_port = PORT_HTTP;
+ }
+ }
+
+ connection = tcp_connection;
+
+ if (conn_host.is_valid_ip_address()) {
+ // Host contains valid IP
+ Error err = tcp_connection->connect_to_host(IPAddress(conn_host), p_port);
+ if (err) {
+ status = STATUS_CANT_CONNECT;
+ return err;
+ }
+
+ status = STATUS_CONNECTING;
+ } else {
+ // Host contains hostname and needs to be resolved to IP
+ resolving = IP::get_singleton()->resolve_hostname_queue_item(conn_host);
+ status = STATUS_RESOLVING;
+ }
+
+ return OK;
+}
+
+void HTTPClientTCP::set_connection(const Ref<StreamPeer> &p_connection) {
+ ERR_FAIL_COND_MSG(p_connection.is_null(), "Connection is not a reference to a valid StreamPeer object.");
+
+ if (ssl) {
+ ERR_FAIL_NULL_MSG(Object::cast_to<StreamPeerSSL>(p_connection.ptr()),
+ "Connection is not a reference to a valid StreamPeerSSL object.");
+ }
+
+ if (connection == p_connection) {
+ return;
+ }
+
+ close();
+ connection = p_connection;
+ status = STATUS_CONNECTED;
+}
+
+Ref<StreamPeer> HTTPClientTCP::get_connection() const {
+ return connection;
+}
+
+static bool _check_request_url(HTTPClientTCP::Method p_method, const String &p_url) {
+ switch (p_method) {
+ case HTTPClientTCP::METHOD_CONNECT: {
+ // Authority in host:port format, as in RFC7231
+ int pos = p_url.find_char(':');
+ return 0 < pos && pos < p_url.length() - 1;
+ }
+ case HTTPClientTCP::METHOD_OPTIONS: {
+ if (p_url == "*") {
+ return true;
+ }
+ [[fallthrough]];
+ }
+ default:
+ // Absolute path or absolute URL
+ return p_url.begins_with("/") || p_url.begins_with("http://") || p_url.begins_with("https://");
+ }
+}
+
+Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) {
+ ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!_check_request_url(p_method, p_url), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA);
+
+ String request = String(_methods[p_method]) + " " + p_url + " HTTP/1.1\r\n";
+ bool add_host = true;
+ bool add_clen = p_body_size > 0;
+ bool add_uagent = true;
+ bool add_accept = true;
+ for (int i = 0; i < p_headers.size(); i++) {
+ request += p_headers[i] + "\r\n";
+ if (add_host && p_headers[i].findn("Host:") == 0) {
+ add_host = false;
+ }
+ if (add_clen && p_headers[i].findn("Content-Length:") == 0) {
+ add_clen = false;
+ }
+ if (add_uagent && p_headers[i].findn("User-Agent:") == 0) {
+ add_uagent = false;
+ }
+ if (add_accept && p_headers[i].findn("Accept:") == 0) {
+ add_accept = false;
+ }
+ }
+ if (add_host) {
+ if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) {
+ // Don't append the standard ports
+ request += "Host: " + conn_host + "\r\n";
+ } else {
+ request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n";
+ }
+ }
+ if (add_clen) {
+ request += "Content-Length: " + itos(p_body_size) + "\r\n";
+ // Should it add utf8 encoding?
+ }
+ if (add_uagent) {
+ request += "User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")\r\n";
+ }
+ if (add_accept) {
+ request += "Accept: */*\r\n";
+ }
+ request += "\r\n";
+ CharString cs = request.utf8();
+
+ Vector<uint8_t> data;
+ data.resize(cs.length() + p_body_size);
+ memcpy(data.ptrw(), cs.get_data(), cs.length());
+ if (p_body_size > 0) {
+ memcpy(data.ptrw() + cs.length(), p_body, p_body_size);
+ }
+
+ // TODO Implement non-blocking requests.
+ Error err = connection->put_data(data.ptr(), data.size());
+
+ if (err) {
+ close();
+ status = STATUS_CONNECTION_ERROR;
+ return err;
+ }
+
+ status = STATUS_REQUESTING;
+ head_request = p_method == METHOD_HEAD;
+
+ return OK;
+}
+
+bool HTTPClientTCP::has_response() const {
+ return response_headers.size() != 0;
+}
+
+bool HTTPClientTCP::is_response_chunked() const {
+ return chunked;
+}
+
+int HTTPClientTCP::get_response_code() const {
+ return response_num;
+}
+
+Error HTTPClientTCP::get_response_headers(List<String> *r_response) {
+ if (!response_headers.size()) {
+ return ERR_INVALID_PARAMETER;
+ }
+
+ for (int i = 0; i < response_headers.size(); i++) {
+ r_response->push_back(response_headers[i]);
+ }
+
+ response_headers.clear();
+
+ return OK;
+}
+
+void HTTPClientTCP::close() {
+ if (tcp_connection->get_status() != StreamPeerTCP::STATUS_NONE) {
+ tcp_connection->disconnect_from_host();
+ }
+
+ connection.unref();
+ status = STATUS_DISCONNECTED;
+ head_request = false;
+ if (resolving != IP::RESOLVER_INVALID_ID) {
+ IP::get_singleton()->erase_resolve_item(resolving);
+ resolving = IP::RESOLVER_INVALID_ID;
+ }
+
+ ip_candidates.clear();
+ response_headers.clear();
+ response_str.clear();
+ body_size = -1;
+ body_left = 0;
+ chunk_left = 0;
+ chunk_trailer_part = false;
+ read_until_eof = false;
+ response_num = 0;
+ handshaking = false;
+}
+
+Error HTTPClientTCP::poll() {
+ switch (status) {
+ case STATUS_RESOLVING: {
+ ERR_FAIL_COND_V(resolving == IP::RESOLVER_INVALID_ID, ERR_BUG);
+
+ IP::ResolverStatus rstatus = IP::get_singleton()->get_resolve_item_status(resolving);
+ switch (rstatus) {
+ case IP::RESOLVER_STATUS_WAITING:
+ return OK; // Still resolving
+
+ case IP::RESOLVER_STATUS_DONE: {
+ ip_candidates = IP::get_singleton()->get_resolve_item_addresses(resolving);
+ IP::get_singleton()->erase_resolve_item(resolving);
+ resolving = IP::RESOLVER_INVALID_ID;
+
+ Error err = ERR_BUG; // Should be at least one entry.
+ while (ip_candidates.size() > 0) {
+ err = tcp_connection->connect_to_host(ip_candidates.front(), conn_port);
+ if (err == OK) {
+ break;
+ }
+ }
+ if (err) {
+ status = STATUS_CANT_CONNECT;
+ return err;
+ }
+
+ status = STATUS_CONNECTING;
+ } break;
+ case IP::RESOLVER_STATUS_NONE:
+ case IP::RESOLVER_STATUS_ERROR: {
+ IP::get_singleton()->erase_resolve_item(resolving);
+ resolving = IP::RESOLVER_INVALID_ID;
+ close();
+ status = STATUS_CANT_RESOLVE;
+ return ERR_CANT_RESOLVE;
+ } break;
+ }
+ } break;
+ case STATUS_CONNECTING: {
+ StreamPeerTCP::Status s = tcp_connection->get_status();
+ switch (s) {
+ case StreamPeerTCP::STATUS_CONNECTING: {
+ return OK;
+ } break;
+ case StreamPeerTCP::STATUS_CONNECTED: {
+ if (ssl) {
+ Ref<StreamPeerSSL> ssl;
+ if (!handshaking) {
+ // Connect the StreamPeerSSL and start handshaking
+ ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
+ ssl->set_blocking_handshake_enabled(false);
+ Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host);
+ if (err != OK) {
+ close();
+ status = STATUS_SSL_HANDSHAKE_ERROR;
+ return ERR_CANT_CONNECT;
+ }
+ connection = ssl;
+ handshaking = true;
+ } else {
+ // We are already handshaking, which means we can use your already active SSL connection
+ ssl = static_cast<Ref<StreamPeerSSL>>(connection);
+ if (ssl.is_null()) {
+ close();
+ status = STATUS_SSL_HANDSHAKE_ERROR;
+ return ERR_CANT_CONNECT;
+ }
+
+ ssl->poll(); // Try to finish the handshake
+ }
+
+ if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) {
+ // Handshake has been successful
+ handshaking = false;
+ ip_candidates.clear();
+ status = STATUS_CONNECTED;
+ return OK;
+ } else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) {
+ // Handshake has failed
+ close();
+ status = STATUS_SSL_HANDSHAKE_ERROR;
+ return ERR_CANT_CONNECT;
+ }
+ // ... we will need to poll more for handshake to finish
+ } else {
+ ip_candidates.clear();
+ status = STATUS_CONNECTED;
+ }
+ return OK;
+ } break;
+ case StreamPeerTCP::STATUS_ERROR:
+ case StreamPeerTCP::STATUS_NONE: {
+ Error err = ERR_CANT_CONNECT;
+ while (ip_candidates.size() > 0) {
+ tcp_connection->disconnect_from_host();
+ err = tcp_connection->connect_to_host(ip_candidates.pop_front(), conn_port);
+ if (err == OK) {
+ return OK;
+ }
+ }
+ close();
+ status = STATUS_CANT_CONNECT;
+ return err;
+ } break;
+ }
+ } break;
+ case STATUS_BODY:
+ case STATUS_CONNECTED: {
+ // Check if we are still connected
+ if (ssl) {
+ Ref<StreamPeerSSL> tmp = connection;
+ tmp->poll();
+ if (tmp->get_status() != StreamPeerSSL::STATUS_CONNECTED) {
+ status = STATUS_CONNECTION_ERROR;
+ return ERR_CONNECTION_ERROR;
+ }
+ } else if (tcp_connection->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
+ status = STATUS_CONNECTION_ERROR;
+ return ERR_CONNECTION_ERROR;
+ }
+ // Connection established, requests can now be made
+ return OK;
+ } break;
+ case STATUS_REQUESTING: {
+ while (true) {
+ uint8_t byte;
+ int rec = 0;
+ Error err = _get_http_data(&byte, 1, rec);
+ if (err != OK) {
+ close();
+ status = STATUS_CONNECTION_ERROR;
+ return ERR_CONNECTION_ERROR;
+ }
+
+ if (rec == 0) {
+ return OK; // Still requesting, keep trying!
+ }
+
+ response_str.push_back(byte);
+ int rs = response_str.size();
+ if (
+ (rs >= 2 && response_str[rs - 2] == '\n' && response_str[rs - 1] == '\n') ||
+ (rs >= 4 && response_str[rs - 4] == '\r' && response_str[rs - 3] == '\n' && response_str[rs - 2] == '\r' && response_str[rs - 1] == '\n')) {
+ // End of response, parse.
+ response_str.push_back(0);
+ String response;
+ response.parse_utf8((const char *)response_str.ptr());
+ Vector<String> responses = response.split("\n");
+ body_size = -1;
+ chunked = false;
+ body_left = 0;
+ chunk_left = 0;
+ chunk_trailer_part = false;
+ read_until_eof = false;
+ response_str.clear();
+ response_headers.clear();
+ response_num = RESPONSE_OK;
+
+ // Per the HTTP 1.1 spec, keep-alive is the default.
+ // Not following that specification breaks standard implementations.
+ // Broken web servers should be fixed.
+ bool keep_alive = true;
+
+ for (int i = 0; i < responses.size(); i++) {
+ String header = responses[i].strip_edges();
+ String s = header.to_lower();
+ if (s.length() == 0) {
+ continue;
+ }
+ if (s.begins_with("content-length:")) {
+ body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int();
+ body_left = body_size;
+
+ } else if (s.begins_with("transfer-encoding:")) {
+ String encoding = header.substr(header.find(":") + 1, header.length()).strip_edges();
+ if (encoding == "chunked") {
+ chunked = true;
+ }
+ } else if (s.begins_with("connection: close")) {
+ keep_alive = false;
+ }
+
+ if (i == 0 && responses[i].begins_with("HTTP")) {
+ String num = responses[i].get_slicec(' ', 1);
+ response_num = num.to_int();
+ } else {
+ response_headers.push_back(header);
+ }
+ }
+
+ // This is a HEAD request, we won't receive anything.
+ if (head_request) {
+ body_size = 0;
+ body_left = 0;
+ }
+
+ if (body_size != -1 || chunked) {
+ status = STATUS_BODY;
+ } else if (!keep_alive) {
+ read_until_eof = true;
+ status = STATUS_BODY;
+ } else {
+ status = STATUS_CONNECTED;
+ }
+ return OK;
+ }
+ }
+ } break;
+ case STATUS_DISCONNECTED: {
+ return ERR_UNCONFIGURED;
+ } break;
+ case STATUS_CONNECTION_ERROR:
+ case STATUS_SSL_HANDSHAKE_ERROR: {
+ return ERR_CONNECTION_ERROR;
+ } break;
+ case STATUS_CANT_CONNECT: {
+ return ERR_CANT_CONNECT;
+ } break;
+ case STATUS_CANT_RESOLVE: {
+ return ERR_CANT_RESOLVE;
+ } break;
+ }
+
+ return OK;
+}
+
+int HTTPClientTCP::get_response_body_length() const {
+ return body_size;
+}
+
+PackedByteArray HTTPClientTCP::read_response_body_chunk() {
+ ERR_FAIL_COND_V(status != STATUS_BODY, PackedByteArray());
+
+ PackedByteArray ret;
+ Error err = OK;
+
+ if (chunked) {
+ while (true) {
+ if (chunk_trailer_part) {
+ // We need to consume the trailer part too or keep-alive will break
+ uint8_t b;
+ int rec = 0;
+ err = _get_http_data(&b, 1, rec);
+
+ if (rec == 0) {
+ break;
+ }
+
+ chunk.push_back(b);
+ int cs = chunk.size();
+ if ((cs >= 2 && chunk[cs - 2] == '\r' && chunk[cs - 1] == '\n')) {
+ if (cs == 2) {
+ // Finally over
+ chunk_trailer_part = false;
+ status = STATUS_CONNECTED;
+ chunk.clear();
+ break;
+ } else {
+ // We do not process nor return the trailer data
+ chunk.clear();
+ }
+ }
+ } else if (chunk_left == 0) {
+ // Reading length
+ uint8_t b;
+ int rec = 0;
+ err = _get_http_data(&b, 1, rec);
+
+ if (rec == 0) {
+ break;
+ }
+
+ chunk.push_back(b);
+
+ if (chunk.size() > 32) {
+ ERR_PRINT("HTTP Invalid chunk hex len");
+ status = STATUS_CONNECTION_ERROR;
+ break;
+ }
+
+ if (chunk.size() > 2 && chunk[chunk.size() - 2] == '\r' && chunk[chunk.size() - 1] == '\n') {
+ int len = 0;
+ for (int i = 0; i < chunk.size() - 2; i++) {
+ char c = chunk[i];
+ int v = 0;
+ if (c >= '0' && c <= '9') {
+ v = c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ v = c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ v = c - 'A' + 10;
+ } else {
+ ERR_PRINT("HTTP Chunk len not in hex!!");
+ status = STATUS_CONNECTION_ERROR;
+ break;
+ }
+ len <<= 4;
+ len |= v;
+ if (len > (1 << 24)) {
+ ERR_PRINT("HTTP Chunk too big!! >16mb");
+ status = STATUS_CONNECTION_ERROR;
+ break;
+ }
+ }
+
+ if (len == 0) {
+ // End reached!
+ chunk_trailer_part = true;
+ chunk.clear();
+ break;
+ }
+
+ chunk_left = len + 2;
+ chunk.resize(chunk_left);
+ }
+ } else {
+ int rec = 0;
+ err = _get_http_data(&chunk.write[chunk.size() - chunk_left], chunk_left, rec);
+ if (rec == 0) {
+ break;
+ }
+ chunk_left -= rec;
+
+ if (chunk_left == 0) {
+ if (chunk[chunk.size() - 2] != '\r' || chunk[chunk.size() - 1] != '\n') {
+ ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)");
+ status = STATUS_CONNECTION_ERROR;
+ break;
+ }
+
+ ret.resize(chunk.size() - 2);
+ uint8_t *w = ret.ptrw();
+ memcpy(w, chunk.ptr(), chunk.size() - 2);
+ chunk.clear();
+ }
+
+ break;
+ }
+ }
+
+ } else {
+ int to_read = !read_until_eof ? MIN(body_left, read_chunk_size) : read_chunk_size;
+ ret.resize(to_read);
+ int _offset = 0;
+ while (to_read > 0) {
+ int rec = 0;
+ {
+ uint8_t *w = ret.ptrw();
+ err = _get_http_data(w + _offset, to_read, rec);
+ }
+ if (rec <= 0) { // Ended up reading less
+ ret.resize(_offset);
+ break;
+ } else {
+ _offset += rec;
+ to_read -= rec;
+ if (!read_until_eof) {
+ body_left -= rec;
+ }
+ }
+ if (err != OK) {
+ ret.resize(_offset);
+ break;
+ }
+ }
+ }
+
+ if (err != OK) {
+ close();
+
+ if (err == ERR_FILE_EOF) {
+ status = STATUS_DISCONNECTED; // Server disconnected
+ } else {
+ status = STATUS_CONNECTION_ERROR;
+ }
+ } else if (body_left == 0 && !chunked && !read_until_eof) {
+ status = STATUS_CONNECTED;
+ }
+
+ return ret;
+}
+
+HTTPClientTCP::Status HTTPClientTCP::get_status() const {
+ return status;
+}
+
+void HTTPClientTCP::set_blocking_mode(bool p_enable) {
+ blocking = p_enable;
+}
+
+bool HTTPClientTCP::is_blocking_mode_enabled() const {
+ return blocking;
+}
+
+Error HTTPClientTCP::_get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
+ if (blocking) {
+ // We can't use StreamPeer.get_data, since when reaching EOF we will get an
+ // error without knowing how many bytes we received.
+ Error err = ERR_FILE_EOF;
+ int read = 0;
+ int left = p_bytes;
+ r_received = 0;
+ while (left > 0) {
+ err = connection->get_partial_data(p_buffer + r_received, left, read);
+ if (err == OK) {
+ r_received += read;
+ } else if (err == ERR_FILE_EOF) {
+ r_received += read;
+ return err;
+ } else {
+ return err;
+ }
+ left -= read;
+ }
+ return err;
+ } else {
+ return connection->get_partial_data(p_buffer, p_bytes, r_received);
+ }
+}
+
+void HTTPClientTCP::set_read_chunk_size(int p_size) {
+ ERR_FAIL_COND(p_size < 256 || p_size > (1 << 24));
+ read_chunk_size = p_size;
+}
+
+int HTTPClientTCP::get_read_chunk_size() const {
+ return read_chunk_size;
+}
+
+HTTPClientTCP::HTTPClientTCP() {
+ tcp_connection.instantiate();
+}
+
+HTTPClient *(*HTTPClient::_create)() = HTTPClientTCP::_create_func;
+
+#endif // #ifndef JAVASCRIPT_ENABLED
diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h
new file mode 100644
index 0000000000..170afb551c
--- /dev/null
+++ b/core/io/http_client_tcp.h
@@ -0,0 +1,93 @@
+/*************************************************************************/
+/* http_client_tcp.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef HTTP_CLIENT_TCP_H
+#define HTTP_CLIENT_TCP_H
+
+#include "http_client.h"
+
+class HTTPClientTCP : public HTTPClient {
+private:
+ Status status = STATUS_DISCONNECTED;
+ IP::ResolverID resolving = IP::RESOLVER_INVALID_ID;
+ Array ip_candidates;
+ int conn_port = -1;
+ String conn_host;
+ bool ssl = false;
+ bool ssl_verify_host = false;
+ bool blocking = false;
+ bool handshaking = false;
+ bool head_request = false;
+
+ Vector<uint8_t> response_str;
+
+ bool chunked = false;
+ Vector<uint8_t> chunk;
+ int chunk_left = 0;
+ bool chunk_trailer_part = false;
+ int body_size = -1;
+ int body_left = 0;
+ bool read_until_eof = false;
+
+ Ref<StreamPeerTCP> tcp_connection;
+ Ref<StreamPeer> connection;
+
+ int response_num = 0;
+ Vector<String> response_headers;
+ // 64 KiB by default (favors fast download speeds at the cost of memory usage).
+ int read_chunk_size = 65536;
+
+ Error _get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received);
+
+public:
+ static HTTPClient *_create_func();
+
+ Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) override;
+
+ Error connect_to_host(const String &p_host, int p_port = -1, bool p_ssl = false, bool p_verify_host = true) override;
+ void set_connection(const Ref<StreamPeer> &p_connection) override;
+ Ref<StreamPeer> get_connection() const override;
+ void close() override;
+ Status get_status() const override;
+ bool has_response() const override;
+ bool is_response_chunked() const override;
+ int get_response_code() const override;
+ Error get_response_headers(List<String> *r_response) override;
+ int get_response_body_length() const override;
+ PackedByteArray read_response_body_chunk() override;
+ void set_blocking_mode(bool p_enable) override;
+ bool is_blocking_mode_enabled() const override;
+ void set_read_chunk_size(int p_size) override;
+ int get_read_chunk_size() const override;
+ Error poll() override;
+ HTTPClientTCP();
+};
+
+#endif // HTTP_CLIENT_TCP_H
diff --git a/core/io/image.cpp b/core/io/image.cpp
index c36fa6e45f..3112dd217f 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -1428,16 +1428,23 @@ void Image::flip_x() {
}
}
+/// Get mipmap size and offset.
int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps, int *r_mm_width, int *r_mm_height) {
+ // Data offset in mipmaps (including the original texture).
int size = 0;
+
int w = p_width;
int h = p_height;
+
+ // Current mipmap index in the loop below. p_mipmaps is the target mipmap index.
+ // In this function, mipmap 0 represents the first mipmap instead of the original texture.
int mm = 0;
int pixsize = get_format_pixel_size(p_format);
int pixshift = get_format_pixel_rshift(p_format);
int block = get_format_block_size(p_format);
- //technically, you can still compress up to 1 px no matter the format, so commenting this
+
+ // Technically, you can still compress up to 1 px no matter the format, so commenting this.
//int minw, minh;
//get_format_min_pixel_size(p_format, minw, minh);
int minw = 1, minh = 1;
@@ -1453,17 +1460,6 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &
size += s;
- if (r_mm_width) {
- *r_mm_width = bw;
- }
- if (r_mm_height) {
- *r_mm_height = bh;
- }
-
- if (p_mipmaps >= 0 && mm == p_mipmaps) {
- break;
- }
-
if (p_mipmaps >= 0) {
w = MAX(minw, w >> 1);
h = MAX(minh, h >> 1);
@@ -1474,6 +1470,21 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &
w = MAX(minw, w >> 1);
h = MAX(minh, h >> 1);
}
+
+ // Set mipmap size.
+ // It might be necessary to put this after the minimum mipmap size check because of the possible occurrence of "1 >> 1".
+ if (r_mm_width) {
+ *r_mm_width = bw >> 1;
+ }
+ if (r_mm_height) {
+ *r_mm_height = bh >> 1;
+ }
+
+ // Reach target mipmap.
+ if (p_mipmaps >= 0 && mm == p_mipmaps) {
+ break;
+ }
+
mm++;
}
@@ -1934,7 +1945,7 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con
memcpy(wr.ptr(), ptr, size);
wr = uint8_t*();
Ref<Image> im;
- im.instance();
+ im.instantiate();
im->create(w, h, false, format, imgdata);
im->save_png("res://mipmap_" + itos(i) + ".png");
}
@@ -1974,6 +1985,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + ".");
ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + ".");
ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS));
+ ERR_FAIL_INDEX_MSG(p_format, FORMAT_MAX, "Image format out of range, please see Image's Format enum.");
int mm = 0;
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
@@ -1996,6 +2008,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + ".");
ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + ".");
ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS));
+ ERR_FAIL_INDEX_MSG(p_format, FORMAT_MAX, "Image format out of range, please see Image's Format enum.");
int mm;
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
@@ -2367,6 +2380,8 @@ Error Image::decompress() {
}
Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality) {
+ ERR_FAIL_INDEX_V_MSG(p_mode, COMPRESS_MAX, ERR_INVALID_PARAMETER, "Invalid compress mode.");
+ ERR_FAIL_INDEX_V_MSG(p_source, COMPRESS_SOURCE_MAX, ERR_INVALID_PARAMETER, "Invalid compress source.");
return compress_from_channels(p_mode, detect_used_channels(p_source), p_lossy_quality);
}
@@ -2392,6 +2407,9 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels
ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE);
_image_compress_bptc_func(this, p_lossy_quality, p_channels);
} break;
+ case COMPRESS_MAX: {
+ ERR_FAIL_V(ERR_INVALID_PARAMETER);
+ } break;
}
return OK;
@@ -2718,10 +2736,11 @@ void (*Image::_image_decompress_bptc)(Image *) = nullptr;
void (*Image::_image_decompress_etc1)(Image *) = nullptr;
void (*Image::_image_decompress_etc2)(Image *) = nullptr;
-Vector<uint8_t> (*Image::lossy_packer)(const Ref<Image> &, float) = nullptr;
-Ref<Image> (*Image::lossy_unpacker)(const Vector<uint8_t> &) = nullptr;
-Vector<uint8_t> (*Image::lossless_packer)(const Ref<Image> &) = nullptr;
-Ref<Image> (*Image::lossless_unpacker)(const Vector<uint8_t> &) = nullptr;
+Vector<uint8_t> (*Image::webp_lossy_packer)(const Ref<Image> &, float) = nullptr;
+Vector<uint8_t> (*Image::webp_lossless_packer)(const Ref<Image> &) = nullptr;
+Ref<Image> (*Image::webp_unpacker)(const Vector<uint8_t> &) = nullptr;
+Vector<uint8_t> (*Image::png_packer)(const Ref<Image> &) = nullptr;
+Ref<Image> (*Image::png_unpacker)(const Vector<uint8_t> &) = nullptr;
Vector<uint8_t> (*Image::basis_universal_packer)(const Ref<Image> &, Image::UsedChannels) = nullptr;
Ref<Image> (*Image::basis_universal_unpacker)(const Vector<uint8_t> &) = nullptr;
@@ -2985,6 +3004,8 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) {
}
void Image::adjust_bcs(float p_brightness, float p_contrast, float p_saturation) {
+ ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot adjust_bcs in compressed or custom image formats.");
+
uint8_t *w = data.ptrw();
uint32_t pixel_size = get_format_pixel_size(format);
uint32_t pixel_count = data.size() / pixel_size;
@@ -3268,7 +3289,7 @@ Ref<Image> Image::rgbe_to_srgb() {
ERR_FAIL_COND_V(format != FORMAT_RGBE9995, Ref<Image>());
Ref<Image> new_image;
- new_image.instance();
+ new_image.instantiate();
new_image->create(width, height, false, Image::FORMAT_RGB8);
for (int row = 0; row < height; row++) {
@@ -3298,7 +3319,7 @@ Ref<Image> Image::get_image_from_mipmap(int p_mipamp) const {
}
Ref<Image> image;
- image.instance();
+ image.instantiate();
image->width = w;
image->height = h;
image->format = format;
@@ -3615,7 +3636,7 @@ Image::Image(const uint8_t *p_mem_png_jpg, int p_len) {
Ref<Resource> Image::duplicate(bool p_subresources) const {
Ref<Image> copy;
- copy.instance();
+ copy.instantiate();
copy->_copy_internals_from(*this);
return copy;
}
diff --git a/core/io/image.h b/core/io/image.h
index df8f9b35a1..8f1b251ac3 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -148,10 +148,11 @@ public:
static void (*_image_decompress_etc1)(Image *);
static void (*_image_decompress_etc2)(Image *);
- static Vector<uint8_t> (*lossy_packer)(const Ref<Image> &p_image, float p_quality);
- static Ref<Image> (*lossy_unpacker)(const Vector<uint8_t> &p_buffer);
- static Vector<uint8_t> (*lossless_packer)(const Ref<Image> &p_image);
- static Ref<Image> (*lossless_unpacker)(const Vector<uint8_t> &p_buffer);
+ static Vector<uint8_t> (*webp_lossy_packer)(const Ref<Image> &p_image, float p_quality);
+ static Vector<uint8_t> (*webp_lossless_packer)(const Ref<Image> &p_image);
+ static Ref<Image> (*webp_unpacker)(const Vector<uint8_t> &p_buffer);
+ static Vector<uint8_t> (*png_packer)(const Ref<Image> &p_image);
+ static Ref<Image> (*png_unpacker)(const Vector<uint8_t> &p_buffer);
static Vector<uint8_t> (*basis_universal_packer)(const Ref<Image> &p_image, UsedChannels p_channels);
static Ref<Image> (*basis_universal_unpacker)(const Vector<uint8_t> &p_buffer);
@@ -335,11 +336,13 @@ public:
COMPRESS_ETC,
COMPRESS_ETC2,
COMPRESS_BPTC,
+ COMPRESS_MAX,
};
enum CompressSource {
COMPRESS_SOURCE_GENERIC,
COMPRESS_SOURCE_SRGB,
- COMPRESS_SOURCE_NORMAL
+ COMPRESS_SOURCE_NORMAL,
+ COMPRESS_SOURCE_MAX,
};
Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7);
diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp
index 7de038e6fe..b9fc416f65 100644
--- a/core/io/image_loader.cpp
+++ b/core/io/image_loader.cpp
@@ -35,8 +35,8 @@
bool ImageFormatLoader::recognize(const String &p_extension) const {
List<String> extensions;
get_recognized_extensions(&extensions);
- for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
- if (E->get().nocasecmp_to(p_extension) == 0) {
+ for (const String &E : extensions) {
+ if (E.nocasecmp_to(p_extension) == 0) {
return true;
}
}
@@ -163,7 +163,7 @@ RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_origin
}
Ref<Image> image;
- image.instance();
+ image.instantiate();
Error err = ImageLoader::loader[idx]->load_image(image, f, false, 1.0);
diff --git a/core/io/image_loader.h b/core/io/image_loader.h
index a5d588e0b5..6d1b1e3646 100644
--- a/core/io/image_loader.h
+++ b/core/io/image_loader.h
@@ -31,9 +31,9 @@
#ifndef IMAGE_LOADER_H
#define IMAGE_LOADER_H
+#include "core/io/file_access.h"
#include "core/io/image.h"
#include "core/io/resource_loader.h"
-#include "core/os/file_access.h"
#include "core/string/ustring.h"
#include "core/templates/list.h"
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index de37ba87dd..e3102508a3 100644
--- a/core/io/ip.cpp
+++ b/core/io/ip.cpp
@@ -83,8 +83,23 @@ struct _IP_ResolverPrivate {
continue;
}
- IP::get_singleton()->_resolve_hostname(queue[i].response, queue[i].hostname, queue[i].type);
- queue[i].status.set(queue[i].response.is_empty() ? IP::RESOLVER_STATUS_ERROR : IP::RESOLVER_STATUS_DONE);
+ mutex.lock();
+ List<IPAddress> response;
+ String hostname = queue[i].hostname;
+ IP::Type type = queue[i].type;
+ mutex.unlock();
+
+ // We should not lock while resolving the hostname,
+ // only when modifying the queue.
+ IP::get_singleton()->_resolve_hostname(response, hostname, type);
+
+ MutexLock lock(mutex);
+ // Could have been completed by another function, or deleted.
+ if (queue[i].status.get() != IP::RESOLVER_STATUS_WAITING) {
+ continue;
+ }
+ queue[i].response = response;
+ queue[i].status.set(response.is_empty() ? IP::RESOLVER_STATUS_ERROR : IP::RESOLVER_STATUS_DONE);
}
}
@@ -93,8 +108,6 @@ struct _IP_ResolverPrivate {
while (!ipr->thread_abort) {
ipr->sem.wait();
-
- MutexLock lock(ipr->mutex);
ipr->resolve_queues();
}
}
@@ -107,15 +120,20 @@ struct _IP_ResolverPrivate {
};
IPAddress IP::resolve_hostname(const String &p_hostname, IP::Type p_type) {
- MutexLock lock(resolver->mutex);
-
List<IPAddress> res;
-
String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type);
+
+ resolver->mutex.lock();
if (resolver->cache.has(key)) {
res = resolver->cache[key];
} else {
+ // This should be run unlocked so the resolver thread can keep
+ // resolving other requests.
+ resolver->mutex.unlock();
_resolve_hostname(res, p_hostname, p_type);
+ resolver->mutex.lock();
+ // We might be overriding another result, but we don't care (they are the
+ // same hostname).
resolver->cache[key] = res;
}
resolver->mutex.unlock();
@@ -129,14 +147,22 @@ IPAddress IP::resolve_hostname(const String &p_hostname, IP::Type p_type) {
}
Array IP::resolve_hostname_addresses(const String &p_hostname, Type p_type) {
- resolver->mutex.lock();
-
+ List<IPAddress> res;
String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type);
- if (!resolver->cache.has(key)) {
- _resolve_hostname(resolver->cache[key], p_hostname, p_type);
- }
- List<IPAddress> res = resolver->cache[key];
+ resolver->mutex.lock();
+ if (resolver->cache.has(key)) {
+ res = resolver->cache[key];
+ } else {
+ // This should be run unlocked so the resolver thread can keep resolving
+ // other requests.
+ resolver->mutex.unlock();
+ _resolve_hostname(res, p_hostname, p_type);
+ resolver->mutex.lock();
+ // We might be overriding another result, but we don't care (they are the
+ // same hostname).
+ resolver->cache[key] = res;
+ }
resolver->mutex.unlock();
Array result;
@@ -180,14 +206,12 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ
IP::ResolverStatus IP::get_resolve_item_status(ResolverID p_id) const {
ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, IP::RESOLVER_STATUS_NONE);
- MutexLock lock(resolver->mutex);
-
- if (resolver->queue[p_id].status.get() == IP::RESOLVER_STATUS_NONE) {
+ IP::ResolverStatus res = resolver->queue[p_id].status.get();
+ if (res == IP::RESOLVER_STATUS_NONE) {
ERR_PRINT("Condition status == IP::RESOLVER_STATUS_NONE");
- resolver->mutex.unlock();
return IP::RESOLVER_STATUS_NONE;
}
- return resolver->queue[p_id].status.get();
+ return res;
}
IPAddress IP::get_resolve_item_address(ResolverID p_id) const {
@@ -197,14 +221,11 @@ IPAddress IP::get_resolve_item_address(ResolverID p_id) const {
if (resolver->queue[p_id].status.get() != IP::RESOLVER_STATUS_DONE) {
ERR_PRINT("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet.");
- resolver->mutex.unlock();
return IPAddress();
}
List<IPAddress> res = resolver->queue[p_id].response;
- resolver->mutex.unlock();
-
for (int i = 0; i < res.size(); ++i) {
if (res[i].is_valid()) {
return res[i];
@@ -215,19 +236,15 @@ IPAddress IP::get_resolve_item_address(ResolverID p_id) const {
Array IP::get_resolve_item_addresses(ResolverID p_id) const {
ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, Array());
-
- resolver->mutex.lock();
+ MutexLock lock(resolver->mutex);
if (resolver->queue[p_id].status.get() != IP::RESOLVER_STATUS_DONE) {
ERR_PRINT("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet.");
- resolver->mutex.unlock();
return Array();
}
List<IPAddress> res = resolver->queue[p_id].response;
- resolver->mutex.unlock();
-
Array result;
for (int i = 0; i < res.size(); ++i) {
if (res[i].is_valid()) {
@@ -240,8 +257,6 @@ Array IP::get_resolve_item_addresses(ResolverID p_id) const {
void IP::erase_resolve_item(ResolverID p_id) {
ERR_FAIL_INDEX(p_id, IP::RESOLVER_MAX_QUERIES);
- MutexLock lock(resolver->mutex);
-
resolver->queue[p_id].status.set(IP::RESOLVER_STATUS_NONE);
}
@@ -262,8 +277,8 @@ Array IP::_get_local_addresses() const {
Array addresses;
List<IPAddress> ip_addresses;
get_local_addresses(&ip_addresses);
- for (List<IPAddress>::Element *E = ip_addresses.front(); E; E = E->next()) {
- addresses.push_back(E->get());
+ for (const IPAddress &E : ip_addresses) {
+ addresses.push_back(E);
}
return addresses;
@@ -281,8 +296,8 @@ Array IP::_get_local_interfaces() const {
rc["index"] = c.index;
Array ips;
- for (const List<IPAddress>::Element *F = c.ip_addresses.front(); F; F = F->next()) {
- ips.push_front(F->get());
+ for (const IPAddress &F : c.ip_addresses) {
+ ips.push_front(F);
}
rc["addresses"] = ips;
@@ -296,8 +311,8 @@ void IP::get_local_addresses(List<IPAddress> *r_addresses) const {
Map<String, Interface_Info> interfaces;
get_local_interfaces(&interfaces);
for (Map<String, Interface_Info>::Element *E = interfaces.front(); E; E = E->next()) {
- for (const List<IPAddress>::Element *F = E->get().ip_addresses.front(); F; F = F->next()) {
- r_addresses->push_front(F->get());
+ for (const IPAddress &F : E->get().ip_addresses) {
+ r_addresses->push_front(F);
}
}
}
diff --git a/core/io/json.cpp b/core/io/json.cpp
index 394cf216e8..5823afbdcd 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -45,7 +45,7 @@ const char *JSON::tk_name[TK_MAX] = {
"EOF",
};
-static String _make_indent(const String &p_indent, int p_size) {
+String JSON::_make_indent(const String &p_indent, int p_size) {
String indent_text = "";
if (!p_indent.is_empty()) {
for (int i = 0; i < p_size; i++) {
@@ -55,7 +55,7 @@ static String _make_indent(const String &p_indent, int p_size) {
return indent_text;
}
-String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys) {
+String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, Set<const void *> &p_markers, bool p_full_precision) {
String colon = ":";
String end_statement = "";
@@ -71,8 +71,17 @@ String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_
return p_var.operator bool() ? "true" : "false";
case Variant::INT:
return itos(p_var);
- case Variant::FLOAT:
- return rtos(p_var);
+ case Variant::FLOAT: {
+ double num = p_var;
+ if (p_full_precision) {
+ // Store unreliable digits (17) instead of just reliable
+ // digits (14) so that the value can be decoded exactly.
+ return String::num(num, 17 - (int)floor(log10(num)));
+ } else {
+ // Store only reliable digits (14) by default.
+ return String::num(num, 14 - (int)floor(log10(num)));
+ }
+ }
case Variant::PACKED_INT32_ARRAY:
case Variant::PACKED_INT64_ARRAY:
case Variant::PACKED_FLOAT32_ARRAY:
@@ -82,20 +91,29 @@ String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_
String s = "[";
s += end_statement;
Array a = p_var;
+
+ ERR_FAIL_COND_V_MSG(p_markers.has(a.id()), "\"[...]\"", "Converting circular structure to JSON.");
+ p_markers.insert(a.id());
+
for (int i = 0; i < a.size(); i++) {
if (i > 0) {
s += ",";
s += end_statement;
}
- s += _make_indent(p_indent, p_cur_indent + 1) + _print_var(a[i], p_indent, p_cur_indent + 1, p_sort_keys);
+ s += _make_indent(p_indent, p_cur_indent + 1) + _stringify(a[i], p_indent, p_cur_indent + 1, p_sort_keys, p_markers);
}
s += end_statement + _make_indent(p_indent, p_cur_indent) + "]";
+ p_markers.erase(a.id());
return s;
}
case Variant::DICTIONARY: {
String s = "{";
s += end_statement;
Dictionary d = p_var;
+
+ ERR_FAIL_COND_V_MSG(p_markers.has(d.id()), "\"{...}\"", "Converting circular structure to JSON.");
+ p_markers.insert(d.id());
+
List<Variant> keys;
d.get_key_list(&keys);
@@ -103,17 +121,21 @@ String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_
keys.sort();
}
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- if (E != keys.front()) {
+ bool first_key = true;
+ for (const Variant &E : keys) {
+ if (first_key) {
+ first_key = false;
+ } else {
s += ",";
s += end_statement;
}
- s += _make_indent(p_indent, p_cur_indent + 1) + _print_var(String(E->get()), p_indent, p_cur_indent + 1, p_sort_keys);
+ s += _make_indent(p_indent, p_cur_indent + 1) + _stringify(String(E), p_indent, p_cur_indent + 1, p_sort_keys, p_markers);
s += colon;
- s += _print_var(d[E->get()], p_indent, p_cur_indent + 1, p_sort_keys);
+ s += _stringify(d[E], p_indent, p_cur_indent + 1, p_sort_keys, p_markers);
}
s += end_statement + _make_indent(p_indent, p_cur_indent) + "}";
+ p_markers.erase(d.id());
return s;
}
default:
@@ -121,10 +143,6 @@ String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_
}
}
-String JSON::print(const Variant &p_var, const String &p_indent, bool p_sort_keys) {
- return _print_var(p_var, p_indent, 0, p_sort_keys);
-}
-
Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str) {
while (p_len > 0) {
switch (p_str[index]) {
@@ -479,7 +497,7 @@ Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index,
return ERR_PARSE_ERROR;
}
-Error JSON::parse(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line) {
+Error JSON::_parse_string(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line) {
const char32_t *str = p_json.ptr();
int idx = 0;
int len = p_json.length();
@@ -510,34 +528,24 @@ Error JSON::parse(const String &p_json, Variant &r_ret, String &r_err_str, int &
return err;
}
-Error JSONParser::parse_string(const String &p_json_string) {
- return JSON::parse(p_json_string, data, err_text, err_line);
-}
-String JSONParser::get_error_text() const {
- return err_text;
-}
-int JSONParser::get_error_line() const {
- return err_line;
-}
-Variant JSONParser::get_data() const {
- return data;
+String JSON::stringify(const Variant &p_var, const String &p_indent, bool p_sort_keys, bool p_full_precision) {
+ Set<const void *> markers;
+ return _stringify(p_var, p_indent, 0, p_sort_keys, markers, p_full_precision);
}
-Error JSONParser::decode_data(const Variant &p_data, const String &p_indent, bool p_sort_keys) {
- string = JSON::print(p_data, p_indent, p_sort_keys);
- data = p_data;
- return OK;
+Error JSON::parse(const String &p_json_string) {
+ Error err = _parse_string(p_json_string, data, err_str, err_line);
+ if (err == Error::OK) {
+ err_line = 0;
+ }
+ return err;
}
-String JSONParser::get_string() const {
- return string;
-}
+void JSON::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("stringify", "data", "indent", "sort_keys", "full_precision"), &JSON::stringify, DEFVAL(""), DEFVAL(true), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("parse", "json_string"), &JSON::parse);
-void JSONParser::_bind_methods() {
- ClassDB::bind_method(D_METHOD("parse_string", "json_string"), &JSONParser::parse_string);
- ClassDB::bind_method(D_METHOD("get_error_text"), &JSONParser::get_error_text);
- ClassDB::bind_method(D_METHOD("get_error_line"), &JSONParser::get_error_line);
- ClassDB::bind_method(D_METHOD("get_data"), &JSONParser::get_data);
- ClassDB::bind_method(D_METHOD("decode_data", "data", "indent", "sort_keys"), &JSONParser::decode_data, DEFVAL(""), DEFVAL(true));
- ClassDB::bind_method(D_METHOD("get_string"), &JSONParser::get_string);
+ ClassDB::bind_method(D_METHOD("get_data"), &JSON::get_data);
+ ClassDB::bind_method(D_METHOD("get_error_line"), &JSON::get_error_line);
+ ClassDB::bind_method(D_METHOD("get_error_message"), &JSON::get_error_message);
}
diff --git a/core/io/json.h b/core/io/json.h
index 537477666e..f20c97f540 100644
--- a/core/io/json.h
+++ b/core/io/json.h
@@ -31,9 +31,12 @@
#ifndef JSON_H
#define JSON_H
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
#include "core/variant/variant.h"
-class JSON {
+
+class JSON : public RefCounted {
+ GDCLASS(JSON, RefCounted);
+
enum TokenType {
TK_CURLY_BRACKET_OPEN,
TK_CURLY_BRACKET_CLOSE,
@@ -60,39 +63,30 @@ class JSON {
Variant value;
};
- static const char *tk_name[TK_MAX];
+ Variant data;
+ String err_str;
+ int err_line = 0;
- static String _print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys);
+ static const char *tk_name[];
+ static String _make_indent(const String &p_indent, int p_size);
+ static String _stringify(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, Set<const void *> &p_markers, bool p_full_precision = false);
static Error _get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str);
static Error _parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str);
static Error _parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str);
static Error _parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str);
-
-public:
- static String print(const Variant &p_var, const String &p_indent = "", bool p_sort_keys = true);
- static Error parse(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line);
-};
-
-class JSONParser : public Reference {
- GDCLASS(JSONParser, Reference);
-
- Variant data;
- String string;
- String err_text;
- int err_line = 0;
+ static Error _parse_string(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line);
protected:
static void _bind_methods();
public:
- Error parse_string(const String &p_json_string);
- String get_error_text() const;
- int get_error_line() const;
- Variant get_data() const;
+ String stringify(const Variant &p_var, const String &p_indent = "", bool p_sort_keys = true, bool p_full_precision = false);
+ Error parse(const String &p_json_string);
- Error decode_data(const Variant &p_data, const String &p_indent = "", bool p_sort_keys = true);
- String get_string() const;
+ inline Variant get_data() const { return data; }
+ inline int get_error_line() const { return err_line; }
+ inline String get_error_message() const { return err_str; }
};
#endif // JSON_H
diff --git a/core/io/logger.cpp b/core/io/logger.cpp
index 8a07459a1d..09539f716c 100644
--- a/core/io/logger.cpp
+++ b/core/io/logger.cpp
@@ -31,8 +31,9 @@
#include "logger.h"
#include "core/config/project_settings.h"
-#include "core/os/dir_access.h"
+#include "core/io/dir_access.h"
#include "core/os/os.h"
+#include "core/os/time.h"
#include "core/string/print_string.h"
#if defined(MINGW_ENABLED) || defined(_MSC_VER)
@@ -156,11 +157,7 @@ void RotatedFileLogger::rotate_file() {
if (FileAccess::exists(base_path)) {
if (max_files > 1) {
- char timestamp[21];
- OS::Date date = OS::get_singleton()->get_date();
- OS::Time time = OS::get_singleton()->get_time();
- sprintf(timestamp, "_%04d-%02d-%02d_%02d.%02d.%02d", date.year, date.month, date.day, time.hour, time.min, time.sec);
-
+ String timestamp = Time::get_singleton()->get_datetime_string_from_system().replace(":", ".");
String backup_name = base_path.get_basename() + timestamp;
if (base_path.get_extension() != String()) {
backup_name += "." + base_path.get_extension();
diff --git a/core/io/logger.h b/core/io/logger.h
index a12945911c..ccf68562d6 100644
--- a/core/io/logger.h
+++ b/core/io/logger.h
@@ -31,7 +31,7 @@
#ifndef LOGGER_H
#define LOGGER_H
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include "core/string/ustring.h"
#include "core/templates/vector.h"
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index 0282609270..e7d5b78d14 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -30,7 +30,7 @@
#include "marshalls.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
#include "core/os/keyboard.h"
#include "core/string/print_string.h"
@@ -111,6 +111,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
*r_len = 4;
}
+ // Note: We cannot use sizeof(real_t) for decoding, in case a different size is encoded.
+ // Decoding math types always checks for the encoded size, while encoding always uses compilation setting.
+ // This does lead to some code duplication for decoding, but compatibility is the priority.
switch (type & ENCODE_MASK) {
case Variant::NIL: {
r_variant = Variant();
@@ -144,18 +147,18 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::FLOAT: {
if (type & ENCODE_FLAG_64) {
- ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V((size_t)len < sizeof(double), ERR_INVALID_DATA);
double val = decode_double(buf);
r_variant = val;
if (r_len) {
- (*r_len) += 8;
+ (*r_len) += sizeof(double);
}
} else {
- ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V((size_t)len < sizeof(float), ERR_INVALID_DATA);
float val = decode_float(buf);
r_variant = val;
if (r_len) {
- (*r_len) += 4;
+ (*r_len) += sizeof(float);
}
}
@@ -172,15 +175,25 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
// math types
case Variant::VECTOR2: {
- ERR_FAIL_COND_V(len < 4 * 2, ERR_INVALID_DATA);
Vector2 val;
- val.x = decode_float(&buf[0]);
- val.y = decode_float(&buf[4]);
- r_variant = val;
+ if (type & ENCODE_FLAG_64) {
+ ERR_FAIL_COND_V((size_t)len < sizeof(double) * 2, ERR_INVALID_DATA);
+ val.x = decode_double(&buf[0]);
+ val.y = decode_double(&buf[sizeof(double)]);
- if (r_len) {
- (*r_len) += 4 * 2;
+ if (r_len) {
+ (*r_len) += sizeof(double) * 2;
+ }
+ } else {
+ ERR_FAIL_COND_V((size_t)len < sizeof(float) * 2, ERR_INVALID_DATA);
+ val.x = decode_float(&buf[0]);
+ val.y = decode_float(&buf[sizeof(float)]);
+
+ if (r_len) {
+ (*r_len) += sizeof(float) * 2;
+ }
}
+ r_variant = val;
} break;
case Variant::VECTOR2I: {
@@ -196,17 +209,29 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::RECT2: {
- ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA);
Rect2 val;
- val.position.x = decode_float(&buf[0]);
- val.position.y = decode_float(&buf[4]);
- val.size.x = decode_float(&buf[8]);
- val.size.y = decode_float(&buf[12]);
- r_variant = val;
+ if (type & ENCODE_FLAG_64) {
+ ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
+ val.position.x = decode_double(&buf[0]);
+ val.position.y = decode_double(&buf[sizeof(double)]);
+ val.size.x = decode_double(&buf[sizeof(double) * 2]);
+ val.size.y = decode_double(&buf[sizeof(double) * 3]);
- if (r_len) {
- (*r_len) += 4 * 4;
+ if (r_len) {
+ (*r_len) += sizeof(double) * 4;
+ }
+ } else {
+ ERR_FAIL_COND_V((size_t)len < sizeof(float) * 4, ERR_INVALID_DATA);
+ val.position.x = decode_float(&buf[0]);
+ val.position.y = decode_float(&buf[sizeof(float)]);
+ val.size.x = decode_float(&buf[sizeof(float) * 2]);
+ val.size.y = decode_float(&buf[sizeof(float) * 3]);
+
+ if (r_len) {
+ (*r_len) += sizeof(float) * 4;
+ }
}
+ r_variant = val;
} break;
case Variant::RECT2I: {
@@ -224,16 +249,27 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::VECTOR3: {
- ERR_FAIL_COND_V(len < 4 * 3, ERR_INVALID_DATA);
Vector3 val;
- val.x = decode_float(&buf[0]);
- val.y = decode_float(&buf[4]);
- val.z = decode_float(&buf[8]);
- r_variant = val;
+ if (type & ENCODE_FLAG_64) {
+ ERR_FAIL_COND_V((size_t)len < sizeof(double) * 3, ERR_INVALID_DATA);
+ val.x = decode_double(&buf[0]);
+ val.y = decode_double(&buf[sizeof(double)]);
+ val.z = decode_double(&buf[sizeof(double) * 2]);
- if (r_len) {
- (*r_len) += 4 * 3;
+ if (r_len) {
+ (*r_len) += sizeof(double) * 3;
+ }
+ } else {
+ ERR_FAIL_COND_V((size_t)len < sizeof(float) * 3, ERR_INVALID_DATA);
+ val.x = decode_float(&buf[0]);
+ val.y = decode_float(&buf[sizeof(float)]);
+ val.z = decode_float(&buf[sizeof(float) * 2]);
+
+ if (r_len) {
+ (*r_len) += sizeof(float) * 3;
+ }
}
+ r_variant = val;
} break;
case Variant::VECTOR3I: {
@@ -250,101 +286,177 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::TRANSFORM2D: {
- ERR_FAIL_COND_V(len < 4 * 6, ERR_INVALID_DATA);
Transform2D val;
- for (int i = 0; i < 3; i++) {
- for (int j = 0; j < 2; j++) {
- val.elements[i][j] = decode_float(&buf[(i * 2 + j) * 4]);
+ if (type & ENCODE_FLAG_64) {
+ ERR_FAIL_COND_V((size_t)len < sizeof(double) * 6, ERR_INVALID_DATA);
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 2; j++) {
+ val.elements[i][j] = decode_double(&buf[(i * 2 + j) * sizeof(double)]);
+ }
}
- }
- r_variant = val;
+ if (r_len) {
+ (*r_len) += sizeof(double) * 6;
+ }
+ } else {
+ ERR_FAIL_COND_V((size_t)len < sizeof(float) * 6, ERR_INVALID_DATA);
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 2; j++) {
+ val.elements[i][j] = decode_float(&buf[(i * 2 + j) * sizeof(float)]);
+ }
+ }
- if (r_len) {
- (*r_len) += 4 * 6;
+ if (r_len) {
+ (*r_len) += sizeof(float) * 6;
+ }
}
+ r_variant = val;
} break;
case Variant::PLANE: {
- ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA);
Plane val;
- val.normal.x = decode_float(&buf[0]);
- val.normal.y = decode_float(&buf[4]);
- val.normal.z = decode_float(&buf[8]);
- val.d = decode_float(&buf[12]);
- r_variant = val;
+ if (type & ENCODE_FLAG_64) {
+ ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
+ val.normal.x = decode_double(&buf[0]);
+ val.normal.y = decode_double(&buf[sizeof(double)]);
+ val.normal.z = decode_double(&buf[sizeof(double) * 2]);
+ val.d = decode_double(&buf[sizeof(double) * 3]);
- if (r_len) {
- (*r_len) += 4 * 4;
+ if (r_len) {
+ (*r_len) += sizeof(double) * 4;
+ }
+ } else {
+ ERR_FAIL_COND_V((size_t)len < sizeof(float) * 4, ERR_INVALID_DATA);
+ val.normal.x = decode_float(&buf[0]);
+ val.normal.y = decode_float(&buf[sizeof(float)]);
+ val.normal.z = decode_float(&buf[sizeof(float) * 2]);
+ val.d = decode_float(&buf[sizeof(float) * 3]);
+
+ if (r_len) {
+ (*r_len) += sizeof(float) * 4;
+ }
}
+ r_variant = val;
} break;
- case Variant::QUAT: {
- ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA);
- Quat val;
- val.x = decode_float(&buf[0]);
- val.y = decode_float(&buf[4]);
- val.z = decode_float(&buf[8]);
- val.w = decode_float(&buf[12]);
- r_variant = val;
+ case Variant::QUATERNION: {
+ Quaternion val;
+ if (type & ENCODE_FLAG_64) {
+ ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
+ val.x = decode_double(&buf[0]);
+ val.y = decode_double(&buf[sizeof(double)]);
+ val.z = decode_double(&buf[sizeof(double) * 2]);
+ val.w = decode_double(&buf[sizeof(double) * 3]);
- if (r_len) {
- (*r_len) += 4 * 4;
+ if (r_len) {
+ (*r_len) += sizeof(double) * 4;
+ }
+ } else {
+ ERR_FAIL_COND_V((size_t)len < sizeof(float) * 4, ERR_INVALID_DATA);
+ val.x = decode_float(&buf[0]);
+ val.y = decode_float(&buf[sizeof(float)]);
+ val.z = decode_float(&buf[sizeof(float) * 2]);
+ val.w = decode_float(&buf[sizeof(float) * 3]);
+
+ if (r_len) {
+ (*r_len) += sizeof(float) * 4;
+ }
}
+ r_variant = val;
} break;
case Variant::AABB: {
- ERR_FAIL_COND_V(len < 4 * 6, ERR_INVALID_DATA);
AABB val;
- val.position.x = decode_float(&buf[0]);
- val.position.y = decode_float(&buf[4]);
- val.position.z = decode_float(&buf[8]);
- val.size.x = decode_float(&buf[12]);
- val.size.y = decode_float(&buf[16]);
- val.size.z = decode_float(&buf[20]);
- r_variant = val;
+ if (type & ENCODE_FLAG_64) {
+ ERR_FAIL_COND_V((size_t)len < sizeof(double) * 6, ERR_INVALID_DATA);
+ val.position.x = decode_double(&buf[0]);
+ val.position.y = decode_double(&buf[sizeof(double)]);
+ val.position.z = decode_double(&buf[sizeof(double) * 2]);
+ val.size.x = decode_double(&buf[sizeof(double) * 3]);
+ val.size.y = decode_double(&buf[sizeof(double) * 4]);
+ val.size.z = decode_double(&buf[sizeof(double) * 5]);
- if (r_len) {
- (*r_len) += 4 * 6;
+ if (r_len) {
+ (*r_len) += sizeof(double) * 6;
+ }
+ } else {
+ ERR_FAIL_COND_V((size_t)len < sizeof(float) * 6, ERR_INVALID_DATA);
+ val.position.x = decode_float(&buf[0]);
+ val.position.y = decode_float(&buf[sizeof(float)]);
+ val.position.z = decode_float(&buf[sizeof(float) * 2]);
+ val.size.x = decode_float(&buf[sizeof(float) * 3]);
+ val.size.y = decode_float(&buf[sizeof(float) * 4]);
+ val.size.z = decode_float(&buf[sizeof(float) * 5]);
+
+ if (r_len) {
+ (*r_len) += sizeof(float) * 6;
+ }
}
+ r_variant = val;
} break;
case Variant::BASIS: {
- ERR_FAIL_COND_V(len < 4 * 9, ERR_INVALID_DATA);
Basis val;
- for (int i = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++) {
- val.elements[i][j] = decode_float(&buf[(i * 3 + j) * 4]);
+ if (type & ENCODE_FLAG_64) {
+ ERR_FAIL_COND_V((size_t)len < sizeof(double) * 9, ERR_INVALID_DATA);
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ val.elements[i][j] = decode_double(&buf[(i * 3 + j) * sizeof(double)]);
+ }
}
- }
- r_variant = val;
+ if (r_len) {
+ (*r_len) += sizeof(double) * 9;
+ }
+ } else {
+ ERR_FAIL_COND_V((size_t)len < sizeof(float) * 9, ERR_INVALID_DATA);
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ val.elements[i][j] = decode_float(&buf[(i * 3 + j) * sizeof(float)]);
+ }
+ }
- if (r_len) {
- (*r_len) += 4 * 9;
+ if (r_len) {
+ (*r_len) += sizeof(float) * 9;
+ }
}
+ r_variant = val;
} break;
- case Variant::TRANSFORM: {
- ERR_FAIL_COND_V(len < 4 * 12, ERR_INVALID_DATA);
- Transform val;
- for (int i = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++) {
- val.basis.elements[i][j] = decode_float(&buf[(i * 3 + j) * 4]);
+ case Variant::TRANSFORM3D: {
+ Transform3D val;
+ if (type & ENCODE_FLAG_64) {
+ ERR_FAIL_COND_V((size_t)len < sizeof(double) * 12, ERR_INVALID_DATA);
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ val.basis.elements[i][j] = decode_double(&buf[(i * 3 + j) * sizeof(double)]);
+ }
}
- }
- val.origin[0] = decode_float(&buf[36]);
- val.origin[1] = decode_float(&buf[40]);
- val.origin[2] = decode_float(&buf[44]);
+ val.origin[0] = decode_double(&buf[sizeof(double) * 9]);
+ val.origin[1] = decode_double(&buf[sizeof(double) * 10]);
+ val.origin[2] = decode_double(&buf[sizeof(double) * 11]);
- r_variant = val;
+ if (r_len) {
+ (*r_len) += sizeof(double) * 12;
+ }
+ } else {
+ ERR_FAIL_COND_V((size_t)len < sizeof(float) * 12, ERR_INVALID_DATA);
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ val.basis.elements[i][j] = decode_float(&buf[(i * 3 + j) * sizeof(float)]);
+ }
+ }
+ val.origin[0] = decode_float(&buf[sizeof(float) * 9]);
+ val.origin[1] = decode_float(&buf[sizeof(float) * 10]);
+ val.origin[2] = decode_float(&buf[sizeof(float) * 11]);
- if (r_len) {
- (*r_len) += 4 * 12;
+ if (r_len) {
+ (*r_len) += sizeof(float) * 12;
+ }
}
+ r_variant = val;
} break;
-
// misc types
case Variant::COLOR: {
ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA);
@@ -356,9 +468,8 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
r_variant = val;
if (r_len) {
- (*r_len) += 4 * 4;
+ (*r_len) += 4 * 4; // Colors should always be in single-precision.
}
-
} break;
case Variant::STRING_NAME: {
String str;
@@ -436,7 +547,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
r_variant = (Object *)nullptr;
} else {
Ref<EncodedObjectAsID> obj_as_id;
- obj_as_id.instance();
+ obj_as_id.instantiate();
obj_as_id->set_object_id(val);
r_variant = obj_as_id;
@@ -454,7 +565,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
if (str == String()) {
r_variant = (Object *)nullptr;
} else {
- Object *obj = ClassDB::instance(str);
+ Object *obj = ClassDB::instantiate(str);
ERR_FAIL_COND_V(!obj, ERR_UNAVAILABLE);
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
@@ -463,7 +574,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
buf += 4;
len -= 4;
if (r_len) {
- (*r_len) += 4;
+ (*r_len) += 4; // Size of count number.
}
for (int i = 0; i < count; i++) {
@@ -489,8 +600,8 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
obj->set(str, value);
}
- if (Object::cast_to<Reference>(obj)) {
- REF ref = REF(Object::cast_to<Reference>(obj));
+ if (Object::cast_to<RefCounted>(obj)) {
+ REF ref = REF(Object::cast_to<RefCounted>(obj));
r_variant = ref;
} else {
r_variant = obj;
@@ -516,7 +627,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
len -= 4;
if (r_len) {
- (*r_len) += 4;
+ (*r_len) += 4; // Size of count number.
}
Dictionary d;
@@ -559,7 +670,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
len -= 4;
if (r_len) {
- (*r_len) += 4;
+ (*r_len) += 4; // Size of count number.
}
Array varr;
@@ -635,7 +746,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::PACKED_INT64_ARRAY: {
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
- int64_t count = decode_uint64(buf);
+ int32_t count = decode_uint32(buf);
buf += 4;
len -= 4;
ERR_FAIL_MUL_OF(count, 8, ERR_INVALID_DATA);
@@ -684,7 +795,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::PACKED_FLOAT64_ARRAY: {
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
- int64_t count = decode_uint64(buf);
+ int32_t count = decode_uint32(buf);
buf += 4;
len -= 4;
ERR_FAIL_MUL_OF(count, 8, ERR_INVALID_DATA);
@@ -693,7 +804,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
Vector<double> data;
if (count) {
- //const double*rbuf=(const double*)buf;
data.resize(count);
double *w = data.ptrw();
for (int64_t i = 0; i < count; i++) {
@@ -716,9 +826,8 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
len -= 4;
if (r_len) {
- (*r_len) += 4;
+ (*r_len) += 4; // Size of count number.
}
- //printf("string count: %i\n",count);
for (int32_t i = 0; i < count; i++) {
String str;
@@ -739,30 +848,57 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
buf += 4;
len -= 4;
- ERR_FAIL_MUL_OF(count, 4 * 2, ERR_INVALID_DATA);
- ERR_FAIL_COND_V(count < 0 || count * 4 * 2 > len, ERR_INVALID_DATA);
Vector<Vector2> varray;
- if (r_len) {
- (*r_len) += 4;
- }
-
- if (count) {
- varray.resize(count);
- Vector2 *w = varray.ptrw();
+ if (type & ENCODE_FLAG_64) {
+ ERR_FAIL_MUL_OF(count, sizeof(double) * 2, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(count < 0 || count * sizeof(double) * 2 > (size_t)len, ERR_INVALID_DATA);
- for (int32_t i = 0; i < count; i++) {
- w[i].x = decode_float(buf + i * 4 * 2 + 4 * 0);
- w[i].y = decode_float(buf + i * 4 * 2 + 4 * 1);
+ if (r_len) {
+ (*r_len) += 4; // Size of count number.
}
- int adv = 4 * 2 * count;
+ if (count) {
+ varray.resize(count);
+ Vector2 *w = varray.ptrw();
+
+ for (int32_t i = 0; i < count; i++) {
+ w[i].x = decode_double(buf + i * sizeof(double) * 2 + sizeof(double) * 0);
+ w[i].y = decode_double(buf + i * sizeof(double) * 2 + sizeof(double) * 1);
+ }
+
+ int adv = sizeof(double) * 2 * count;
+
+ if (r_len) {
+ (*r_len) += adv;
+ }
+ len -= adv;
+ buf += adv;
+ }
+ } else {
+ ERR_FAIL_MUL_OF(count, sizeof(float) * 2, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(count < 0 || count * sizeof(float) * 2 > (size_t)len, ERR_INVALID_DATA);
if (r_len) {
- (*r_len) += adv;
+ (*r_len) += 4; // Size of count number.
}
- }
+ if (count) {
+ varray.resize(count);
+ Vector2 *w = varray.ptrw();
+
+ for (int32_t i = 0; i < count; i++) {
+ w[i].x = decode_float(buf + i * sizeof(float) * 2 + sizeof(float) * 0);
+ w[i].y = decode_float(buf + i * sizeof(float) * 2 + sizeof(float) * 1);
+ }
+
+ int adv = sizeof(float) * 2 * count;
+
+ if (r_len) {
+ (*r_len) += adv;
+ }
+ }
+ }
r_variant = varray;
} break;
@@ -772,32 +908,61 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
buf += 4;
len -= 4;
- ERR_FAIL_MUL_OF(count, 4 * 3, ERR_INVALID_DATA);
- ERR_FAIL_COND_V(count < 0 || count * 4 * 3 > len, ERR_INVALID_DATA);
-
Vector<Vector3> varray;
- if (r_len) {
- (*r_len) += 4;
- }
-
- if (count) {
- varray.resize(count);
- Vector3 *w = varray.ptrw();
+ if (type & ENCODE_FLAG_64) {
+ ERR_FAIL_MUL_OF(count, sizeof(double) * 3, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(count < 0 || count * sizeof(double) * 3 > (size_t)len, ERR_INVALID_DATA);
- for (int32_t i = 0; i < count; i++) {
- w[i].x = decode_float(buf + i * 4 * 3 + 4 * 0);
- w[i].y = decode_float(buf + i * 4 * 3 + 4 * 1);
- w[i].z = decode_float(buf + i * 4 * 3 + 4 * 2);
+ if (r_len) {
+ (*r_len) += 4; // Size of count number.
}
- int adv = 4 * 3 * count;
+ if (count) {
+ varray.resize(count);
+ Vector3 *w = varray.ptrw();
+
+ for (int32_t i = 0; i < count; i++) {
+ w[i].x = decode_double(buf + i * sizeof(double) * 3 + sizeof(double) * 0);
+ w[i].y = decode_double(buf + i * sizeof(double) * 3 + sizeof(double) * 1);
+ w[i].z = decode_double(buf + i * sizeof(double) * 3 + sizeof(double) * 2);
+ }
+
+ int adv = sizeof(double) * 3 * count;
+
+ if (r_len) {
+ (*r_len) += adv;
+ }
+ len -= adv;
+ buf += adv;
+ }
+ } else {
+ ERR_FAIL_MUL_OF(count, sizeof(float) * 3, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(count < 0 || count * sizeof(float) * 3 > (size_t)len, ERR_INVALID_DATA);
if (r_len) {
- (*r_len) += adv;
+ (*r_len) += 4; // Size of count number.
}
- }
+ if (count) {
+ varray.resize(count);
+ Vector3 *w = varray.ptrw();
+
+ for (int32_t i = 0; i < count; i++) {
+ w[i].x = decode_float(buf + i * sizeof(float) * 3 + sizeof(float) * 0);
+ w[i].y = decode_float(buf + i * sizeof(float) * 3 + sizeof(float) * 1);
+ w[i].z = decode_float(buf + i * sizeof(float) * 3 + sizeof(float) * 2);
+ }
+
+ int adv = sizeof(float) * 3 * count;
+
+ if (r_len) {
+ (*r_len) += adv;
+ }
+ len -= adv;
+ buf += adv;
+ }
+ }
r_variant = varray;
} break;
@@ -813,7 +978,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
Vector<Color> carray;
if (r_len) {
- (*r_len) += 4;
+ (*r_len) += 4; // Size of count number.
}
if (count) {
@@ -821,6 +986,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
Color *w = carray.ptrw();
for (int32_t i = 0; i < count; i++) {
+ // Colors should always be in single-precision.
w[i].r = decode_float(buf + i * 4 * 4 + 4 * 0);
w[i].g = decode_float(buf + i * 4 * 4 + 4 * 1);
w[i].b = decode_float(buf + i * 4 * 4 + 4 * 2);
@@ -864,7 +1030,8 @@ static void _encode_string(const String &p_string, uint8_t *&buf, int &r_len) {
}
}
-Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects) {
+Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects, int p_depth) {
+ ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ERR_OUT_OF_MEMORY, "Potential inifite recursion detected. Bailing.");
uint8_t *buf = r_buffer;
r_len = 0;
@@ -882,14 +1049,14 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
double d = p_variant;
float f = d;
if (double(f) != d) {
- flags |= ENCODE_FLAG_64; //always encode real as double
+ flags |= ENCODE_FLAG_64;
}
} break;
case Variant::OBJECT: {
// Test for potential wrong values sent by the debugger when it breaks.
Object *obj = p_variant.get_validated_object();
if (!obj) {
- // Object is invalid, send a nullptr instead.
+ // Object is invalid, send a nullptr instead.
if (buf) {
encode_uint32(Variant::NIL, buf);
}
@@ -1013,11 +1180,11 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
case Variant::VECTOR2: {
if (buf) {
Vector2 v2 = p_variant;
- encode_float(v2.x, &buf[0]);
- encode_float(v2.y, &buf[4]);
+ encode_real(v2.x, &buf[0]);
+ encode_real(v2.y, &buf[sizeof(real_t)]);
}
- r_len += 2 * 4;
+ r_len += 2 * sizeof(real_t);
} break;
case Variant::VECTOR2I: {
@@ -1033,12 +1200,12 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
case Variant::RECT2: {
if (buf) {
Rect2 r2 = p_variant;
- encode_float(r2.position.x, &buf[0]);
- encode_float(r2.position.y, &buf[4]);
- encode_float(r2.size.x, &buf[8]);
- encode_float(r2.size.y, &buf[12]);
+ encode_real(r2.position.x, &buf[0]);
+ encode_real(r2.position.y, &buf[sizeof(real_t)]);
+ encode_real(r2.size.x, &buf[sizeof(real_t) * 2]);
+ encode_real(r2.size.y, &buf[sizeof(real_t) * 3]);
}
- r_len += 4 * 4;
+ r_len += 4 * sizeof(real_t);
} break;
case Variant::RECT2I: {
@@ -1055,12 +1222,12 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
case Variant::VECTOR3: {
if (buf) {
Vector3 v3 = p_variant;
- encode_float(v3.x, &buf[0]);
- encode_float(v3.y, &buf[4]);
- encode_float(v3.z, &buf[8]);
+ encode_real(v3.x, &buf[0]);
+ encode_real(v3.y, &buf[sizeof(real_t)]);
+ encode_real(v3.z, &buf[sizeof(real_t) * 2]);
}
- r_len += 3 * 4;
+ r_len += 3 * sizeof(real_t);
} break;
case Variant::VECTOR3I: {
@@ -1079,50 +1246,50 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
Transform2D val = p_variant;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
- memcpy(&buf[(i * 2 + j) * 4], &val.elements[i][j], sizeof(float));
+ memcpy(&buf[(i * 2 + j) * sizeof(real_t)], &val.elements[i][j], sizeof(real_t));
}
}
}
- r_len += 6 * 4;
+ r_len += 6 * sizeof(real_t);
} break;
case Variant::PLANE: {
if (buf) {
Plane p = p_variant;
- encode_float(p.normal.x, &buf[0]);
- encode_float(p.normal.y, &buf[4]);
- encode_float(p.normal.z, &buf[8]);
- encode_float(p.d, &buf[12]);
+ encode_real(p.normal.x, &buf[0]);
+ encode_real(p.normal.y, &buf[sizeof(real_t)]);
+ encode_real(p.normal.z, &buf[sizeof(real_t) * 2]);
+ encode_real(p.d, &buf[sizeof(real_t) * 3]);
}
- r_len += 4 * 4;
+ r_len += 4 * sizeof(real_t);
} break;
- case Variant::QUAT: {
+ case Variant::QUATERNION: {
if (buf) {
- Quat q = p_variant;
- encode_float(q.x, &buf[0]);
- encode_float(q.y, &buf[4]);
- encode_float(q.z, &buf[8]);
- encode_float(q.w, &buf[12]);
+ Quaternion q = p_variant;
+ encode_real(q.x, &buf[0]);
+ encode_real(q.y, &buf[sizeof(real_t)]);
+ encode_real(q.z, &buf[sizeof(real_t) * 2]);
+ encode_real(q.w, &buf[sizeof(real_t) * 3]);
}
- r_len += 4 * 4;
+ r_len += 4 * sizeof(real_t);
} break;
case Variant::AABB: {
if (buf) {
AABB aabb = p_variant;
- encode_float(aabb.position.x, &buf[0]);
- encode_float(aabb.position.y, &buf[4]);
- encode_float(aabb.position.z, &buf[8]);
- encode_float(aabb.size.x, &buf[12]);
- encode_float(aabb.size.y, &buf[16]);
- encode_float(aabb.size.z, &buf[20]);
+ encode_real(aabb.position.x, &buf[0]);
+ encode_real(aabb.position.y, &buf[sizeof(real_t)]);
+ encode_real(aabb.position.z, &buf[sizeof(real_t) * 2]);
+ encode_real(aabb.size.x, &buf[sizeof(real_t) * 3]);
+ encode_real(aabb.size.y, &buf[sizeof(real_t) * 4]);
+ encode_real(aabb.size.z, &buf[sizeof(real_t) * 5]);
}
- r_len += 6 * 4;
+ r_len += 6 * sizeof(real_t);
} break;
case Variant::BASIS: {
@@ -1130,29 +1297,29 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
Basis val = p_variant;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
- memcpy(&buf[(i * 3 + j) * 4], &val.elements[i][j], sizeof(float));
+ memcpy(&buf[(i * 3 + j) * sizeof(real_t)], &val.elements[i][j], sizeof(real_t));
}
}
}
- r_len += 9 * 4;
+ r_len += 9 * sizeof(real_t);
} break;
- case Variant::TRANSFORM: {
+ case Variant::TRANSFORM3D: {
if (buf) {
- Transform val = p_variant;
+ Transform3D val = p_variant;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
- memcpy(&buf[(i * 3 + j) * 4], &val.basis.elements[i][j], sizeof(float));
+ memcpy(&buf[(i * 3 + j) * sizeof(real_t)], &val.basis.elements[i][j], sizeof(real_t));
}
}
- encode_float(val.origin.x, &buf[36]);
- encode_float(val.origin.y, &buf[40]);
- encode_float(val.origin.z, &buf[44]);
+ encode_real(val.origin.x, &buf[sizeof(real_t) * 9]);
+ encode_real(val.origin.y, &buf[sizeof(real_t) * 10]);
+ encode_real(val.origin.z, &buf[sizeof(real_t) * 11]);
}
- r_len += 12 * 4;
+ r_len += 12 * sizeof(real_t);
} break;
@@ -1166,7 +1333,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
encode_float(c.a, &buf[12]);
}
- r_len += 4 * 4;
+ r_len += 4 * 4; // Colors should always be in single-precision.
} break;
case Variant::RID: {
@@ -1191,8 +1358,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
obj->get_property_list(&props);
int pc = 0;
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ for (const PropertyInfo &E : props) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
pc++;
@@ -1205,18 +1372,16 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
r_len += 4;
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ for (const PropertyInfo &E : props) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- _encode_string(E->get().name, buf, r_len);
+ _encode_string(E.name, buf, r_len);
int len;
- Error err = encode_variant(obj->get(E->get().name), buf, len, p_full_objects);
- if (err) {
- return err;
- }
+ Error err = encode_variant(obj->get(E.name), buf, len, p_full_objects, p_depth + 1);
+ ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
r_len += len;
if (buf) {
@@ -1251,7 +1416,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
List<Variant> keys;
d.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+ for (const Variant &E : keys) {
/*
CharString utf8 = E->->utf8();
@@ -1266,15 +1431,17 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
r_len++; //pad
*/
int len;
- encode_variant(E->get(), buf, len, p_full_objects);
+ Error err = encode_variant(E, buf, len, p_full_objects, p_depth + 1);
+ ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
r_len += len;
if (buf) {
buf += len;
}
- Variant *v = d.getptr(E->get());
+ Variant *v = d.getptr(E);
ERR_FAIL_COND_V(!v, ERR_BUG);
- encode_variant(*v, buf, len, p_full_objects);
+ err = encode_variant(*v, buf, len, p_full_objects, p_depth + 1);
+ ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
r_len += len;
if (buf) {
@@ -1295,7 +1462,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
for (int i = 0; i < v.size(); i++) {
int len;
- encode_variant(v.get(i), buf, len, p_full_objects);
+ Error err = encode_variant(v.get(i), buf, len, p_full_objects, p_depth + 1);
+ ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
r_len += len;
if (buf) {
@@ -1350,7 +1518,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
int datasize = sizeof(int64_t);
if (buf) {
- encode_uint64(datalen, buf);
+ encode_uint32(datalen, buf);
buf += 4;
const int64_t *r = data.ptr();
for (int64_t i = 0; i < datalen; i++) {
@@ -1441,13 +1609,13 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
for (int i = 0; i < len; i++) {
Vector2 v = data.get(i);
- encode_float(v.x, &buf[0]);
- encode_float(v.y, &buf[4]);
- buf += 4 * 2;
+ encode_real(v.x, &buf[0]);
+ encode_real(v.y, &buf[sizeof(real_t)]);
+ buf += sizeof(real_t) * 2;
}
}
- r_len += 4 * 2 * len;
+ r_len += sizeof(real_t) * 2 * len;
} break;
case Variant::PACKED_VECTOR3_ARRAY: {
@@ -1465,14 +1633,14 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
for (int i = 0; i < len; i++) {
Vector3 v = data.get(i);
- encode_float(v.x, &buf[0]);
- encode_float(v.y, &buf[4]);
- encode_float(v.z, &buf[8]);
- buf += 4 * 3;
+ encode_real(v.x, &buf[0]);
+ encode_real(v.y, &buf[sizeof(real_t)]);
+ encode_real(v.z, &buf[sizeof(real_t) * 2]);
+ buf += sizeof(real_t) * 3;
}
}
- r_len += 4 * 3 * len;
+ r_len += sizeof(real_t) * 3 * len;
} break;
case Variant::PACKED_COLOR_ARRAY: {
@@ -1494,7 +1662,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
encode_float(c.g, &buf[4]);
encode_float(c.b, &buf[8]);
encode_float(c.a, &buf[12]);
- buf += 4 * 4;
+ buf += 4 * 4; // Colors should always be in single-precision.
}
}
diff --git a/core/io/marshalls.h b/core/io/marshalls.h
index cc0e9ba301..05804d5a46 100644
--- a/core/io/marshalls.h
+++ b/core/io/marshalls.h
@@ -31,10 +31,18 @@
#ifndef MARSHALLS_H
#define MARSHALLS_H
-#include "core/object/reference.h"
+#include "core/math/math_defs.h"
+#include "core/object/ref_counted.h"
#include "core/typedefs.h"
#include "core/variant/variant.h"
+// uintr_t is only for pairing with real_t, and we only need it in here.
+#ifdef REAL_T_IS_DOUBLE
+typedef uint64_t uintr_t;
+#else
+typedef uint32_t uintr_t;
+#endif
+
/**
* Miscellaneous helpers for marshalling data types, and encoding
* in an endian independent way
@@ -50,6 +58,12 @@ union MarshallDouble {
double d; ///< double
};
+// Behaves like one of the above, depending on compilation setting.
+union MarshallReal {
+ uintr_t i;
+ real_t r;
+};
+
static inline unsigned int encode_uint16(uint16_t p_uint, uint8_t *p_arr) {
for (int i = 0; i < 2; i++) {
*p_arr = p_uint & 0xFF;
@@ -96,6 +110,24 @@ static inline unsigned int encode_double(double p_double, uint8_t *p_arr) {
return sizeof(uint64_t);
}
+static inline unsigned int encode_uintr(uintr_t p_uint, uint8_t *p_arr) {
+ for (size_t i = 0; i < sizeof(uintr_t); i++) {
+ *p_arr = p_uint & 0xFF;
+ p_arr++;
+ p_uint >>= 8;
+ }
+
+ return sizeof(uintr_t);
+}
+
+static inline unsigned int encode_real(real_t p_real, uint8_t *p_arr) {
+ MarshallReal mr;
+ mr.r = p_real;
+ encode_uintr(mr.i, p_arr);
+
+ return sizeof(uintr_t);
+}
+
static inline int encode_cstring(const char *p_string, uint8_t *p_data) {
int len = 0;
@@ -165,8 +197,8 @@ static inline double decode_double(const uint8_t *p_arr) {
return md.d;
}
-class EncodedObjectAsID : public Reference {
- GDCLASS(EncodedObjectAsID, Reference);
+class EncodedObjectAsID : public RefCounted {
+ GDCLASS(EncodedObjectAsID, RefCounted);
ObjectID id;
@@ -181,6 +213,6 @@ public:
};
Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len = nullptr, bool p_allow_objects = false);
-Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects = false);
+Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects = false, int p_depth = 0);
#endif // MARSHALLS_H
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp
deleted file mode 100644
index fda4083804..0000000000
--- a/core/io/multiplayer_api.cpp
+++ /dev/null
@@ -1,1254 +0,0 @@
-/*************************************************************************/
-/* multiplayer_api.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "multiplayer_api.h"
-
-#include "core/debugger/engine_debugger.h"
-#include "core/io/marshalls.h"
-#include "scene/main/node.h"
-
-#include <stdint.h>
-
-#define NODE_ID_COMPRESSION_SHIFT 3
-#define NAME_ID_COMPRESSION_SHIFT 5
-#define BYTE_ONLY_OR_NO_ARGS_SHIFT 6
-
-#ifdef DEBUG_ENABLED
-#include "core/os/os.h"
-#endif
-
-_FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) {
- switch (mode) {
- case MultiplayerAPI::RPC_MODE_DISABLED: {
- // Do nothing.
- } break;
- case MultiplayerAPI::RPC_MODE_REMOTE: {
- // Do nothing. Remote cannot produce a local call.
- } break;
- case MultiplayerAPI::RPC_MODE_MASTERSYNC: {
- if (is_master) {
- r_skip_rpc = true; // I am the master, so skip remote call.
- }
- [[fallthrough]];
- }
- case MultiplayerAPI::RPC_MODE_REMOTESYNC:
- case MultiplayerAPI::RPC_MODE_PUPPETSYNC: {
- // Call it, sync always results in a local call.
- return true;
- } break;
- case MultiplayerAPI::RPC_MODE_MASTER: {
- if (is_master) {
- r_skip_rpc = true; // I am the master, so skip remote call.
- }
- return is_master;
- } break;
- case MultiplayerAPI::RPC_MODE_PUPPET: {
- return !is_master;
- } break;
- }
- return false;
-}
-
-_FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) {
- switch (mode) {
- case MultiplayerAPI::RPC_MODE_DISABLED: {
- return false;
- } break;
- case MultiplayerAPI::RPC_MODE_REMOTE:
- case MultiplayerAPI::RPC_MODE_REMOTESYNC: {
- return true;
- } break;
- case MultiplayerAPI::RPC_MODE_MASTERSYNC:
- case MultiplayerAPI::RPC_MODE_MASTER: {
- return p_node->is_network_master();
- } break;
- case MultiplayerAPI::RPC_MODE_PUPPETSYNC:
- case MultiplayerAPI::RPC_MODE_PUPPET: {
- return !p_node->is_network_master() && p_remote_id == p_node->get_network_master();
- } break;
- }
-
- return false;
-}
-
-void MultiplayerAPI::poll() {
- if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) {
- return;
- }
-
- network_peer->poll();
-
- if (!network_peer.is_valid()) { // It's possible that polling might have resulted in a disconnection, so check here.
- return;
- }
-
- while (network_peer->get_available_packet_count()) {
- int sender = network_peer->get_packet_peer();
- const uint8_t *packet;
- int len;
-
- Error err = network_peer->get_packet(&packet, len);
- if (err != OK) {
- ERR_PRINT("Error getting packet!");
- break; // Something is wrong!
- }
-
- rpc_sender_id = sender;
- _process_packet(sender, packet, len);
- rpc_sender_id = 0;
-
- if (!network_peer.is_valid()) {
- break; // It's also possible that a packet or RPC caused a disconnection, so also check here.
- }
- }
-}
-
-void MultiplayerAPI::clear() {
- connected_peers.clear();
- path_get_cache.clear();
- path_send_cache.clear();
- packet_cache.clear();
- last_send_cache_id = 1;
-}
-
-void MultiplayerAPI::set_root_node(Node *p_node) {
- root_node = p_node;
-}
-
-Node *MultiplayerAPI::get_root_node() {
- return root_node;
-}
-
-void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer) {
- if (p_peer == network_peer) {
- return; // Nothing to do
- }
-
- ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED,
- "Supplied NetworkedMultiplayerPeer must be connecting or connected.");
-
- if (network_peer.is_valid()) {
- network_peer->disconnect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer));
- network_peer->disconnect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer));
- network_peer->disconnect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server));
- network_peer->disconnect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed));
- network_peer->disconnect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected));
- clear();
- }
-
- network_peer = p_peer;
-
- if (network_peer.is_valid()) {
- network_peer->connect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer));
- network_peer->connect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer));
- network_peer->connect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server));
- network_peer->connect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed));
- network_peer->connect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected));
- }
-}
-
-Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const {
- return network_peer;
-}
-
-#ifdef DEBUG_ENABLED
-void _profile_node_data(const String &p_what, ObjectID p_id) {
- if (EngineDebugger::is_profiling("multiplayer")) {
- Array values;
- values.push_back("node");
- values.push_back(p_id);
- values.push_back(p_what);
- EngineDebugger::profiler_add_frame_data("multiplayer", values);
- }
-}
-
-void _profile_bandwidth_data(const String &p_inout, int p_size) {
- if (EngineDebugger::is_profiling("multiplayer")) {
- Array values;
- values.push_back("bandwidth");
- values.push_back(p_inout);
- values.push_back(OS::get_singleton()->get_ticks_msec());
- values.push_back(p_size);
- EngineDebugger::profiler_add_frame_data("multiplayer", values);
- }
-}
-#endif
-
-// Returns the packet size stripping the node path added when the node is not yet cached.
-int get_packet_len(uint32_t p_node_target, int p_packet_len) {
- if (p_node_target & 0x80000000) {
- int ofs = p_node_target & 0x7FFFFFFF;
- return p_packet_len - (p_packet_len - ofs);
- } else {
- return p_packet_len;
- }
-}
-
-void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(root_node == nullptr, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it.");
- ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small.");
-
-#ifdef DEBUG_ENABLED
- _profile_bandwidth_data("in", p_packet_len);
-#endif
-
- // Extract the `packet_type` from the LSB three bits:
- uint8_t packet_type = p_packet[0] & 7;
-
- switch (packet_type) {
- case NETWORK_COMMAND_SIMPLIFY_PATH: {
- _process_simplify_path(p_from, p_packet, p_packet_len);
- } break;
-
- case NETWORK_COMMAND_CONFIRM_PATH: {
- _process_confirm_path(p_from, p_packet, p_packet_len);
- } break;
-
- case NETWORK_COMMAND_REMOTE_CALL:
- case NETWORK_COMMAND_REMOTE_SET: {
- // Extract packet meta
- int packet_min_size = 1;
- int name_id_offset = 1;
- ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
- // Compute the meta size, which depends on the compression level.
- int node_id_compression = (p_packet[0] & 24) >> NODE_ID_COMPRESSION_SHIFT;
- int name_id_compression = (p_packet[0] & 32) >> NAME_ID_COMPRESSION_SHIFT;
-
- switch (node_id_compression) {
- case NETWORK_NODE_ID_COMPRESSION_8:
- packet_min_size += 1;
- name_id_offset += 1;
- break;
- case NETWORK_NODE_ID_COMPRESSION_16:
- packet_min_size += 2;
- name_id_offset += 2;
- break;
- case NETWORK_NODE_ID_COMPRESSION_32:
- packet_min_size += 4;
- name_id_offset += 4;
- break;
- default:
- ERR_FAIL_MSG("Was not possible to extract the node id compression mode.");
- }
- switch (name_id_compression) {
- case NETWORK_NAME_ID_COMPRESSION_8:
- packet_min_size += 1;
- break;
- case NETWORK_NAME_ID_COMPRESSION_16:
- packet_min_size += 2;
- break;
- default:
- ERR_FAIL_MSG("Was not possible to extract the name id compression mode.");
- }
- ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
-
- uint32_t node_target = 0;
- switch (node_id_compression) {
- case NETWORK_NODE_ID_COMPRESSION_8:
- node_target = p_packet[1];
- break;
- case NETWORK_NODE_ID_COMPRESSION_16:
- node_target = decode_uint16(p_packet + 1);
- break;
- case NETWORK_NODE_ID_COMPRESSION_32:
- node_target = decode_uint32(p_packet + 1);
- break;
- default:
- // Unreachable, checked before.
- CRASH_NOW();
- }
-
- Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len);
- ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found.");
-
- uint16_t name_id = 0;
- switch (name_id_compression) {
- case NETWORK_NAME_ID_COMPRESSION_8:
- name_id = p_packet[name_id_offset];
- break;
- case NETWORK_NAME_ID_COMPRESSION_16:
- name_id = decode_uint16(p_packet + name_id_offset);
- break;
- default:
- // Unreachable, checked before.
- CRASH_NOW();
- }
-
- const int packet_len = get_packet_len(node_target, p_packet_len);
- if (packet_type == NETWORK_COMMAND_REMOTE_CALL) {
- _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size);
-
- } else {
- _process_rset(node, name_id, p_from, p_packet, packet_len, packet_min_size);
- }
-
- } break;
-
- case NETWORK_COMMAND_RAW: {
- _process_raw(p_from, p_packet, p_packet_len);
- } break;
- }
-}
-
-Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) {
- Node *node = nullptr;
-
- if (p_node_target & 0x80000000) {
- // Use full path (not cached yet).
-
- int ofs = p_node_target & 0x7FFFFFFF;
-
- ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared.");
-
- String paths;
- paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs);
-
- NodePath np = paths;
-
- node = root_node->get_node(np);
-
- if (!node) {
- ERR_PRINT("Failed to get path from RPC: " + String(np) + ".");
- }
- } else {
- // Use cached path.
- int id = p_node_target;
-
- Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from);
- ERR_FAIL_COND_V_MSG(!E, nullptr, "Invalid packet received. Requests invalid peer cache.");
-
- Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id);
- ERR_FAIL_COND_V_MSG(!F, nullptr, "Invalid packet received. Unabled to find requested cached node.");
-
- PathGetCache::NodeInfo *ni = &F->get();
- // Do proper caching later.
-
- node = root_node->get_node(ni->path);
- if (!node) {
- ERR_PRINT("Failed to get cached path from RPC: " + String(ni->path) + ".");
- }
- }
- return node;
-}
-
-void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
- ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small.");
-
- // Check that remote can call the RPC on this node.
- StringName name = p_node->get_node_rpc_method(p_rpc_method_id);
- RPCMode rpc_mode = p_node->get_node_rpc_mode_by_id(p_rpc_method_id);
- if (name == StringName() && p_node->get_script_instance()) {
- name = p_node->get_script_instance()->get_rpc_method(p_rpc_method_id);
- rpc_mode = p_node->get_script_instance()->get_rpc_mode_by_id(p_rpc_method_id);
- }
- ERR_FAIL_COND(name == StringName());
-
- bool can_call = _can_call_mode(p_node, rpc_mode, p_from);
- ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
-
- int argc = 0;
- bool byte_only = false;
-
- const bool byte_only_or_no_args = ((p_packet[0] & 64) >> BYTE_ONLY_OR_NO_ARGS_SHIFT) == 1;
- if (byte_only_or_no_args) {
- if (p_offset < p_packet_len) {
- // This packet contains only bytes.
- argc = 1;
- byte_only = true;
- } else {
- // This rpc calls a method without parameters.
- }
- } else {
- // Normal variant, takes the argument count from the packet.
- ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
- argc = p_packet[p_offset];
- p_offset += 1;
- }
-
- Vector<Variant> args;
- Vector<const Variant *> argp;
- args.resize(argc);
- argp.resize(argc);
-
-#ifdef DEBUG_ENABLED
- _profile_node_data("in_rpc", p_node->get_instance_id());
-#endif
-
- if (byte_only) {
- Vector<uint8_t> pure_data;
- const int len = p_packet_len - p_offset;
- pure_data.resize(len);
- memcpy(pure_data.ptrw(), &p_packet[p_offset], len);
- args.write[0] = pure_data;
- argp.write[0] = &args[0];
- p_offset += len;
- } else {
- for (int i = 0; i < argc; i++) {
- ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
-
- int vlen;
- Error err = _decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen);
- ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument.");
-
- argp.write[i] = &args[i];
- p_offset += vlen;
- }
- }
-
- Callable::CallError ce;
-
- p_node->call(name, (const Variant **)argp.ptr(), argc, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(p_node, name, (const Variant **)argp.ptr(), argc, ce);
- error = "RPC - " + error;
- ERR_PRINT(error);
- }
-}
-
-void MultiplayerAPI::_process_rset(Node *p_node, const uint16_t p_rpc_property_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
- ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
-
- // Check that remote can call the RSET on this node.
- StringName name = p_node->get_node_rset_property(p_rpc_property_id);
- RPCMode rset_mode = p_node->get_node_rset_mode_by_id(p_rpc_property_id);
- if (name == StringName() && p_node->get_script_instance()) {
- name = p_node->get_script_instance()->get_rset_property(p_rpc_property_id);
- rset_mode = p_node->get_script_instance()->get_rset_mode_by_id(p_rpc_property_id);
- }
- ERR_FAIL_COND(name == StringName());
-
- bool can_call = _can_call_mode(p_node, rset_mode, p_from);
- ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
-
-#ifdef DEBUG_ENABLED
- _profile_node_data("in_rset", p_node->get_instance_id());
-#endif
-
- Variant value;
- Error err = _decode_and_decompress_variant(value, &p_packet[p_offset], p_packet_len - p_offset, nullptr);
-
- ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RSET value.");
-
- bool valid;
-
- p_node->set(name, value, &valid);
- if (!valid) {
- String error = "Error setting remote property '" + String(name) + "', not found in object of type " + p_node->get_class() + ".";
- ERR_PRINT(error);
- }
-}
-
-void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small.");
- int ofs = 1;
-
- String methods_md5;
- methods_md5.parse_utf8((const char *)(p_packet + ofs), 32);
- ofs += 33;
-
- int id = decode_uint32(&p_packet[ofs]);
- ofs += 4;
-
- String paths;
- paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs);
-
- NodePath path = paths;
-
- if (!path_get_cache.has(p_from)) {
- path_get_cache[p_from] = PathGetCache();
- }
-
- Node *node = root_node->get_node(path);
- ERR_FAIL_COND(node == nullptr);
- const bool valid_rpc_checksum = node->get_rpc_md5() == methods_md5;
- if (valid_rpc_checksum == false) {
- ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
- }
-
- PathGetCache::NodeInfo ni;
- ni.path = path;
-
- path_get_cache[p_from].nodes[id] = ni;
-
- // Encode path to send ack.
- CharString pname = String(path).utf8();
- int len = encode_cstring(pname.get_data(), nullptr);
-
- Vector<uint8_t> packet;
-
- packet.resize(1 + 1 + len);
- packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH;
- packet.write[1] = valid_rpc_checksum;
- encode_cstring(pname.get_data(), &packet.write[2]);
-
- network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
- network_peer->set_target_peer(p_from);
- network_peer->put_packet(packet.ptr(), packet.size());
-}
-
-void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small.");
-
- const bool valid_rpc_checksum = p_packet[1];
-
- String paths;
- paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2);
-
- NodePath path = paths;
-
- if (valid_rpc_checksum == false) {
- ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
- }
-
- PathSentCache *psc = path_send_cache.getptr(path);
- ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache.");
-
- Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from);
- ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path.");
- E->get() = true;
-}
-
-bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) {
- bool has_all_peers = true;
- List<int> peers_to_add; // If one is missing, take note to add it.
-
- for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
- if (p_target < 0 && E->get() == -p_target) {
- continue; // Continue, excluded.
- }
-
- if (p_target > 0 && E->get() != p_target) {
- continue; // Continue, not for this peer.
- }
-
- Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get());
-
- if (!F || !F->get()) {
- // Path was not cached, or was cached but is unconfirmed.
- if (!F) {
- // Not cached at all, take note.
- peers_to_add.push_back(E->get());
- }
-
- has_all_peers = false;
- }
- }
-
- if (peers_to_add.size() > 0) {
- // Those that need to be added, send a message for this.
-
- // Encode function name.
- const CharString path = String(p_path).utf8();
- const int path_len = encode_cstring(path.get_data(), nullptr);
-
- // Extract MD5 from rpc methods list.
- const String methods_md5 = p_node->get_rpc_md5();
- const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder.
-
- Vector<uint8_t> packet;
- packet.resize(1 + 4 + path_len + methods_md5_len);
- int ofs = 0;
-
- packet.write[ofs] = NETWORK_COMMAND_SIMPLIFY_PATH;
- ofs += 1;
-
- ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]);
-
- ofs += encode_uint32(psc->id, &packet.write[ofs]);
-
- ofs += encode_cstring(path.get_data(), &packet.write[ofs]);
-
- for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) {
- network_peer->set_target_peer(E->get()); // To all of you.
- network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
- network_peer->put_packet(packet.ptr(), packet.size());
-
- psc->confirmed_peers.insert(E->get(), false); // Insert into confirmed, but as false since it was not confirmed.
- }
- }
-
- return has_all_peers;
-}
-
-// The variant is compressed and encoded; The first byte contains all the meta
-// information and the format is:
-// - The first LSB 5 bits are used for the variant type.
-// - The next two bits are used to store the encoding mode.
-// - The most significant is used to store the boolean value.
-#define VARIANT_META_TYPE_MASK 0x1F
-#define VARIANT_META_EMODE_MASK 0x60
-#define VARIANT_META_BOOL_MASK 0x80
-#define ENCODE_8 0 << 5
-#define ENCODE_16 1 << 5
-#define ENCODE_32 2 << 5
-#define ENCODE_64 3 << 5
-Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) {
- // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31
- CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK);
-
- uint8_t *buf = r_buffer;
- r_len = 0;
- uint8_t encode_mode = 0;
-
- switch (p_variant.get_type()) {
- case Variant::BOOL: {
- if (buf) {
- // We still have 1 free bit in the meta, so let's use it.
- buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0;
- buf[0] |= encode_mode | p_variant.get_type();
- }
- r_len += 1;
- } break;
- case Variant::INT: {
- if (buf) {
- // Reserve the first byte for the meta.
- buf += 1;
- }
- r_len += 1;
- int64_t val = p_variant;
- if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) {
- // Use 8 bit
- encode_mode = ENCODE_8;
- if (buf) {
- buf[0] = val;
- }
- r_len += 1;
- } else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) {
- // Use 16 bit
- encode_mode = ENCODE_16;
- if (buf) {
- encode_uint16(val, buf);
- }
- r_len += 2;
- } else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) {
- // Use 32 bit
- encode_mode = ENCODE_32;
- if (buf) {
- encode_uint32(val, buf);
- }
- r_len += 4;
- } else {
- // Use 64 bit
- encode_mode = ENCODE_64;
- if (buf) {
- encode_uint64(val, buf);
- }
- r_len += 8;
- }
- // Store the meta
- if (buf) {
- buf -= 1;
- buf[0] = encode_mode | p_variant.get_type();
- }
- } break;
- default:
- // Any other case is not yet compressed.
- Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding);
- if (err != OK) {
- return err;
- }
- if (r_buffer) {
- // The first byte is not used by the marshalling, so store the type
- // so we know how to decompress and decode this variant.
- r_buffer[0] = p_variant.get_type();
- }
- }
-
- return OK;
-}
-
-Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) {
- const uint8_t *buf = p_buffer;
- int len = p_len;
-
- ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
- uint8_t type = buf[0] & VARIANT_META_TYPE_MASK;
- uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK;
-
- ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
-
- switch (type) {
- case Variant::BOOL: {
- bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0;
- r_variant = val;
- if (r_len) {
- *r_len = 1;
- }
- } break;
- case Variant::INT: {
- buf += 1;
- len -= 1;
- if (r_len) {
- *r_len = 1;
- }
- if (encode_mode == ENCODE_8) {
- // 8 bits.
- ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
- int8_t val = buf[0];
- r_variant = val;
- if (r_len) {
- (*r_len) += 1;
- }
- } else if (encode_mode == ENCODE_16) {
- // 16 bits.
- ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA);
- int16_t val = decode_uint16(buf);
- r_variant = val;
- if (r_len) {
- (*r_len) += 2;
- }
- } else if (encode_mode == ENCODE_32) {
- // 32 bits.
- ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
- int32_t val = decode_uint32(buf);
- r_variant = val;
- if (r_len) {
- (*r_len) += 4;
- }
- } else {
- // 64 bits.
- ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
- int64_t val = decode_uint64(buf);
- r_variant = val;
- if (r_len) {
- (*r_len) += 8;
- }
- }
- } break;
- default:
- Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding);
- if (err != OK) {
- return err;
- }
- }
-
- return OK;
-}
-
-void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount) {
- ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree.");
-
- ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree.");
-
- ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to remote call/set when networking is disconnected.");
-
- ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments >255.");
-
- if (p_to != 0 && !connected_peers.has(ABS(p_to))) {
- ERR_FAIL_COND_MSG(p_to == network_peer->get_unique_id(), "Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id()) + ".");
-
- ERR_FAIL_MSG("Attempt to remote call unexisting ID: " + itos(p_to) + ".");
- }
-
- NodePath from_path = (root_node->get_path()).rel_path_to(p_from->get_path());
- ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!");
-
- // See if the path is cached.
- PathSentCache *psc = path_send_cache.getptr(from_path);
- if (!psc) {
- // Path is not cached, create.
- path_send_cache[from_path] = PathSentCache();
- psc = path_send_cache.getptr(from_path);
- psc->id = last_send_cache_id++;
- }
-
- // See if all peers have cached path (if so, call can be fast).
- const bool has_all_peers = _send_confirm_path(p_from, from_path, psc, p_to);
-
- // Create base packet, lots of hardcode because it must be tight.
-
- int ofs = 0;
-
-#define MAKE_ROOM(m_amount) \
- if (packet_cache.size() < m_amount) \
- packet_cache.resize(m_amount);
-
- // Encode meta.
- // The meta is composed by a single byte that contains (starting from the least significant bit):
- // - `NetworkCommands` in the first three bits.
- // - `NetworkNodeIdCompression` in the next 2 bits.
- // - `NetworkNameIdCompression` in the next 1 bit.
- // - `byte_only_or_no_args` in the next 1 bit.
- // - So we still have the last bit free!
- uint8_t command_type = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL;
- uint8_t node_id_compression = UINT8_MAX;
- uint8_t name_id_compression = UINT8_MAX;
- bool byte_only_or_no_args = false;
-
- MAKE_ROOM(1);
- // The meta is composed along the way, so just set 0 for now.
- packet_cache.write[0] = 0;
- ofs += 1;
-
- // Encode Node ID.
- if (has_all_peers) {
- // Compress the node ID only if all the target peers already know it.
- if (psc->id >= 0 && psc->id <= 255) {
- // We can encode the id in 1 byte
- node_id_compression = NETWORK_NODE_ID_COMPRESSION_8;
- MAKE_ROOM(ofs + 1);
- packet_cache.write[ofs] = static_cast<uint8_t>(psc->id);
- ofs += 1;
- } else if (psc->id >= 0 && psc->id <= 65535) {
- // We can encode the id in 2 bytes
- node_id_compression = NETWORK_NODE_ID_COMPRESSION_16;
- MAKE_ROOM(ofs + 2);
- encode_uint16(static_cast<uint16_t>(psc->id), &(packet_cache.write[ofs]));
- ofs += 2;
- } else {
- // Too big, let's use 4 bytes.
- node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
- MAKE_ROOM(ofs + 4);
- encode_uint32(psc->id, &(packet_cache.write[ofs]));
- ofs += 4;
- }
- } else {
- // The targets don't know the node yet, so we need to use 32 bits int.
- node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
- MAKE_ROOM(ofs + 4);
- encode_uint32(psc->id, &(packet_cache.write[ofs]));
- ofs += 4;
- }
-
- if (p_set) {
- // Take the rpc property ID
- uint16_t property_id = p_from->get_node_rset_property_id(p_name);
- if (property_id == UINT16_MAX && p_from->get_script_instance()) {
- property_id = p_from->get_script_instance()->get_rset_property_id(p_name);
- }
- ERR_FAIL_COND_MSG(property_id == UINT16_MAX, "Unable to take the `property_id` for the property:" + p_name + ". This can only happen if this property is not marked as `remote`.");
-
- if (property_id <= UINT8_MAX) {
- // The ID fits in 1 byte
- name_id_compression = NETWORK_NAME_ID_COMPRESSION_8;
- MAKE_ROOM(ofs + 1);
- packet_cache.write[ofs] = static_cast<uint8_t>(property_id);
- ofs += 1;
- } else {
- // The ID is larger, let's use 2 bytes
- name_id_compression = NETWORK_NAME_ID_COMPRESSION_16;
- MAKE_ROOM(ofs + 2);
- encode_uint16(property_id, &(packet_cache.write[ofs]));
- ofs += 2;
- }
-
- // Set argument.
- int len(0);
- Error err = _encode_and_compress_variant(*p_arg[0], nullptr, len);
- ERR_FAIL_COND_MSG(err != OK, "Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!");
- MAKE_ROOM(ofs + len);
- _encode_and_compress_variant(*p_arg[0], &(packet_cache.write[ofs]), len);
- ofs += len;
-
- } else {
- // Take the rpc method ID
- uint16_t method_id = p_from->get_node_rpc_method_id(p_name);
- if (method_id == UINT16_MAX && p_from->get_script_instance()) {
- method_id = p_from->get_script_instance()->get_rpc_method_id(p_name);
- }
- ERR_FAIL_COND_MSG(method_id == UINT16_MAX,
- vformat("Unable to take the `method_id` for the function \"%s\" at path: \"%s\". This happens when the method is not marked as `remote`.", p_name, p_from->get_path()));
-
- if (method_id <= UINT8_MAX) {
- // The ID fits in 1 byte
- name_id_compression = NETWORK_NAME_ID_COMPRESSION_8;
- MAKE_ROOM(ofs + 1);
- packet_cache.write[ofs] = static_cast<uint8_t>(method_id);
- ofs += 1;
- } else {
- // The ID is larger, let's use 2 bytes
- name_id_compression = NETWORK_NAME_ID_COMPRESSION_16;
- MAKE_ROOM(ofs + 2);
- encode_uint16(method_id, &(packet_cache.write[ofs]));
- ofs += 2;
- }
-
- if (p_argcount == 0) {
- byte_only_or_no_args = true;
- } else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) {
- byte_only_or_no_args = true;
- // Special optimization when only the byte vector is sent.
- const Vector<uint8_t> data = *p_arg[0];
- MAKE_ROOM(ofs + data.size());
- memcpy(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size());
- ofs += data.size();
- } else {
- // Arguments
- MAKE_ROOM(ofs + 1);
- packet_cache.write[ofs] = p_argcount;
- ofs += 1;
- for (int i = 0; i < p_argcount; i++) {
- int len(0);
- Error err = _encode_and_compress_variant(*p_arg[i], nullptr, len);
- ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
- MAKE_ROOM(ofs + len);
- _encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
- ofs += len;
- }
- }
- }
-
- ERR_FAIL_COND(command_type > 7);
- ERR_FAIL_COND(node_id_compression > 3);
- ERR_FAIL_COND(name_id_compression > 1);
-
- // We can now set the meta
- packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + ((byte_only_or_no_args ? 1 : 0) << BYTE_ONLY_OR_NO_ARGS_SHIFT);
-
-#ifdef DEBUG_ENABLED
- _profile_bandwidth_data("out", ofs);
-#endif
-
- // Take chance and set transfer mode, since all send methods will use it.
- network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
-
- if (has_all_peers) {
- // They all have verified paths, so send fast.
- network_peer->set_target_peer(p_to); // To all of you.
- network_peer->put_packet(packet_cache.ptr(), ofs); // A message with love.
- } else {
- // Unreachable because the node ID is never compressed if the peers doesn't know it.
- CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32);
-
- // Not all verified path, so send one by one.
-
- // Append path at the end, since we will need it for some packets.
- CharString pname = String(from_path).utf8();
- int path_len = encode_cstring(pname.get_data(), nullptr);
- MAKE_ROOM(ofs + path_len);
- encode_cstring(pname.get_data(), &(packet_cache.write[ofs]));
-
- for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
- if (p_to < 0 && E->get() == -p_to) {
- continue; // Continue, excluded.
- }
-
- if (p_to > 0 && E->get() != p_to) {
- continue; // Continue, not for this peer.
- }
-
- Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get());
- ERR_CONTINUE(!F); // Should never happen.
-
- network_peer->set_target_peer(E->get()); // To this one specifically.
-
- if (F->get()) {
- // This one confirmed path, so use id.
- encode_uint32(psc->id, &(packet_cache.write[1]));
- network_peer->put_packet(packet_cache.ptr(), ofs);
- } else {
- // This one did not confirm path yet, so use entire path (sorry!).
- encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag.
- network_peer->put_packet(packet_cache.ptr(), ofs + path_len);
- }
- }
- }
-}
-
-void MultiplayerAPI::_add_peer(int p_id) {
- connected_peers.insert(p_id);
- path_get_cache.insert(p_id, PathGetCache());
- emit_signal("network_peer_connected", p_id);
-}
-
-void MultiplayerAPI::_del_peer(int p_id) {
- connected_peers.erase(p_id);
- // Cleanup get cache.
- path_get_cache.erase(p_id);
- // Cleanup sent cache.
- // Some refactoring is needed to make this faster and do paths GC.
- List<NodePath> keys;
- path_send_cache.get_key_list(&keys);
- for (List<NodePath>::Element *E = keys.front(); E; E = E->next()) {
- PathSentCache *psc = path_send_cache.getptr(E->get());
- psc->confirmed_peers.erase(p_id);
- }
- emit_signal("network_peer_disconnected", p_id);
-}
-
-void MultiplayerAPI::_connected_to_server() {
- emit_signal("connected_to_server");
-}
-
-void MultiplayerAPI::_connection_failed() {
- emit_signal("connection_failed");
-}
-
-void MultiplayerAPI::_server_disconnected() {
- emit_signal("server_disconnected");
-}
-
-void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) {
- ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to call an RPC while no network peer is active.");
- ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree.");
- ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected.");
-
- int node_id = network_peer->get_unique_id();
- bool skip_rpc = node_id == p_peer_id;
- bool call_local_native = false;
- bool call_local_script = false;
- bool is_master = p_node->is_network_master();
-
- if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
- // Check that send mode can use local call.
-
- RPCMode rpc_mode = p_node->get_node_rpc_mode(p_method);
- call_local_native = _should_call_local(rpc_mode, is_master, skip_rpc);
-
- if (call_local_native) {
- // Done below.
- } else if (p_node->get_script_instance()) {
- // Attempt with script.
- rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method);
- call_local_script = _should_call_local(rpc_mode, is_master, skip_rpc);
- }
- }
-
- if (!skip_rpc) {
-#ifdef DEBUG_ENABLED
- _profile_node_data("out_rpc", p_node->get_instance_id());
-#endif
-
- _send_rpc(p_node, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount);
- }
-
- if (call_local_native) {
- int temp_id = rpc_sender_id;
- rpc_sender_id = get_network_unique_id();
- Callable::CallError ce;
- p_node->call(p_method, p_arg, p_argcount, ce);
- rpc_sender_id = temp_id;
- if (ce.error != Callable::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
- error = "rpc() aborted in local call: - " + error + ".";
- ERR_PRINT(error);
- return;
- }
- }
-
- if (call_local_script) {
- int temp_id = rpc_sender_id;
- rpc_sender_id = get_network_unique_id();
- Callable::CallError ce;
- ce.error = Callable::CallError::CALL_OK;
- p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce);
- rpc_sender_id = temp_id;
- if (ce.error != Callable::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
- error = "rpc() aborted in script local call: - " + error + ".";
- ERR_PRINT(error);
- return;
- }
- }
-
- ERR_FAIL_COND_MSG(skip_rpc && !(call_local_native || call_local_script), "RPC '" + p_method + "' on yourself is not allowed by selected mode.");
-}
-
-void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) {
- ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to RSET while no network peer is active.");
- ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to RSET on a node which is not inside SceneTree.");
- ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to send an RSET via a network peer which is not connected.");
-
- int node_id = network_peer->get_unique_id();
- bool is_master = p_node->is_network_master();
- bool skip_rset = node_id == p_peer_id;
- bool set_local = false;
-
- if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
- // Check that send mode can use local call.
- RPCMode rpc_mode = p_node->get_node_rset_mode(p_property);
- set_local = _should_call_local(rpc_mode, is_master, skip_rset);
-
- if (set_local) {
- bool valid;
- int temp_id = rpc_sender_id;
-
- rpc_sender_id = get_network_unique_id();
- p_node->set(p_property, p_value, &valid);
- rpc_sender_id = temp_id;
-
- if (!valid) {
- String error = "rset() aborted in local set, property not found: - " + String(p_property) + ".";
- ERR_PRINT(error);
- return;
- }
- } else if (p_node->get_script_instance()) {
- // Attempt with script.
- rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property);
-
- set_local = _should_call_local(rpc_mode, is_master, skip_rset);
-
- if (set_local) {
- int temp_id = rpc_sender_id;
-
- rpc_sender_id = get_network_unique_id();
- bool valid = p_node->get_script_instance()->set(p_property, p_value);
- rpc_sender_id = temp_id;
-
- if (!valid) {
- String error = "rset() aborted in local script set, property not found: - " + String(p_property) + ".";
- ERR_PRINT(error);
- return;
- }
- }
- }
- }
-
- if (skip_rset) {
- ERR_FAIL_COND_MSG(!set_local, "RSET for '" + p_property + "' on yourself is not allowed by selected mode.");
- return;
- }
-
-#ifdef DEBUG_ENABLED
- _profile_node_data("out_rset", p_node->get_instance_id());
-#endif
-
- const Variant *vptr = &p_value;
-
- _send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1);
-}
-
-Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) {
- ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet.");
- ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no network peer is active.");
- ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected.");
-
- MAKE_ROOM(p_data.size() + 1);
- const uint8_t *r = p_data.ptr();
- packet_cache.write[0] = NETWORK_COMMAND_RAW;
- memcpy(&packet_cache.write[1], &r[0], p_data.size());
-
- network_peer->set_target_peer(p_to);
- network_peer->set_transfer_mode(p_mode);
-
- return network_peer->put_packet(packet_cache.ptr(), p_data.size() + 1);
-}
-
-void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small.");
-
- Vector<uint8_t> out;
- int len = p_packet_len - 1;
- out.resize(len);
- {
- uint8_t *w = out.ptrw();
- memcpy(&w[0], &p_packet[1], len);
- }
- emit_signal("network_peer_packet", p_from, out);
-}
-
-int MultiplayerAPI::get_network_unique_id() const {
- ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), 0, "No network peer is assigned. Unable to get unique network ID.");
- return network_peer->get_unique_id();
-}
-
-bool MultiplayerAPI::is_network_server() const {
- // XXX Maybe fail silently? Maybe should actually return true to make development of both local and online multiplayer easier?
- ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. I can't be a server.");
- return network_peer->is_server();
-}
-
-void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) {
- ERR_FAIL_COND_MSG(!network_peer.is_valid(), "No network peer is assigned. Unable to set 'refuse_new_connections'.");
- network_peer->set_refuse_new_connections(p_refuse);
-}
-
-bool MultiplayerAPI::is_refusing_new_network_connections() const {
- ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. Unable to get 'refuse_new_connections'.");
- return network_peer->is_refusing_new_connections();
-}
-
-Vector<int> MultiplayerAPI::get_network_connected_peers() const {
- ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), Vector<int>(), "No network peer is assigned. Assume no peers are connected.");
-
- Vector<int> ret;
- for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
- ret.push_back(E->get());
- }
-
- return ret;
-}
-
-void MultiplayerAPI::set_allow_object_decoding(bool p_enable) {
- allow_object_decoding = p_enable;
-}
-
-bool MultiplayerAPI::is_object_decoding_allowed() const {
- return allow_object_decoding;
-}
-
-void MultiplayerAPI::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
- ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node);
- ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE));
- ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer);
- ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer);
- ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id);
- ClassDB::bind_method(D_METHOD("is_network_server"), &MultiplayerAPI::is_network_server);
- ClassDB::bind_method(D_METHOD("get_rpc_sender_id"), &MultiplayerAPI::get_rpc_sender_id);
- ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &MultiplayerAPI::set_network_peer);
- ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll);
- ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear);
-
- ClassDB::bind_method(D_METHOD("get_network_connected_peers"), &MultiplayerAPI::get_network_connected_peers);
- ClassDB::bind_method(D_METHOD("set_refuse_new_network_connections", "refuse"), &MultiplayerAPI::set_refuse_new_network_connections);
- ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections);
- ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding);
- ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_root_node", "get_root_node");
- ADD_PROPERTY_DEFAULT("refuse_new_network_connections", false);
-
- ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id")));
- ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id")));
- ADD_SIGNAL(MethodInfo("network_peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet")));
- ADD_SIGNAL(MethodInfo("connected_to_server"));
- ADD_SIGNAL(MethodInfo("connection_failed"));
- ADD_SIGNAL(MethodInfo("server_disconnected"));
-
- BIND_ENUM_CONSTANT(RPC_MODE_DISABLED);
- BIND_ENUM_CONSTANT(RPC_MODE_REMOTE);
- BIND_ENUM_CONSTANT(RPC_MODE_MASTER);
- BIND_ENUM_CONSTANT(RPC_MODE_PUPPET);
- BIND_ENUM_CONSTANT(RPC_MODE_REMOTESYNC);
- BIND_ENUM_CONSTANT(RPC_MODE_MASTERSYNC);
- BIND_ENUM_CONSTANT(RPC_MODE_PUPPETSYNC);
-}
-
-MultiplayerAPI::MultiplayerAPI() {
- clear();
-}
-
-MultiplayerAPI::~MultiplayerAPI() {
- clear();
-}
diff --git a/core/io/net_socket.h b/core/io/net_socket.h
index 98ff9562d9..fd7d50c704 100644
--- a/core/io/net_socket.h
+++ b/core/io/net_socket.h
@@ -32,9 +32,9 @@
#define NET_SOCKET_H
#include "core/io/ip.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
-class NetSocket : public Reference {
+class NetSocket : public RefCounted {
protected:
static NetSocket *(*_create)();
diff --git a/core/io/packed_data_container.cpp b/core/io/packed_data_container.cpp
index 52169987fd..4a76f0191d 100644
--- a/core/io/packed_data_container.cpp
+++ b/core/io/packed_data_container.cpp
@@ -227,10 +227,10 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd
case Variant::VECTOR3:
case Variant::TRANSFORM2D:
case Variant::PLANE:
- case Variant::QUAT:
+ case Variant::QUATERNION:
case Variant::AABB:
case Variant::BASIS:
- case Variant::TRANSFORM:
+ case Variant::TRANSFORM3D:
case Variant::PACKED_BYTE_ARRAY:
case Variant::PACKED_INT32_ARRAY:
case Variant::PACKED_INT64_ARRAY:
@@ -268,21 +268,21 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd
d.get_key_list(&keys);
List<DictKey> sortk;
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+ for (const Variant &key : keys) {
DictKey dk;
- dk.hash = E->get().hash();
- dk.key = E->get();
+ dk.hash = key.hash();
+ dk.key = key;
sortk.push_back(dk);
}
sortk.sort();
int idx = 0;
- for (List<DictKey>::Element *E = sortk.front(); E; E = E->next()) {
- encode_uint32(E->get().hash, &tmpdata.write[pos + 8 + idx * 12 + 0]);
- uint32_t ofs = _pack(E->get().key, tmpdata, string_cache);
+ for (const DictKey &E : sortk) {
+ encode_uint32(E.hash, &tmpdata.write[pos + 8 + idx * 12 + 0]);
+ uint32_t ofs = _pack(E.key, tmpdata, string_cache);
encode_uint32(ofs, &tmpdata.write[pos + 8 + idx * 12 + 4]);
- ofs = _pack(d[E->get().key], tmpdata, string_cache);
+ ofs = _pack(d[E.key], tmpdata, string_cache);
encode_uint32(ofs, &tmpdata.write[pos + 8 + idx * 12 + 8]);
idx++;
}
diff --git a/core/io/packed_data_container.h b/core/io/packed_data_container.h
index 7791e21bb3..40772bb2bf 100644
--- a/core/io/packed_data_container.h
+++ b/core/io/packed_data_container.h
@@ -80,8 +80,8 @@ public:
PackedDataContainer() {}
};
-class PackedDataContainerRef : public Reference {
- GDCLASS(PackedDataContainerRef, Reference);
+class PackedDataContainerRef : public RefCounted {
+ GDCLASS(PackedDataContainerRef, RefCounted);
friend class PackedDataContainer;
uint32_t offset = 0;
diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp
index 318fd10243..8da44fd290 100644
--- a/core/io/packet_peer.cpp
+++ b/core/io/packet_peer.cpp
@@ -161,7 +161,7 @@ void PacketPeerStream::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "input_buffer_max_size"), "set_input_buffer_max_size", "get_input_buffer_max_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "output_buffer_max_size"), "set_output_buffer_max_size", "get_output_buffer_max_size");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream_peer", PROPERTY_HINT_RESOURCE_TYPE, "StreamPeer", 0), "set_stream_peer", "get_stream_peer");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream_peer", PROPERTY_HINT_RESOURCE_TYPE, "StreamPeer", PROPERTY_USAGE_NONE), "set_stream_peer", "get_stream_peer");
}
Error PacketPeerStream::_poll_buffer() const {
diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h
index 9e03c44750..9a345af3d0 100644
--- a/core/io/packet_peer.h
+++ b/core/io/packet_peer.h
@@ -35,8 +35,8 @@
#include "core/object/class_db.h"
#include "core/templates/ring_buffer.h"
-class PacketPeer : public Reference {
- GDCLASS(PacketPeer, Reference);
+class PacketPeer : public RefCounted {
+ GDCLASS(PacketPeer, RefCounted);
Variant _bnd_get_var(bool p_allow_objects = false);
diff --git a/core/io/packet_peer_dtls.cpp b/core/io/packet_peer_dtls.cpp
index bac98e20e7..a6d220622b 100644
--- a/core/io/packet_peer_dtls.cpp
+++ b/core/io/packet_peer_dtls.cpp
@@ -30,7 +30,7 @@
#include "packet_peer_dtls.h"
#include "core/config/project_settings.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
PacketPeerDTLS *(*PacketPeerDTLS::_create)() = nullptr;
bool PacketPeerDTLS::available = false;
diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp
index cadb02b5dd..806a95398f 100644
--- a/core/io/pck_packer.cpp
+++ b/core/io/pck_packer.cpp
@@ -31,9 +31,9 @@
#include "pck_packer.h"
#include "core/crypto/crypto_core.h"
+#include "core/io/file_access.h"
#include "core/io/file_access_encrypted.h"
#include "core/io/file_access_pack.h" // PACK_HEADER_MAGIC, PACK_FORMAT_VERSION
-#include "core/os/file_access.h"
#include "core/version.h"
static int _get_pad(int p_alignment, int p_n) {
diff --git a/core/io/pck_packer.h b/core/io/pck_packer.h
index dec8f8748d..3d2ce8f240 100644
--- a/core/io/pck_packer.h
+++ b/core/io/pck_packer.h
@@ -31,12 +31,12 @@
#ifndef PCK_PACKER_H
#define PCK_PACKER_H
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
class FileAccess;
-class PCKPacker : public Reference {
- GDCLASS(PCKPacker, Reference);
+class PCKPacker : public RefCounted {
+ GDCLASS(PCKPacker, RefCounted);
FileAccess *file = nullptr;
int alignment = 0;
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index d46e9edafa..87b4d7195d 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -31,9 +31,10 @@
#include "resource.h"
#include "core/core_string_names.h"
+#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
+#include "core/math/math_funcs.h"
#include "core/object/script_language.h"
-#include "core/os/file_access.h"
#include "core/os/os.h"
#include "scene/main/node.h" //only so casting works
@@ -94,12 +95,43 @@ String Resource::get_path() const {
return path_cache;
}
-void Resource::set_subindex(int p_sub_index) {
- subindex = p_sub_index;
+String Resource::generate_scene_unique_id() {
+ // Generate a unique enough hash, but still user-readable.
+ // If it's not unique it does not matter because the saver will try again.
+ OS::Date date = OS::get_singleton()->get_date();
+ OS::Time time = OS::get_singleton()->get_time();
+ uint32_t hash = hash_djb2_one_32(OS::get_singleton()->get_ticks_usec());
+ hash = hash_djb2_one_32(date.year, hash);
+ hash = hash_djb2_one_32(date.month, hash);
+ hash = hash_djb2_one_32(date.day, hash);
+ hash = hash_djb2_one_32(time.hour, hash);
+ hash = hash_djb2_one_32(time.minute, hash);
+ hash = hash_djb2_one_32(time.second, hash);
+ hash = hash_djb2_one_32(Math::rand(), hash);
+
+ static constexpr uint32_t characters = 5;
+ static constexpr uint32_t char_count = ('z' - 'a');
+ static constexpr uint32_t base = char_count + ('9' - '0');
+ String id;
+ for (uint32_t i = 0; i < characters; i++) {
+ uint32_t c = hash % base;
+ if (c < char_count) {
+ id += String::chr('a' + c);
+ } else {
+ id += String::chr('0' + (c - char_count));
+ }
+ hash /= base;
+ }
+
+ return id;
}
-int Resource::get_subindex() const {
- return subindex;
+void Resource::set_scene_unique_id(const String &p_id) {
+ scene_unique_id = p_id;
+}
+
+String Resource::get_scene_unique_id() const {
+ return scene_unique_id;
}
void Resource::set_name(const String &p_name) {
@@ -133,15 +165,15 @@ Error Resource::copy_from(const Ref<Resource> &p_resource) {
List<PropertyInfo> pi;
p_resource->get_property_list(&pi);
- for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ for (const PropertyInfo &E : pi) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- if (E->get().name == "resource_path") {
+ if (E.name == "resource_path") {
continue; //do not change path
}
- set(E->get().name, p_resource->get(E->get().name));
+ set(E.name, p_resource->get(E.name));
}
return OK;
}
@@ -164,16 +196,16 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Res
List<PropertyInfo> plist;
get_property_list(&plist);
- Ref<Resource> r = Object::cast_to<Resource>(ClassDB::instance(get_class()));
+ Ref<Resource> r = Object::cast_to<Resource>(ClassDB::instantiate(get_class()));
ERR_FAIL_COND_V(r.is_null(), Ref<Resource>());
r->local_scene = p_for_scene;
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ for (const PropertyInfo &E : plist) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- Variant p = get(E->get().name);
+ Variant p = get(E.name);
if (p.get_type() == Variant::OBJECT) {
RES sr = p;
if (sr.is_valid()) {
@@ -189,7 +221,7 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Res
}
}
- r->set(E->get().name, p);
+ r->set(E.name, p);
}
return r;
@@ -201,11 +233,11 @@ void Resource::configure_for_local_scene(Node *p_for_scene, Map<Ref<Resource>, R
local_scene = p_for_scene;
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ for (const PropertyInfo &E : plist) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- Variant p = get(E->get().name);
+ Variant p = get(E.name);
if (p.get_type() == Variant::OBJECT) {
RES sr = p;
if (sr.is_valid()) {
@@ -224,24 +256,24 @@ Ref<Resource> Resource::duplicate(bool p_subresources) const {
List<PropertyInfo> plist;
get_property_list(&plist);
- Ref<Resource> r = (Resource *)ClassDB::instance(get_class());
+ Ref<Resource> r = (Resource *)ClassDB::instantiate(get_class());
ERR_FAIL_COND_V(r.is_null(), Ref<Resource>());
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ for (const PropertyInfo &E : plist) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- Variant p = get(E->get().name);
+ Variant p = get(E.name);
if ((p.get_type() == Variant::DICTIONARY || p.get_type() == Variant::ARRAY)) {
- r->set(E->get().name, p.duplicate(p_subresources));
- } else if (p.get_type() == Variant::OBJECT && (p_subresources || (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE))) {
+ r->set(E.name, p.duplicate(p_subresources));
+ } else if (p.get_type() == Variant::OBJECT && (p_subresources || (E.usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE))) {
RES sr = p;
if (sr.is_valid()) {
- r->set(E->get().name, sr->duplicate(p_subresources));
+ r->set(E.name, sr->duplicate(p_subresources));
}
} else {
- r->set(E->get().name, p);
+ r->set(E.name, p);
}
}
@@ -285,9 +317,9 @@ uint32_t Resource::hash_edited_version() const {
List<PropertyInfo> plist;
get_property_list(&plist);
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- if (E->get().usage & PROPERTY_USAGE_STORAGE && E->get().type == Variant::OBJECT && E->get().hint == PROPERTY_HINT_RESOURCE_TYPE) {
- RES res = get(E->get().name);
+ for (const PropertyInfo &E : plist) {
+ if (E.usage & PROPERTY_USAGE_STORAGE && E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ RES res = get(E.name);
if (res.is_valid()) {
hash = hash_djb2_one_32(res->hash_edited_version(), hash);
}
@@ -320,9 +352,8 @@ Node *Resource::get_local_scene() const {
}
void Resource::setup_local_to_scene() {
- if (get_script_instance()) {
- get_script_instance()->call("_setup_local_to_scene");
- }
+ // Can't use GDVIRTUAL in Resource, so this will have to be done with a signal
+ emit_signal(SNAME("setup_local_to_scene_requested"));
}
Node *(*Resource::_get_local_scene_func)() = nullptr;
@@ -350,8 +381,8 @@ bool Resource::is_translation_remapped() const {
#ifdef TOOLS_ENABLED
//helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored
-void Resource::set_id_for_path(const String &p_path, int p_id) {
- if (p_id == -1) {
+void Resource::set_id_for_path(const String &p_path, const String &p_id) {
+ if (p_id == "") {
ResourceCache::path_cache_lock.write_lock();
ResourceCache::resource_path_cache[p_path].erase(get_path());
ResourceCache::path_cache_lock.write_unlock();
@@ -362,15 +393,15 @@ void Resource::set_id_for_path(const String &p_path, int p_id) {
}
}
-int Resource::get_id_for_path(const String &p_path) const {
+String Resource::get_id_for_path(const String &p_path) const {
ResourceCache::path_cache_lock.read_lock();
if (ResourceCache::resource_path_cache[p_path].has(get_path())) {
- int result = ResourceCache::resource_path_cache[p_path][get_path()];
+ String result = ResourceCache::resource_path_cache[p_path][get_path()];
ResourceCache::path_cache_lock.read_unlock();
return result;
} else {
ResourceCache::path_cache_lock.read_unlock();
- return -1;
+ return "";
}
}
#endif
@@ -390,12 +421,12 @@ void Resource::_bind_methods() {
ClassDB::bind_method(D_METHOD("duplicate", "subresources"), &Resource::duplicate, DEFVAL(false));
ADD_SIGNAL(MethodInfo("changed"));
+ ADD_SIGNAL(MethodInfo("setup_local_to_scene_requested"));
+
ADD_GROUP("Resource", "resource_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resource_local_to_scene"), "set_local_to_scene", "is_local_to_scene");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_path", "get_path");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "resource_name"), "set_name", "get_name");
-
- BIND_VMETHOD(MethodInfo("_setup_local_to_scene"));
}
Resource::Resource() :
@@ -414,7 +445,7 @@ Resource::~Resource() {
HashMap<String, Resource *> ResourceCache::resources;
#ifdef TOOLS_ENABLED
-HashMap<String, HashMap<String, int>> ResourceCache::resource_path_cache;
+HashMap<String, HashMap<String, String>> ResourceCache::resource_path_cache;
#endif
RWLock ResourceCache::lock;
@@ -520,5 +551,7 @@ void ResourceCache::dump(const char *p_file, bool p_short) {
}
lock.read_unlock();
+#else
+ WARN_PRINT("ResourceCache::dump only with in debug builds.");
#endif
}
diff --git a/core/io/resource.h b/core/io/resource.h
index 75a9f928f8..9ccc247887 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -31,8 +31,9 @@
#ifndef RESOURCE_H
#define RESOURCE_H
+#include "core/io/resource_uid.h"
#include "core/object/class_db.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
#include "core/templates/safe_refcount.h"
#include "core/templates/self_list.h"
@@ -43,8 +44,8 @@ public:
\
private:
-class Resource : public Reference {
- GDCLASS(Resource, Reference);
+class Resource : public RefCounted {
+ GDCLASS(Resource, RefCounted);
OBJ_CATEGORY("Resources");
public:
@@ -59,9 +60,7 @@ private:
String name;
String path_cache;
- int subindex = 0;
-
- virtual bool _use_builtin_script() const { return true; }
+ String scene_unique_id;
#ifdef TOOLS_ENABLED
uint64_t last_modified_time = 0;
@@ -105,8 +104,9 @@ public:
virtual void set_path(const String &p_path, bool p_take_over = false);
String get_path() const;
- void set_subindex(int p_sub_index);
- int get_subindex() const;
+ static String generate_scene_unique_id();
+ void set_scene_unique_id(const String &p_id);
+ String get_scene_unique_id() const;
virtual Ref<Resource> duplicate(bool p_subresources = false) const;
Ref<Resource> duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Resource>, Ref<Resource>> &remap_cache);
@@ -140,8 +140,8 @@ public:
#ifdef TOOLS_ENABLED
//helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored
- void set_id_for_path(const String &p_path, int p_id);
- int get_id_for_path(const String &p_path) const;
+ void set_id_for_path(const String &p_path, const String &p_id);
+ String get_id_for_path(const String &p_path) const;
#endif
Resource();
@@ -156,7 +156,7 @@ class ResourceCache {
static RWLock lock;
static HashMap<String, Resource *> resources;
#ifdef TOOLS_ENABLED
- static HashMap<String, HashMap<String, int>> resource_path_cache; // each tscn has a set of resource paths and IDs
+ static HashMap<String, HashMap<String, String>> resource_path_cache; // Each tscn has a set of resource paths and IDs.
static RWLock path_cache_lock;
#endif // TOOLS_ENABLED
friend void unregister_core_types();
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 50c9b2371a..84fd6496a7 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -31,10 +31,10 @@
#include "resource_format_binary.h"
#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
#include "core/io/file_access_compressed.h"
#include "core/io/image.h"
#include "core/io/marshalls.h"
-#include "core/os/dir_access.h"
#include "core/version.h"
//#define print_bl(m_what) print_line(m_what)
@@ -51,7 +51,7 @@ enum {
VARIANT_RECT2 = 11,
VARIANT_VECTOR3 = 12,
VARIANT_PLANE = 13,
- VARIANT_QUAT = 14,
+ VARIANT_QUATERNION = 14,
VARIANT_AABB = 15,
VARIANT_MATRIX3 = 16,
VARIANT_TRANSFORM = 17,
@@ -84,9 +84,10 @@ enum {
OBJECT_EXTERNAL_RESOURCE = 1,
OBJECT_INTERNAL_RESOURCE = 2,
OBJECT_EXTERNAL_RESOURCE_INDEX = 3,
- //version 2: added 64 bits support for float and int
- //version 3: changed nodepath encoding
- FORMAT_VERSION = 3,
+ // Version 2: added 64 bits support for float and int.
+ // Version 3: changed nodepath encoding.
+ // Version 4: new string ID for ext/subresources, breaks forward compat.
+ FORMAT_VERSION = 4,
FORMAT_VERSION_CAN_RENAME_DEPS = 1,
FORMAT_VERSION_NO_NODEPATH_PROPERTY = 3,
};
@@ -199,8 +200,8 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
v.d = f->get_real();
r_v = v;
} break;
- case VARIANT_QUAT: {
- Quat v;
+ case VARIANT_QUATERNION: {
+ Quaternion v;
v.x = f->get_real();
v.y = f->get_real();
v.z = f->get_real();
@@ -245,7 +246,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
} break;
case VARIANT_TRANSFORM: {
- Transform v;
+ Transform3D v;
v.basis.elements[0].x = f->get_real();
v.basis.elements[0].y = f->get_real();
v.basis.elements[0].z = f->get_real();
@@ -311,7 +312,14 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
} break;
case OBJECT_INTERNAL_RESOURCE: {
uint32_t index = f->get_32();
- String path = res_path + "::" + itos(index);
+ String path;
+
+ if (using_named_scene_ids) { // New format.
+ ERR_FAIL_INDEX_V((int)index, internal_resources.size(), ERR_PARSE_ERROR);
+ path = internal_resources[index].path;
+ } else {
+ path += res_path + "::" + itos(index);
+ }
//always use internal cache for loading internal resources
if (!internal_index_cache.has(path)) {
@@ -320,7 +328,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
} else {
r_v = internal_index_cache[path];
}
-
} break;
case OBJECT_EXTERNAL_RESOURCE: {
//old file format, still around for compatibility
@@ -328,7 +335,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
String exttype = get_unicode_string();
String path = get_unicode_string();
- if (path.find("://") == -1 && path.is_rel_path()) {
+ if (path.find("://") == -1 && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(res_path.get_base_dir().plus_file(path));
}
@@ -378,7 +385,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
ERR_FAIL_V(ERR_FILE_CORRUPT);
} break;
}
-
} break;
case VARIANT_CALLABLE: {
r_v = Callable();
@@ -620,7 +626,7 @@ Error ResourceLoaderBinary::load() {
path = remaps[path];
}
- if (path.find("://") == -1 && path.is_rel_path()) {
+ if (path.find("://") == -1 && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(path.get_base_dir().plus_file(external_resources[i].path));
}
@@ -659,15 +665,17 @@ Error ResourceLoaderBinary::load() {
//maybe it is loaded already
String path;
- int subindex = 0;
+ String id;
if (!main) {
path = internal_resources[i].path;
if (path.begins_with("local://")) {
path = path.replace_first("local://", "");
- subindex = path.to_int();
+ id = path;
path = res_path + "::" + path;
+
+ internal_resources.write[i].path = path; // Update path.
}
if (cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE) {
@@ -704,7 +712,7 @@ Error ResourceLoaderBinary::load() {
if (res.is_null()) {
//did not replace
- Object *obj = ClassDB::instance(t);
+ Object *obj = ClassDB::instantiate(t);
if (!obj) {
error = ERR_FILE_CORRUPT;
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource of unrecognized type in file: " + t + ".");
@@ -722,7 +730,7 @@ Error ResourceLoaderBinary::load() {
if (path != String() && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
r->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); //if got here because the resource with same path has different type, replace it
}
- r->set_subindex(subindex);
+ r->set_scene_unique_id(id);
}
if (!main) {
@@ -808,13 +816,18 @@ String ResourceLoaderBinary::get_unicode_string() {
}
void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types) {
- open(p_f);
+ open(p_f, false, true);
if (error) {
return;
}
for (int i = 0; i < external_resources.size(); i++) {
- String dep = external_resources[i].path;
+ String dep;
+ if (external_resources[i].uid != ResourceUID::INVALID_ID) {
+ dep = ResourceUID::get_singleton()->id_to_text(external_resources[i].uid);
+ } else {
+ dep = external_resources[i].path;
+ }
if (p_add_types && external_resources[i].type != String()) {
dep += "::" + external_resources[i].type;
@@ -824,7 +837,7 @@ void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dep
}
}
-void ResourceLoaderBinary::open(FileAccess *p_f) {
+void ResourceLoaderBinary::open(FileAccess *p_f, bool p_no_resources, bool p_keep_uuid_paths) {
error = OK;
f = p_f;
@@ -851,7 +864,7 @@ void ResourceLoaderBinary::open(FileAccess *p_f) {
bool big_endian = f->get_32();
bool use_real64 = f->get_32();
- f->set_endian_swap(big_endian != 0); //read big endian if saved as big endian
+ f->set_big_endian(big_endian != 0); //read big endian if saved as big endian
uint32_t ver_major = f->get_32();
uint32_t ver_minor = f->get_32();
@@ -879,10 +892,29 @@ void ResourceLoaderBinary::open(FileAccess *p_f) {
print_bl("type: " + type);
importmd_ofs = f->get_64();
- for (int i = 0; i < 14; i++) {
+ uint32_t flags = f->get_32();
+ if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_NAMED_SCENE_IDS) {
+ using_named_scene_ids = true;
+ }
+ if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS) {
+ using_uids = true;
+ }
+
+ if (using_uids) {
+ uid = f->get_64();
+ } else {
+ f->get_64(); // skip over uid field
+ uid = ResourceUID::INVALID_ID;
+ }
+
+ for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) {
f->get_32(); //skip a few reserved fields
}
+ if (p_no_resources) {
+ return;
+ }
+
uint32_t string_table_size = f->get_32();
string_map.resize(string_table_size);
for (uint32_t i = 0; i < string_table_size; i++) {
@@ -896,8 +928,18 @@ void ResourceLoaderBinary::open(FileAccess *p_f) {
for (uint32_t i = 0; i < ext_resources_size; i++) {
ExtResource er;
er.type = get_unicode_string();
-
er.path = get_unicode_string();
+ if (using_uids) {
+ er.uid = f->get_64();
+ if (!p_keep_uuid_paths && er.uid != ResourceUID::INVALID_ID) {
+ if (ResourceUID::get_singleton()->has_id(er.uid)) {
+ // If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path.
+ er.path = ResourceUID::get_singleton()->get_id_path(er.uid);
+ } else {
+ WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UUID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data());
+ }
+ }
+ }
external_resources.push_back(er);
}
@@ -948,7 +990,7 @@ String ResourceLoaderBinary::recognize(FileAccess *p_f) {
bool big_endian = f->get_32();
f->get_32(); // use_real64
- f->set_endian_swap(big_endian != 0); //read big endian if saved as big endian
+ f->set_big_endian(big_endian != 0); //read big endian if saved as big endian
uint32_t ver_major = f->get_32();
f->get_32(); // ver_minor
@@ -1013,8 +1055,8 @@ void ResourceFormatLoaderBinary::get_recognized_extensions_for_type(const String
extensions.sort();
- for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
- String ext = E->get().to_lower();
+ for (const String &E : extensions) {
+ String ext = E.to_lower();
p_extensions->push_back(ext);
}
}
@@ -1024,8 +1066,8 @@ void ResourceFormatLoaderBinary::get_recognized_extensions(List<String> *p_exten
ClassDB::get_resource_base_extensions(&extensions);
extensions.sort();
- for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
- String ext = E->get().to_lower();
+ for (const String &E : extensions) {
+ String ext = E.to_lower();
p_extensions->push_back(ext);
}
}
@@ -1097,13 +1139,13 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
bool big_endian = f->get_32();
bool use_real64 = f->get_32();
- f->set_endian_swap(big_endian != 0); //read big endian if saved as big endian
+ f->set_big_endian(big_endian != 0); //read big endian if saved as big endian
#ifdef BIG_ENDIAN_ENABLED
fw->store_32(!big_endian);
#else
fw->store_32(big_endian);
#endif
- fw->set_endian_swap(big_endian != 0);
+ fw->set_big_endian(big_endian != 0);
fw->store_32(use_real64); //use real64
uint32_t ver_major = f->get_32();
@@ -1161,8 +1203,15 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
uint64_t importmd_ofs = f->get_64();
fw->store_64(0); //metadata offset
- for (int i = 0; i < 14; i++) {
- fw->store_32(0);
+ uint32_t flags = f->get_32();
+ bool using_uids = (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS);
+ uint64_t uid_data = f->get_64();
+
+ fw->store_32(flags);
+ fw->store_64(uid_data);
+
+ for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) {
+ fw->store_32(0); // reserved
f->get_32();
}
@@ -1183,6 +1232,16 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
String type = get_ustring(f);
String path = get_ustring(f);
+ if (using_uids) {
+ ResourceUID::ID uid = f->get_64();
+ if (uid != ResourceUID::INVALID_ID) {
+ if (ResourceUID::get_singleton()->has_id(uid)) {
+ // If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path.
+ path = ResourceUID::get_singleton()->get_id_path(uid);
+ }
+ }
+ }
+
bool relative = false;
if (!path.begins_with("res://")) {
path = local_path.plus_file(path).simplify_path();
@@ -1194,6 +1253,8 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
path = np;
}
+ String full_path = path;
+
if (relative) {
//restore relative
path = local_path.path_to_file(path);
@@ -1201,6 +1262,11 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
save_ustring(fw, type);
save_ustring(fw, path);
+
+ if (using_uids) {
+ ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(full_path);
+ fw->store_64(uid);
+ }
}
int64_t size_diff = (int64_t)fw->get_position() - (int64_t)f->get_position();
@@ -1256,6 +1322,28 @@ String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const
return ClassDB::get_compatibility_remapped_class(r);
}
+ResourceUID::ID ResourceFormatLoaderBinary::get_resource_uid(const String &p_path) const {
+ String ext = p_path.get_extension().to_lower();
+ if (!ClassDB::is_resource_extension(ext)) {
+ return ResourceUID::INVALID_ID;
+ }
+
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
+ if (!f) {
+ return ResourceUID::INVALID_ID; //could not read
+ }
+
+ ResourceLoaderBinary loader;
+ loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
+ loader.res_path = loader.local_path;
+ //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) );
+ loader.open(f, true);
+ if (loader.error != OK) {
+ return ResourceUID::INVALID_ID; //could not read
+ }
+ return loader.uid;
+}
+
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
@@ -1269,11 +1357,7 @@ void ResourceFormatSaverBinaryInstance::_pad_buffer(FileAccess *f, int p_bytes)
}
}
-void ResourceFormatSaverBinaryInstance::_write_variant(const Variant &p_property, const PropertyInfo &p_hint) {
- write_variant(f, p_property, resource_set, external_resources, string_map, p_hint);
-}
-
-void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Variant &p_property, Set<RES> &resource_set, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint) {
+void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Variant &p_property, Map<RES, int> &resource_map, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint) {
switch (p_property.get_type()) {
case Variant::NIL: {
f->store_32(VARIANT_NIL);
@@ -1371,9 +1455,9 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
f->store_real(val.d);
} break;
- case Variant::QUAT: {
- f->store_32(VARIANT_QUAT);
- Quat val = p_property;
+ case Variant::QUATERNION: {
+ f->store_32(VARIANT_QUATERNION);
+ Quaternion val = p_property;
f->store_real(val.x);
f->store_real(val.y);
f->store_real(val.z);
@@ -1416,9 +1500,9 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
f->store_real(val.elements[2].z);
} break;
- case Variant::TRANSFORM: {
+ case Variant::TRANSFORM3D: {
f->store_32(VARIANT_TRANSFORM);
- Transform val = p_property;
+ Transform3D val = p_property;
f->store_real(val.basis.elements[0].x);
f->store_real(val.basis.elements[0].y);
f->store_real(val.basis.elements[0].z);
@@ -1492,13 +1576,13 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
f->store_32(OBJECT_EXTERNAL_RESOURCE_INDEX);
f->store_32(external_resources[res]);
} else {
- if (!resource_set.has(res)) {
+ if (!resource_map.has(res)) {
f->store_32(OBJECT_EMPTY);
ERR_FAIL_MSG("Resource was not pre cached for the resource section, most likely due to circular reference.");
}
f->store_32(OBJECT_INTERNAL_RESOURCE);
- f->store_32(res->get_subindex());
+ f->store_32(resource_map[res]);
//internal resource
}
@@ -1520,14 +1604,14 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
List<Variant> keys;
d.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+ for (const Variant &E : keys) {
/*
- if (!_check_type(dict[E->get()]))
+ if (!_check_type(dict[E]))
continue;
*/
- write_variant(f, E->get(), resource_set, external_resources, string_map);
- write_variant(f, d[E->get()], resource_set, external_resources, string_map);
+ write_variant(f, E, resource_map, external_resources, string_map);
+ write_variant(f, d[E], resource_map, external_resources, string_map);
}
} break;
@@ -1536,7 +1620,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
Array a = p_property;
f->store_32(uint32_t(a.size()));
for (int i = 0; i < a.size(); i++) {
- write_variant(f, a[i], resource_set, external_resources, string_map);
+ write_variant(f, a[i], resource_map, external_resources, string_map);
}
} break;
@@ -1677,15 +1761,15 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
res->get_property_list(&property_list);
- for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
- if (E->get().usage & PROPERTY_USAGE_STORAGE) {
- Variant value = res->get(E->get().name);
- if (E->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
+ for (const PropertyInfo &E : property_list) {
+ if (E.usage & PROPERTY_USAGE_STORAGE) {
+ Variant value = res->get(E.name);
+ if (E.usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
RES sres = value;
if (sres.is_valid()) {
NonPersistentKey npk;
npk.base = res;
- npk.property = E->get().name;
+ npk.property = E.name;
non_persistent_map[npk] = sres;
resource_set.insert(sres);
saved_resources.push_back(sres);
@@ -1715,9 +1799,9 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
Dictionary d = p_variant;
List<Variant> keys;
d.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- _find_resources(E->get());
- Variant v = d[E->get()];
+ for (const Variant &E : keys) {
+ _find_resources(E);
+ Variant v = d[E];
_find_resources(v);
}
} break;
@@ -1798,7 +1882,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
if (big_endian) {
f->store_32(1);
- f->set_endian_swap(true);
+ f->set_big_endian(true);
} else {
f->store_32(0);
}
@@ -1816,46 +1900,49 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
save_unicode_string(f, p_resource->get_class());
f->store_64(0); //offset to import metadata
- for (int i = 0; i < 14; i++) {
+ f->store_32(FORMAT_FLAG_NAMED_SCENE_IDS | FORMAT_FLAG_UIDS);
+ ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(p_path, true);
+ f->store_64(uid);
+ for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) {
f->store_32(0); // reserved
}
List<ResourceData> resources;
{
- for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) {
+ for (const RES &E : saved_resources) {
ResourceData &rd = resources.push_back(ResourceData())->get();
- rd.type = E->get()->get_class();
+ rd.type = E->get_class();
List<PropertyInfo> property_list;
- E->get()->get_property_list(&property_list);
+ E->get_property_list(&property_list);
- for (List<PropertyInfo>::Element *F = property_list.front(); F; F = F->next()) {
- if (skip_editor && F->get().name.begins_with("__editor")) {
+ for (const PropertyInfo &F : property_list) {
+ if (skip_editor && F.name.begins_with("__editor")) {
continue;
}
- if ((F->get().usage & PROPERTY_USAGE_STORAGE)) {
+ if ((F.usage & PROPERTY_USAGE_STORAGE)) {
Property p;
- p.name_idx = get_string_index(F->get().name);
+ p.name_idx = get_string_index(F.name);
- if (F->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
+ if (F.usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
NonPersistentKey npk;
- npk.base = E->get();
- npk.property = F->get().name;
+ npk.base = E;
+ npk.property = F.name;
if (non_persistent_map.has(npk)) {
p.value = non_persistent_map[npk];
}
} else {
- p.value = E->get()->get(F->get().name);
+ p.value = E->get(F.name);
}
- Variant default_value = ClassDB::class_get_default_property_value(E->get()->get_class(), F->get().name);
+ Variant default_value = ClassDB::class_get_default_property_value(E->get_class(), F.name);
if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, p.value, default_value))) {
continue;
}
- p.pi = F->get();
+ p.pi = F;
rd.properties.push_back(p);
}
@@ -1882,41 +1969,47 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
String path = save_order[i]->get_path();
path = relative_paths ? local_path.path_to_file(path) : path;
save_unicode_string(f, path);
+ ResourceUID::ID ruid = ResourceSaver::get_resource_id_for_path(save_order[i]->get_path(), false);
+ f->store_64(ruid);
}
// save internal resource table
f->store_32(saved_resources.size()); //amount of internal resources
Vector<uint64_t> ofs_pos;
- Set<int> used_indices;
+ Set<String> used_unique_ids;
- for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) {
- RES r = E->get();
+ for (RES &r : saved_resources) {
if (r->get_path() == "" || r->get_path().find("::") != -1) {
- if (r->get_subindex() != 0) {
- if (used_indices.has(r->get_subindex())) {
- r->set_subindex(0); //repeated
+ if (r->get_scene_unique_id() != "") {
+ if (used_unique_ids.has(r->get_scene_unique_id())) {
+ r->set_scene_unique_id("");
} else {
- used_indices.insert(r->get_subindex());
+ used_unique_ids.insert(r->get_scene_unique_id());
}
}
}
}
- for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) {
- RES r = E->get();
+ Map<RES, int> resource_map;
+ int res_index = 0;
+ for (RES &r : saved_resources) {
if (r->get_path() == "" || r->get_path().find("::") != -1) {
- if (r->get_subindex() == 0) {
- int new_subindex = 1;
- if (used_indices.size()) {
- new_subindex = used_indices.back()->get() + 1;
+ if (r->get_scene_unique_id() == "") {
+ String new_id;
+
+ while (true) {
+ new_id = r->get_class() + "_" + Resource::generate_scene_unique_id();
+ if (!used_unique_ids.has(new_id)) {
+ break;
+ }
}
- r->set_subindex(new_subindex);
- used_indices.insert(new_subindex);
+ r->set_scene_unique_id(new_id);
+ used_unique_ids.insert(new_id);
}
- save_unicode_string(f, "local://" + itos(r->get_subindex()));
+ save_unicode_string(f, "local://" + r->get_scene_unique_id());
if (takeover_paths) {
- r->set_path(p_path + "::" + itos(r->get_subindex()), true);
+ r->set_path(p_path + "::" + r->get_scene_unique_id(), true);
}
#ifdef TOOLS_ENABLED
r->set_edited(false);
@@ -1926,22 +2019,20 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
}
ofs_pos.push_back(f->get_position());
f->store_64(0); //offset in 64 bits
+ resource_map[r] = res_index++;
}
Vector<uint64_t> ofs_table;
//now actually save the resources
- for (List<ResourceData>::Element *E = resources.front(); E; E = E->next()) {
- ResourceData &rd = E->get();
-
+ for (const ResourceData &rd : resources) {
ofs_table.push_back(f->get_position());
save_unicode_string(f, rd.type);
f->store_32(rd.properties.size());
- for (List<Property>::Element *F = rd.properties.front(); F; F = F->next()) {
- Property &p = F->get();
+ for (const Property &p : rd.properties) {
f->store_32(p.name_idx);
- _write_variant(p.value, F->get().pi);
+ write_variant(f, p.value, resource_map, external_resources, string_map, p.pi);
}
}
diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h
index 3592bbdbc4..a6e6d1848e 100644
--- a/core/io/resource_format_binary.h
+++ b/core/io/resource_format_binary.h
@@ -31,9 +31,9 @@
#ifndef RESOURCE_FORMAT_BINARY_H
#define RESOURCE_FORMAT_BINARY_H
+#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/os/file_access.h"
class ResourceLoaderBinary {
bool translation_remapped = false;
@@ -47,6 +47,8 @@ class ResourceLoaderBinary {
uint64_t importmd_ofs = 0;
+ ResourceUID::ID uid = ResourceUID::INVALID_ID;
+
Vector<char> str_buf;
List<RES> resource_cache;
@@ -57,9 +59,12 @@ class ResourceLoaderBinary {
struct ExtResource {
String path;
String type;
+ ResourceUID::ID uid = ResourceUID::INVALID_ID;
RES cache;
};
+ bool using_named_scene_ids = false;
+ bool using_uids = false;
bool use_sub_threads = false;
float *progress = nullptr;
Vector<ExtResource> external_resources;
@@ -93,7 +98,7 @@ public:
void set_translation_remapped(bool p_remapped);
void set_remaps(const Map<String, String> &p_remaps) { remaps = p_remaps; }
- void open(FileAccess *p_f);
+ void open(FileAccess *p_f, bool p_no_resources = false, bool p_keep_uuid_paths = false);
String recognize(FileAccess *p_f);
void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types);
@@ -108,6 +113,7 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
+ virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map);
};
@@ -150,14 +156,19 @@ class ResourceFormatSaverBinaryInstance {
};
static void _pad_buffer(FileAccess *f, int p_bytes);
- void _write_variant(const Variant &p_property, const PropertyInfo &p_hint = PropertyInfo());
void _find_resources(const Variant &p_variant, bool p_main = false);
static void save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len = false);
int get_string_index(const String &p_string);
public:
+ enum {
+ FORMAT_FLAG_NAMED_SCENE_IDS = 1,
+ FORMAT_FLAG_UIDS = 2,
+ // Amount of reserved 32-bit fields in resource header
+ RESERVED_FIELDS = 11
+ };
Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
- static void write_variant(FileAccess *f, const Variant &p_property, Set<RES> &resource_set, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint = PropertyInfo());
+ static void write_variant(FileAccess *f, const Variant &p_property, Map<RES, int> &resource_map, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint = PropertyInfo());
};
class ResourceFormatSaverBinary : public ResourceFormatSaver {
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index b503655edd..1e166015b0 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -93,6 +93,8 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy
r_path_and_type.type = ClassDB::get_compatibility_remapped_class(value);
} else if (assign == "importer") {
r_path_and_type.importer = value;
+ } else if (assign == "uid") {
+ r_path_and_type.uid = ResourceUID::get_singleton()->text_to_id(value);
} else if (assign == "group_file") {
r_path_and_type.group_file = value;
} else if (assign == "metadata") {
@@ -146,10 +148,10 @@ void ResourceFormatImporter::get_recognized_extensions(List<String> *p_extension
for (int i = 0; i < importers.size(); i++) {
List<String> local_exts;
importers[i]->get_recognized_extensions(&local_exts);
- for (List<String>::Element *F = local_exts.front(); F; F = F->next()) {
- if (!found.has(F->get())) {
- p_extensions->push_back(F->get());
- found.insert(F->get());
+ for (const String &F : local_exts) {
+ if (!found.has(F)) {
+ p_extensions->push_back(F);
+ found.insert(F);
}
}
}
@@ -175,10 +177,10 @@ void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_
List<String> local_exts;
importers[i]->get_recognized_extensions(&local_exts);
- for (List<String>::Element *F = local_exts.front(); F; F = F->next()) {
- if (!found.has(F->get())) {
- p_extensions->push_back(F->get());
- found.insert(F->get());
+ for (const String &F : local_exts) {
+ if (!found.has(F)) {
+ p_extensions->push_back(F);
+ found.insert(F);
}
}
}
@@ -336,6 +338,17 @@ String ResourceFormatImporter::get_resource_type(const String &p_path) const {
return pat.type;
}
+ResourceUID::ID ResourceFormatImporter::get_resource_uid(const String &p_path) const {
+ PathAndType pat;
+ Error err = _get_path_and_type(p_path, pat);
+
+ if (err != OK) {
+ return ResourceUID::INVALID_ID;
+ }
+
+ return pat.uid;
+}
+
Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) const {
PathAndType pat;
Error err = _get_path_and_type(p_path, pat);
@@ -372,8 +385,8 @@ void ResourceFormatImporter::get_importers_for_extension(const String &p_extensi
for (int i = 0; i < importers.size(); i++) {
List<String> local_exts;
importers[i]->get_recognized_extensions(&local_exts);
- for (List<String>::Element *F = local_exts.front(); F; F = F->next()) {
- if (p_extension.to_lower() == F->get()) {
+ for (const String &F : local_exts) {
+ if (p_extension.to_lower() == F) {
r_importers->push_back(importers[i]);
}
}
@@ -393,8 +406,8 @@ Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_extension(const St
for (int i = 0; i < importers.size(); i++) {
List<String> local_exts;
importers[i]->get_recognized_extensions(&local_exts);
- for (List<String>::Element *F = local_exts.front(); F; F = F->next()) {
- if (p_extension.to_lower() == F->get() && importers[i]->get_priority() > priority) {
+ for (const String &F : local_exts) {
+ if (p_extension.to_lower() == F && importers[i]->get_priority() > priority) {
importer = importers[i];
priority = importers[i]->get_priority();
}
@@ -445,3 +458,8 @@ ResourceFormatImporter *ResourceFormatImporter::singleton = nullptr;
ResourceFormatImporter::ResourceFormatImporter() {
singleton = this;
}
+
+void ResourceImporter::_bind_methods() {
+ BIND_ENUM_CONSTANT(IMPORT_ORDER_DEFAULT);
+ BIND_ENUM_CONSTANT(IMPORT_ORDER_SCENE);
+}
diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h
index a14d6ba52c..a1cacbd306 100644
--- a/core/io/resource_importer.h
+++ b/core/io/resource_importer.h
@@ -42,6 +42,7 @@ class ResourceFormatImporter : public ResourceFormatLoader {
String importer;
String group_file;
Variant metadata;
+ uint64_t uid = ResourceUID::INVALID_ID;
};
Error _get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid = nullptr) const;
@@ -63,6 +64,8 @@ public:
virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
+ virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
+
virtual Variant get_resource_metadata(const String &p_path) const;
virtual bool is_import_valid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
@@ -93,8 +96,11 @@ public:
ResourceFormatImporter();
};
-class ResourceImporter : public Reference {
- GDCLASS(ResourceImporter, Reference);
+class ResourceImporter : public RefCounted {
+ GDCLASS(ResourceImporter, RefCounted);
+
+protected:
+ static void _bind_methods();
public:
virtual String get_importer_name() const = 0;
@@ -103,7 +109,7 @@ public:
virtual String get_save_extension() const = 0;
virtual String get_resource_type() const = 0;
virtual float get_priority() const { return 1.0; }
- virtual int get_import_order() const { return 0; }
+ virtual int get_import_order() const { return IMPORT_ORDER_DEFAULT; }
virtual int get_format_version() const { return 0; }
struct ImportOption {
@@ -117,6 +123,11 @@ public:
ImportOption() {}
};
+ enum ImportOrder {
+ IMPORT_ORDER_DEFAULT = 0,
+ IMPORT_ORDER_SCENE = 100,
+ };
+
virtual bool has_advanced_options() const { return false; }
virtual void show_advanced_options(const String &p_path) {}
@@ -137,4 +148,6 @@ public:
virtual String get_import_settings_string() const { return String(); }
};
+VARIANT_ENUM_CAST(ResourceImporter::ImportOrder);
+
#endif // RESOURCE_IMPORTER_H
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index b942c30086..3026236f07 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -31,8 +31,8 @@
#include "resource_loader.h"
#include "core/config/project_settings.h"
+#include "core/io/file_access.h"
#include "core/io/resource_importer.h"
-#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
@@ -58,8 +58,8 @@ bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_
get_recognized_extensions_for_type(p_for_type, &extensions);
}
- for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
- if (E->get().nocasecmp_to(extension) == 0) {
+ for (const String &E : extensions) {
+ if (E.nocasecmp_to(extension) == 0) {
return true;
}
}
@@ -68,22 +68,33 @@ bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_
}
bool ResourceFormatLoader::handles_type(const String &p_type) const {
- if (get_script_instance() && get_script_instance()->has_method("handles_type")) {
- // I guess custom loaders for custom resources should use "Resource"
- return get_script_instance()->call("handles_type", p_type);
+ bool success;
+ if (GDVIRTUAL_CALL(_handles_type, p_type, success)) {
+ return success;
}
return false;
}
String ResourceFormatLoader::get_resource_type(const String &p_path) const {
- if (get_script_instance() && get_script_instance()->has_method("get_resource_type")) {
- return get_script_instance()->call("get_resource_type", p_path);
+ String ret;
+
+ if (GDVIRTUAL_CALL(_get_resource_type, p_path, ret)) {
+ return ret;
}
return "";
}
+ResourceUID::ID ResourceFormatLoader::get_resource_uid(const String &p_path) const {
+ int64_t uid;
+ if (GDVIRTUAL_CALL(_get_resource_uid, p_path, uid)) {
+ return uid;
+ }
+
+ return ResourceUID::INVALID_ID;
+}
+
void ResourceFormatLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const {
if (p_type == "" || handles_type(p_type)) {
get_recognized_extensions(p_extensions);
@@ -97,27 +108,26 @@ void ResourceLoader::get_recognized_extensions_for_type(const String &p_type, Li
}
bool ResourceFormatLoader::exists(const String &p_path) const {
+ bool success;
+ if (GDVIRTUAL_CALL(_exists, p_path, success)) {
+ return success;
+ }
return FileAccess::exists(p_path); //by default just check file
}
void ResourceFormatLoader::get_recognized_extensions(List<String> *p_extensions) const {
- if (get_script_instance() && get_script_instance()->has_method("get_recognized_extensions")) {
- PackedStringArray exts = get_script_instance()->call("get_recognized_extensions");
-
- {
- const String *r = exts.ptr();
- for (int i = 0; i < exts.size(); ++i) {
- p_extensions->push_back(r[i]);
- }
+ PackedStringArray exts;
+ if (GDVIRTUAL_CALL(_get_recognized_extensions, exts)) {
+ const String *r = exts.ptr();
+ for (int i = 0; i < exts.size(); ++i) {
+ p_extensions->push_back(r[i]);
}
}
}
RES ResourceFormatLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
- // Check user-defined loader if there's any. Hard fail if it returns an error.
- if (get_script_instance() && get_script_instance()->has_method("load")) {
- Variant res = get_script_instance()->call("load", p_path, p_original_path, p_use_sub_threads, p_cache_mode);
-
+ Variant res;
+ if (GDVIRTUAL_CALL(_load, p_path, p_original_path, p_use_sub_threads, p_cache_mode, res)) {
if (res.get_type() == Variant::INT) { // Error code, abort.
if (r_error) {
*r_error = (Error)res.operator int64_t();
@@ -135,48 +145,42 @@ RES ResourceFormatLoader::load(const String &p_path, const String &p_original_pa
}
void ResourceFormatLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
- if (get_script_instance() && get_script_instance()->has_method("get_dependencies")) {
- PackedStringArray deps = get_script_instance()->call("get_dependencies", p_path, p_add_types);
-
- {
- const String *r = deps.ptr();
- for (int i = 0; i < deps.size(); ++i) {
- p_dependencies->push_back(r[i]);
- }
+ PackedStringArray deps;
+ if (GDVIRTUAL_CALL(_get_dependencies, p_path, p_add_types, deps)) {
+ const String *r = deps.ptr();
+ for (int i = 0; i < deps.size(); ++i) {
+ p_dependencies->push_back(r[i]);
}
}
}
Error ResourceFormatLoader::rename_dependencies(const String &p_path, const Map<String, String> &p_map) {
- if (get_script_instance() && get_script_instance()->has_method("rename_dependencies")) {
- Dictionary deps_dict;
- for (Map<String, String>::Element *E = p_map.front(); E; E = E->next()) {
- deps_dict[E->key()] = E->value();
- }
+ Dictionary deps_dict;
+ for (Map<String, String>::Element *E = p_map.front(); E; E = E->next()) {
+ deps_dict[E->key()] = E->value();
+ }
- int64_t res = get_script_instance()->call("rename_dependencies", deps_dict);
- return (Error)res;
+ int64_t err;
+ if (GDVIRTUAL_CALL(_rename_dependencies, p_path, deps_dict, err)) {
+ return (Error)err;
}
return OK;
}
void ResourceFormatLoader::_bind_methods() {
- {
- MethodInfo info = MethodInfo(Variant::NIL, "load", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "original_path"), PropertyInfo(Variant::BOOL, "use_sub_threads"), PropertyInfo(Variant::INT, "cache_mode"));
- info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- ClassDB::add_virtual_method(get_class_static(), info);
- }
-
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::PACKED_STRING_ARRAY, "get_recognized_extensions"));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "handles_type", PropertyInfo(Variant::STRING_NAME, "typename")));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_resource_type", PropertyInfo(Variant::STRING, "path")));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo("get_dependencies", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "add_types")));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::INT, "rename_dependencies", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "renames")));
-
BIND_ENUM_CONSTANT(CACHE_MODE_IGNORE);
BIND_ENUM_CONSTANT(CACHE_MODE_REUSE);
BIND_ENUM_CONSTANT(CACHE_MODE_REPLACE);
+
+ GDVIRTUAL_BIND(_get_recognized_extensions);
+ GDVIRTUAL_BIND(_handles_type, "type");
+ GDVIRTUAL_BIND(_get_resource_type, "path");
+ GDVIRTUAL_BIND(_get_resource_uid, "path");
+ GDVIRTUAL_BIND(_get_dependencies, "path", "add_types");
+ GDVIRTUAL_BIND(_rename_dependencies, "path", "renames");
+ GDVIRTUAL_BIND(_exists, "path");
+ GDVIRTUAL_BIND(_load, "path", "original_path", "use_sub_threads", "cache_mode");
}
///////////////////////////////////
@@ -270,13 +274,18 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
thread_load_mutex->unlock();
}
-Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode, const String &p_source_resource) {
- String local_path;
- if (p_path.is_rel_path()) {
- local_path = "res://" + p_path;
+static String _validate_local_path(const String &p_path) {
+ ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(p_path);
+ if (uid != ResourceUID::INVALID_ID) {
+ return ResourceUID::get_singleton()->get_id_path(uid);
+ } else if (p_path.is_relative_path()) {
+ return "res://" + p_path;
} else {
- local_path = ProjectSettings::get_singleton()->localize_path(p_path);
+ return ProjectSettings::get_singleton()->localize_path(p_path);
}
+}
+Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode, const String &p_source_resource) {
+ String local_path = _validate_local_path(p_path);
thread_load_mutex->lock();
@@ -354,7 +363,7 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String &
ThreadLoadTask &load_task = thread_load_tasks[local_path];
- if (load_task.resource.is_null()) { //needs to be loaded in thread
+ if (load_task.resource.is_null()) { //needs to be loaded in thread
load_task.semaphore = memnew(Semaphore);
if (thread_loading_count < thread_load_max) {
@@ -399,12 +408,7 @@ float ResourceLoader::_dependency_get_progress(const String &p_path) {
}
ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const String &p_path, float *r_progress) {
- String local_path;
- if (p_path.is_rel_path()) {
- local_path = "res://" + p_path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(p_path);
- }
+ String local_path = _validate_local_path(p_path);
thread_load_mutex->lock();
if (!thread_load_tasks.has(local_path)) {
@@ -424,12 +428,7 @@ ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const
}
RES ResourceLoader::load_threaded_get(const String &p_path, Error *r_error) {
- String local_path;
- if (p_path.is_rel_path()) {
- local_path = "res://" + p_path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(p_path);
- }
+ String local_path = _validate_local_path(p_path);
thread_load_mutex->lock();
if (!thread_load_tasks.has(local_path)) {
@@ -510,12 +509,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, Resour
*r_error = ERR_CANT_OPEN;
}
- String local_path;
- if (p_path.is_rel_path()) {
- local_path = "res://" + p_path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(p_path);
- }
+ String local_path = _validate_local_path(p_path);
if (p_cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
thread_load_mutex->lock();
@@ -612,12 +606,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, Resour
}
bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) {
- String local_path;
- if (p_path.is_rel_path()) {
- local_path = "res://" + p_path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(p_path);
- }
+ String local_path = _validate_local_path(p_path);
if (ResourceCache::has(local_path)) {
return true; // If cached, it probably exists
@@ -677,14 +666,7 @@ void ResourceLoader::remove_resource_format_loader(Ref<ResourceFormatLoader> p_f
}
int ResourceLoader::get_import_order(const String &p_path) {
- String path = _path_remap(p_path);
-
- String local_path;
- if (path.is_rel_path()) {
- local_path = "res://" + path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(path);
- }
+ String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@@ -702,14 +684,7 @@ int ResourceLoader::get_import_order(const String &p_path) {
}
String ResourceLoader::get_import_group_file(const String &p_path) {
- String path = _path_remap(p_path);
-
- String local_path;
- if (path.is_rel_path()) {
- local_path = "res://" + path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(path);
- }
+ String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@@ -727,14 +702,7 @@ String ResourceLoader::get_import_group_file(const String &p_path) {
}
bool ResourceLoader::is_import_valid(const String &p_path) {
- String path = _path_remap(p_path);
-
- String local_path;
- if (path.is_rel_path()) {
- local_path = "res://" + path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(path);
- }
+ String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@@ -752,14 +720,7 @@ bool ResourceLoader::is_import_valid(const String &p_path) {
}
bool ResourceLoader::is_imported(const String &p_path) {
- String path = _path_remap(p_path);
-
- String local_path;
- if (path.is_rel_path()) {
- local_path = "res://" + path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(path);
- }
+ String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@@ -777,14 +738,7 @@ bool ResourceLoader::is_imported(const String &p_path) {
}
void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
- String path = _path_remap(p_path);
-
- String local_path;
- if (path.is_rel_path()) {
- local_path = "res://" + path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(path);
- }
+ String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@@ -800,14 +754,7 @@ void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_depe
}
Error ResourceLoader::rename_dependencies(const String &p_path, const Map<String, String> &p_map) {
- String path = _path_remap(p_path);
-
- String local_path;
- if (path.is_rel_path()) {
- local_path = "res://" + path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(path);
- }
+ String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@@ -825,12 +772,7 @@ Error ResourceLoader::rename_dependencies(const String &p_path, const Map<String
}
String ResourceLoader::get_resource_type(const String &p_path) {
- String local_path;
- if (p_path.is_rel_path()) {
- local_path = "res://" + p_path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(p_path);
- }
+ String local_path = _validate_local_path(p_path);
for (int i = 0; i < loader_count; i++) {
String result = loader[i]->get_resource_type(local_path);
@@ -842,6 +784,19 @@ String ResourceLoader::get_resource_type(const String &p_path) {
return "";
}
+ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) {
+ String local_path = _validate_local_path(p_path);
+
+ for (int i = 0; i < loader_count; i++) {
+ ResourceUID::ID id = loader[i]->get_resource_uid(local_path);
+ if (id != ResourceUID::INVALID_ID) {
+ return id;
+ }
+ }
+
+ return ResourceUID::INVALID_ID;
+}
+
String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_remapped) {
String new_path = p_path;
@@ -978,15 +933,15 @@ void ResourceLoader::load_translation_remaps() {
Dictionary remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps");
List<Variant> keys;
remaps.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- Array langs = remaps[E->get()];
+ for (const Variant &E : keys) {
+ Array langs = remaps[E];
Vector<String> lang_remaps;
lang_remaps.resize(langs.size());
for (int i = 0; i < langs.size(); i++) {
lang_remaps.write[i] = langs[i];
}
- translation_remaps[String(E->get())] = lang_remaps;
+ translation_remaps[String(E)] = lang_remaps;
}
}
@@ -1045,7 +1000,7 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) {
bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatLoader");
ERR_FAIL_COND_V_MSG(!valid_type, false, "Script does not inherit a CustomResourceLoader: " + script_path + ".");
- Object *obj = ClassDB::instance(ibt);
+ Object *obj = ClassDB::instantiate(ibt);
ERR_FAIL_COND_V_MSG(obj == nullptr, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + ".");
@@ -1071,8 +1026,7 @@ void ResourceLoader::add_custom_loaders() {
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
- for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
- StringName class_name = E->get();
+ for (const StringName &class_name : global_classes) {
StringName base_class = ScriptServer::get_global_class_native_base(class_name);
if (base_class == custom_loader_base_class) {
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 914d988caa..f1d9815635 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -32,22 +32,34 @@
#define RESOURCE_LOADER_H
#include "core/io/resource.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
#include "core/os/semaphore.h"
#include "core/os/thread.h"
-class ResourceFormatLoader : public Reference {
- GDCLASS(ResourceFormatLoader, Reference);
+class ResourceFormatLoader : public RefCounted {
+ GDCLASS(ResourceFormatLoader, RefCounted);
public:
enum CacheMode {
- CACHE_MODE_IGNORE, //resource and subresources do not use path cache, no path is set into resource.
- CACHE_MODE_REUSE, //resource and subresources use patch cache, reuse existing loaded resources instead of loading from disk when available
- CACHE_MODE_REPLACE, //resource and and subresource use path cache, but replace existing loaded resources when available with information from disk
+ CACHE_MODE_IGNORE, // Resource and subresources do not use path cache, no path is set into resource.
+ CACHE_MODE_REUSE, // Resource and subresources use patch cache, reuse existing loaded resources instead of loading from disk when available.
+ CACHE_MODE_REPLACE, // Resource and subresource use path cache, but replace existing loaded resources when available with information from disk.
};
protected:
static void _bind_methods();
+ GDVIRTUAL0RC(Vector<String>, _get_recognized_extensions)
+ GDVIRTUAL1RC(bool, _handles_type, StringName)
+ GDVIRTUAL1RC(String, _get_resource_type, String)
+ GDVIRTUAL1RC(ResourceUID::ID, _get_resource_uid, String)
+ GDVIRTUAL2RC(Vector<String>, _get_dependencies, String, bool)
+ GDVIRTUAL2RC(int64_t, _rename_dependencies, String, Dictionary)
+ GDVIRTUAL1RC(bool, _exists, String)
+
+ GDVIRTUAL4RC(Variant, _load, String, String, bool, int)
+
public:
virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual bool exists(const String &p_path) const;
@@ -56,6 +68,7 @@ public:
virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
+ virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map);
virtual bool is_import_valid(const String &p_path) const { return true; }
@@ -107,7 +120,7 @@ private:
friend class ResourceFormatImporter;
friend class ResourceInteractiveLoader;
- //internal load function
+ // Internal load function.
static RES _load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress);
static ResourceLoadedCallback _loaded_callback;
@@ -157,6 +170,7 @@ public:
static void add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front = false);
static void remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader);
static String get_resource_type(const String &p_path);
+ static ResourceUID::ID get_resource_uid(const String &p_path);
static void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
static Error rename_dependencies(const String &p_path, const Map<String, String> &p_map);
static bool is_import_valid(const String &p_path);
diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp
index 7ebc7f34b3..823b5f75b1 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -30,55 +30,49 @@
#include "resource_saver.h"
#include "core/config/project_settings.h"
+#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
#include "core/object/script_language.h"
-#include "core/os/file_access.h"
Ref<ResourceFormatSaver> ResourceSaver::saver[MAX_SAVERS];
int ResourceSaver::saver_count = 0;
bool ResourceSaver::timestamp_on_save = false;
ResourceSavedCallback ResourceSaver::save_callback = nullptr;
+ResourceSaverGetResourceIDForPath ResourceSaver::save_get_id_for_path = nullptr;
Error ResourceFormatSaver::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
- if (get_script_instance() && get_script_instance()->has_method("save")) {
- return (Error)get_script_instance()->call("save", p_path, p_resource, p_flags).operator int64_t();
+ int64_t res;
+ if (GDVIRTUAL_CALL(_save, p_path, p_resource, p_flags, res)) {
+ return (Error)res;
}
return ERR_METHOD_NOT_FOUND;
}
bool ResourceFormatSaver::recognize(const RES &p_resource) const {
- if (get_script_instance() && get_script_instance()->has_method("recognize")) {
- return get_script_instance()->call("recognize", p_resource);
+ bool success;
+ if (GDVIRTUAL_CALL(_recognize, p_resource, success)) {
+ return success;
}
return false;
}
void ResourceFormatSaver::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const {
- if (get_script_instance() && get_script_instance()->has_method("get_recognized_extensions")) {
- PackedStringArray exts = get_script_instance()->call("get_recognized_extensions", p_resource);
-
- {
- const String *r = exts.ptr();
- for (int i = 0; i < exts.size(); ++i) {
- p_extensions->push_back(r[i]);
- }
+ PackedStringArray exts;
+ if (GDVIRTUAL_CALL(_get_recognized_extensions, p_resource, exts)) {
+ const String *r = exts.ptr();
+ for (int i = 0; i < exts.size(); ++i) {
+ p_extensions->push_back(r[i]);
}
}
}
void ResourceFormatSaver::_bind_methods() {
- {
- PropertyInfo arg0 = PropertyInfo(Variant::STRING, "path");
- PropertyInfo arg1 = PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource");
- PropertyInfo arg2 = PropertyInfo(Variant::INT, "flags");
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::INT, "save", arg0, arg1, arg2));
- }
-
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::PACKED_STRING_ARRAY, "get_recognized_extensions", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "recognize", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
+ GDVIRTUAL_BIND(_save, "path", "resource", "flags");
+ GDVIRTUAL_BIND(_recognize, "resource");
+ GDVIRTUAL_BIND(_get_recognized_extensions, "resource");
}
Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
@@ -94,8 +88,8 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t
bool recognized = false;
saver[i]->get_recognized_extensions(p_resource, &extensions);
- for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
- if (E->get().nocasecmp_to(extension) == 0) {
+ for (const String &E : extensions) {
+ if (E.nocasecmp_to(extension) == 0) {
recognized = true;
}
}
@@ -210,7 +204,7 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) {
bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatSaver");
ERR_FAIL_COND_V_MSG(!valid_type, false, "Script does not inherit a CustomResourceSaver: " + script_path + ".");
- Object *obj = ClassDB::instance(ibt);
+ Object *obj = ClassDB::instantiate(ibt);
ERR_FAIL_COND_V_MSG(obj == nullptr, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + ".");
@@ -236,8 +230,7 @@ void ResourceSaver::add_custom_savers() {
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
- for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
- StringName class_name = E->get();
+ for (const StringName &class_name : global_classes) {
StringName base_class = ScriptServer::get_global_class_native_base(class_name);
if (base_class == custom_saver_base_class) {
@@ -259,3 +252,14 @@ void ResourceSaver::remove_custom_savers() {
remove_resource_format_saver(custom_savers[i]);
}
}
+
+ResourceUID::ID ResourceSaver::get_resource_id_for_path(const String &p_path, bool p_generate) {
+ if (save_get_id_for_path) {
+ return save_get_id_for_path(p_path, p_generate);
+ }
+ return ResourceUID::INVALID_ID;
+}
+
+void ResourceSaver::set_get_resource_id_for_path(ResourceSaverGetResourceIDForPath p_callback) {
+ save_get_id_for_path = p_callback;
+}
diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h
index 2c9e8f1aa3..fcde835dab 100644
--- a/core/io/resource_saver.h
+++ b/core/io/resource_saver.h
@@ -32,13 +32,19 @@
#define RESOURCE_SAVER_H
#include "core/io/resource.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
-class ResourceFormatSaver : public Reference {
- GDCLASS(ResourceFormatSaver, Reference);
+class ResourceFormatSaver : public RefCounted {
+ GDCLASS(ResourceFormatSaver, RefCounted);
protected:
static void _bind_methods();
+ GDVIRTUAL3R(int64_t, _save, String, RES, uint32_t)
+ GDVIRTUAL1RC(bool, _recognize, RES)
+ GDVIRTUAL1RC(Vector<String>, _get_recognized_extensions, RES)
+
public:
virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
virtual bool recognize(const RES &p_resource) const;
@@ -48,6 +54,7 @@ public:
};
typedef void (*ResourceSavedCallback)(Ref<Resource> p_resource, const String &p_path);
+typedef ResourceUID::ID (*ResourceSaverGetResourceIDForPath)(const String &p_path, bool p_generate);
class ResourceSaver {
enum {
@@ -58,6 +65,7 @@ class ResourceSaver {
static int saver_count;
static bool timestamp_on_save;
static ResourceSavedCallback save_callback;
+ static ResourceSaverGetResourceIDForPath save_get_id_for_path;
static Ref<ResourceFormatSaver> _find_custom_resource_format_saver(String path);
@@ -80,7 +88,10 @@ public:
static void set_timestamp_on_save(bool p_timestamp) { timestamp_on_save = p_timestamp; }
static bool get_timestamp_on_save() { return timestamp_on_save; }
+ static ResourceUID::ID get_resource_id_for_path(const String &p_path, bool p_generate = false);
+
static void set_save_callback(ResourceSavedCallback p_callback);
+ static void set_get_resource_id_for_path(ResourceSaverGetResourceIDForPath p_callback);
static bool add_custom_resource_format_saver(String script_path);
static void remove_custom_resource_format_saver(String script_path);
diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp
new file mode 100644
index 0000000000..97d683f415
--- /dev/null
+++ b/core/io/resource_uid.cpp
@@ -0,0 +1,262 @@
+/*************************************************************************/
+/* resource_uid.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "resource_uid.h"
+#include "core/crypto/crypto.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
+
+static constexpr uint32_t char_count = ('z' - 'a');
+static constexpr uint32_t base = char_count + ('9' - '0');
+
+const char *ResourceUID::CACHE_FILE = "res://.godot/uid_cache.bin";
+
+String ResourceUID::id_to_text(ID p_id) const {
+ if (p_id < 0) {
+ return "uid://<invalid>";
+ }
+ String txt;
+
+ while (p_id) {
+ uint32_t c = p_id % base;
+ if (c < char_count) {
+ txt = String::chr('a' + c) + txt;
+ } else {
+ txt = String::chr('0' + (c - char_count)) + txt;
+ }
+ p_id /= base;
+ }
+
+ return "uid://" + txt;
+}
+
+ResourceUID::ID ResourceUID::text_to_id(const String &p_text) const {
+ if (!p_text.begins_with("uid://") || p_text == "uid://<invalid>") {
+ return INVALID_ID;
+ }
+
+ uint32_t l = p_text.length();
+ uint64_t uid = 0;
+ for (uint32_t i = 6; i < l; i++) {
+ uid *= base;
+ uint32_t c = p_text[i];
+ if (c >= 'a' && c <= 'z') {
+ uid += c - 'a';
+ } else if (c >= '0' && c <= '9') {
+ uid += c - '0' + char_count;
+ } else {
+ return INVALID_ID;
+ }
+ }
+ return ID(uid & 0x7FFFFFFFFFFFFFFF);
+}
+
+ResourceUID::ID ResourceUID::create_id() const {
+ mutex.lock();
+ if (crypto.is_null()) {
+ crypto = Ref<Crypto>(Crypto::create());
+ }
+ mutex.unlock();
+ while (true) {
+ PackedByteArray bytes = crypto->generate_random_bytes(8);
+ ERR_FAIL_COND_V(bytes.size() != 8, INVALID_ID);
+ const uint64_t *ptr64 = (const uint64_t *)bytes.ptr();
+ ID id = int64_t((*ptr64) & 0x7FFFFFFFFFFFFFFF);
+ mutex.lock();
+ bool exists = unique_ids.has(id);
+ mutex.unlock();
+ if (!exists) {
+ return id;
+ }
+ }
+}
+
+bool ResourceUID::has_id(ID p_id) const {
+ MutexLock l(mutex);
+ return unique_ids.has(p_id);
+}
+void ResourceUID::add_id(ID p_id, const String &p_path) {
+ MutexLock l(mutex);
+ ERR_FAIL_COND(unique_ids.has(p_id));
+ Cache c;
+ c.cs = p_path.utf8();
+ unique_ids[p_id] = c;
+ changed = true;
+}
+
+void ResourceUID::set_id(ID p_id, const String &p_path) {
+ MutexLock l(mutex);
+ ERR_FAIL_COND(!unique_ids.has(p_id));
+ CharString cs = p_path.utf8();
+ if (strcmp(cs.ptr(), unique_ids[p_id].cs.ptr()) != 0) {
+ unique_ids[p_id].cs = cs;
+ unique_ids[p_id].saved_to_cache = false; //changed
+ changed = true;
+ }
+}
+
+String ResourceUID::get_id_path(ID p_id) const {
+ MutexLock l(mutex);
+ ERR_FAIL_COND_V(!unique_ids.has(p_id), String());
+ const CharString &cs = unique_ids[p_id].cs;
+ String s(cs.ptr());
+ return s;
+}
+void ResourceUID::remove_id(ID p_id) {
+ MutexLock l(mutex);
+ ERR_FAIL_COND(!unique_ids.has(p_id));
+ unique_ids.erase(p_id);
+}
+
+Error ResourceUID::save_to_cache() {
+ if (!FileAccess::exists(CACHE_FILE)) {
+ DirAccessRef d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ d->make_dir_recursive(String(CACHE_FILE).get_base_dir()); //ensure base dir exists
+ }
+
+ FileAccessRef f = FileAccess::open(CACHE_FILE, FileAccess::WRITE);
+ if (!f) {
+ return ERR_CANT_OPEN;
+ }
+
+ MutexLock l(mutex);
+ f->store_32(unique_ids.size());
+
+ cache_entries = 0;
+
+ for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) {
+ f->store_64(E.key());
+ uint32_t s = E.get().cs.length();
+ f->store_32(s);
+ f->store_buffer((const uint8_t *)E.get().cs.ptr(), s);
+ E.get().saved_to_cache = true;
+ cache_entries++;
+ }
+
+ changed = false;
+ return OK;
+}
+
+Error ResourceUID::load_from_cache() {
+ FileAccessRef f = FileAccess::open(CACHE_FILE, FileAccess::READ);
+ if (!f) {
+ return ERR_CANT_OPEN;
+ }
+
+ MutexLock l(mutex);
+ unique_ids.clear();
+
+ uint32_t entry_count = f->get_32();
+ for (uint32_t i = 0; i < entry_count; i++) {
+ int64_t id = f->get_64();
+ int32_t len = f->get_32();
+ Cache c;
+ c.cs.resize(len + 1);
+ ERR_FAIL_COND_V(c.cs.size() != len + 1, ERR_FILE_CORRUPT); // out of memory
+ c.cs[len] = 0;
+ int32_t rl = f->get_buffer((uint8_t *)c.cs.ptrw(), len);
+ ERR_FAIL_COND_V(rl != len, ERR_FILE_CORRUPT);
+
+ c.saved_to_cache = true;
+ unique_ids[id] = c;
+ }
+
+ cache_entries = entry_count;
+ changed = false;
+ return OK;
+}
+
+Error ResourceUID::update_cache() {
+ if (!changed) {
+ return OK;
+ }
+
+ if (cache_entries == 0) {
+ return save_to_cache();
+ }
+ MutexLock l(mutex);
+
+ FileAccess *f = nullptr;
+ for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) {
+ if (!E.get().saved_to_cache) {
+ if (f == nullptr) {
+ f = FileAccess::open(CACHE_FILE, FileAccess::READ_WRITE); //append
+ if (!f) {
+ return ERR_CANT_OPEN;
+ }
+ f->seek_end();
+ }
+ f->store_64(E.key());
+ uint32_t s = E.get().cs.length();
+ f->store_32(s);
+ f->store_buffer((const uint8_t *)E.get().cs.ptr(), s);
+ E.get().saved_to_cache = true;
+ cache_entries++;
+ }
+ }
+
+ if (f != nullptr) {
+ f->seek(0);
+ f->store_32(cache_entries); //update amount of entries
+ f->close();
+ memdelete(f);
+ }
+
+ changed = false;
+
+ return OK;
+}
+
+void ResourceUID::clear() {
+ cache_entries = 0;
+ unique_ids.clear();
+ changed = false;
+}
+void ResourceUID::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("id_to_text", "id"), &ResourceUID::id_to_text);
+ ClassDB::bind_method(D_METHOD("text_to_id", "text_id"), &ResourceUID::text_to_id);
+
+ ClassDB::bind_method(D_METHOD("create_id"), &ResourceUID::create_id);
+
+ ClassDB::bind_method(D_METHOD("has_id", "id"), &ResourceUID::has_id);
+ ClassDB::bind_method(D_METHOD("add_id", "id", "path"), &ResourceUID::add_id);
+ ClassDB::bind_method(D_METHOD("set_id", "id", "path"), &ResourceUID::set_id);
+ ClassDB::bind_method(D_METHOD("get_id_path", "id"), &ResourceUID::get_id_path);
+ ClassDB::bind_method(D_METHOD("remove_id", "id"), &ResourceUID::remove_id);
+
+ BIND_CONSTANT(INVALID_ID)
+}
+ResourceUID *ResourceUID::singleton = nullptr;
+ResourceUID::ResourceUID() {
+ ERR_FAIL_COND(singleton != nullptr);
+ singleton = this;
+}
+ResourceUID::~ResourceUID() {
+}
diff --git a/core/io/resource_uid.h b/core/io/resource_uid.h
new file mode 100644
index 0000000000..b12138425a
--- /dev/null
+++ b/core/io/resource_uid.h
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* resource_uid.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RESOURCE_UUID_H
+#define RESOURCE_UUID_H
+
+#include "core/object/ref_counted.h"
+#include "core/string/string_name.h"
+#include "core/templates/ordered_hash_map.h"
+
+class Crypto;
+class ResourceUID : public Object {
+ GDCLASS(ResourceUID, Object)
+public:
+ typedef int64_t ID;
+ enum {
+ INVALID_ID = -1
+ };
+
+ static const char *CACHE_FILE;
+
+private:
+ mutable Ref<Crypto> crypto;
+ Mutex mutex;
+ struct Cache {
+ CharString cs;
+ bool saved_to_cache = false;
+ };
+
+ OrderedHashMap<ID, Cache> unique_ids; //unique IDs and utf8 paths (less memory used)
+ static ResourceUID *singleton;
+
+ uint32_t cache_entries = 0;
+ bool changed = false;
+
+protected:
+ static void _bind_methods();
+
+public:
+ String id_to_text(ID p_id) const;
+ ID text_to_id(const String &p_text) const;
+
+ ID create_id() const;
+ bool has_id(ID p_id) const;
+ void add_id(ID p_id, const String &p_path);
+ void set_id(ID p_id, const String &p_path);
+ String get_id_path(ID p_id) const;
+ void remove_id(ID p_id);
+
+ Error load_from_cache();
+ Error save_to_cache();
+ Error update_cache();
+
+ void clear();
+
+ static ResourceUID *get_singleton() { return singleton; }
+
+ ResourceUID();
+ ~ResourceUID();
+};
+
+#endif // RESOURCEUUID_H
diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp
index 74154321b3..27f8d4e88f 100644
--- a/core/io/stream_peer.cpp
+++ b/core/io/stream_peer.cpp
@@ -108,8 +108,8 @@ Array StreamPeer::_get_partial_data(int p_bytes) {
return ret;
}
-void StreamPeer::set_big_endian(bool p_enable) {
- big_endian = p_enable;
+void StreamPeer::set_big_endian(bool p_big_endian) {
+ big_endian = p_big_endian;
}
bool StreamPeer::is_big_endian_enabled() const {
@@ -512,7 +512,7 @@ void StreamPeerBuffer::clear() {
Ref<StreamPeerBuffer> StreamPeerBuffer::duplicate() const {
Ref<StreamPeerBuffer> spb;
- spb.instance();
+ spb.instantiate();
spb->data = data;
return spb;
}
diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h
index dadedbd2dc..effc3850af 100644
--- a/core/io/stream_peer.h
+++ b/core/io/stream_peer.h
@@ -31,10 +31,10 @@
#ifndef STREAM_PEER_H
#define STREAM_PEER_H
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
-class StreamPeer : public Reference {
- GDCLASS(StreamPeer, Reference);
+class StreamPeer : public RefCounted {
+ GDCLASS(StreamPeer, RefCounted);
OBJ_CATEGORY("Networking");
protected:
@@ -58,7 +58,7 @@ public:
virtual int get_available_bytes() const = 0;
- void set_big_endian(bool p_enable);
+ void set_big_endian(bool p_big_endian);
bool is_big_endian_enabled() const;
void put_8(int8_t p_val);
diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp
index b760a9ef80..5e0c0390f9 100644
--- a/core/io/tcp_server.cpp
+++ b/core/io/tcp_server.cpp
@@ -43,7 +43,6 @@ Error TCPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive).");
Error err;
IP::Type ip_type = IP::TYPE_ANY;
diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h
index abefa53c6f..10985a04d5 100644
--- a/core/io/tcp_server.h
+++ b/core/io/tcp_server.h
@@ -36,8 +36,8 @@
#include "core/io/stream_peer.h"
#include "core/io/stream_peer_tcp.h"
-class TCPServer : public Reference {
- GDCLASS(TCPServer, Reference);
+class TCPServer : public RefCounted {
+ GDCLASS(TCPServer, RefCounted);
protected:
enum {
diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp
index 9adf912224..83d575cee8 100644
--- a/core/io/translation_loader_po.cpp
+++ b/core/io/translation_loader_po.cpp
@@ -30,7 +30,7 @@
#include "translation_loader_po.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include "core/string/translation.h"
#include "core/string/translation_po.h"
diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h
index 36d33fcac3..c52820e60d 100644
--- a/core/io/translation_loader_po.h
+++ b/core/io/translation_loader_po.h
@@ -31,8 +31,8 @@
#ifndef TRANSLATION_LOADER_PO_H
#define TRANSLATION_LOADER_PO_H
+#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
-#include "core/os/file_access.h"
#include "core/string/translation.h"
class TranslationLoaderPO : public ResourceFormatLoader {
diff --git a/core/io/udp_server.cpp b/core/io/udp_server.cpp
index 6a1af0c2a9..27a1cab721 100644
--- a/core/io/udp_server.cpp
+++ b/core/io/udp_server.cpp
@@ -91,7 +91,6 @@ Error UDPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive).");
Error err;
IP::Type ip_type = IP::TYPE_ANY;
diff --git a/core/io/udp_server.h b/core/io/udp_server.h
index 60d03f37f0..e49a559c51 100644
--- a/core/io/udp_server.h
+++ b/core/io/udp_server.h
@@ -34,8 +34,8 @@
#include "core/io/net_socket.h"
#include "core/io/packet_peer_udp.h"
-class UDPServer : public Reference {
- GDCLASS(UDPServer, Reference);
+class UDPServer : public RefCounted {
+ GDCLASS(UDPServer, RefCounted);
protected:
enum {
diff --git a/core/io/xml_parser.h b/core/io/xml_parser.h
index 847edf958d..1113cce715 100644
--- a/core/io/xml_parser.h
+++ b/core/io/xml_parser.h
@@ -31,8 +31,8 @@
#ifndef XML_PARSER_H
#define XML_PARSER_H
-#include "core/object/reference.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
+#include "core/object/ref_counted.h"
#include "core/string/ustring.h"
#include "core/templates/vector.h"
@@ -40,8 +40,8 @@
Based on irrXML (see their zlib license). Added mainly for compatibility with their Collada loader.
*/
-class XMLParser : public Reference {
- GDCLASS(XMLParser, Reference);
+class XMLParser : public RefCounted {
+ GDCLASS(XMLParser, RefCounted);
public:
//! Enumeration of all supported source text file formats
@@ -80,7 +80,6 @@ private:
Vector<Attribute> attributes;
- String _replace_special_characters(const String &origstr);
bool _set_text(char *start, char *end);
void _parse_closing_xml_element();
void _ignore_definition();
diff --git a/core/io/zip_io.h b/core/io/zip_io.h
index 52691c65e9..776473bfa1 100644
--- a/core/io/zip_io.h
+++ b/core/io/zip_io.h
@@ -31,7 +31,7 @@
#ifndef ZIP_IO_H
#define ZIP_IO_H
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
// Not directly used in this header, but assumed available in downstream users
// like platform/*/export/export.cpp. Could be fixed, but probably better to have
diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp
index 88e11a630c..b380860522 100644
--- a/core/math/a_star.cpp
+++ b/core/math/a_star.cpp
@@ -35,18 +35,12 @@
#include "scene/scene_string_names.h"
int AStar::get_available_point_id() const {
- if (points.is_empty()) {
- return 1;
- }
-
- // calculate our new next available point id if bigger than before or next id already contained in set of points.
if (points.has(last_free_id)) {
- int cur_new_id = last_free_id;
+ int cur_new_id = last_free_id + 1;
while (points.has(cur_new_id)) {
cur_new_id++;
}
- int &non_const = const_cast<int &>(last_free_id);
- non_const = cur_new_id;
+ const_cast<int &>(last_free_id) = cur_new_id;
}
return last_free_id;
@@ -388,8 +382,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
}
real_t AStar::_estimate_cost(int p_from_id, int p_to_id) {
- if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost)) {
- return get_script_instance()->call(SceneStringNames::get_singleton()->_estimate_cost, p_from_id, p_to_id);
+ real_t scost;
+ if (GDVIRTUAL_CALL(_estimate_cost, p_from_id, p_to_id, scost)) {
+ return scost;
}
Point *from_point;
@@ -404,8 +399,9 @@ real_t AStar::_estimate_cost(int p_from_id, int p_to_id) {
}
real_t AStar::_compute_cost(int p_from_id, int p_to_id) {
- if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost)) {
- return get_script_instance()->call(SceneStringNames::get_singleton()->_compute_cost, p_from_id, p_to_id);
+ real_t scost;
+ if (GDVIRTUAL_CALL(_compute_cost, p_from_id, p_to_id, scost)) {
+ return scost;
}
Point *from_point;
@@ -563,8 +559,8 @@ void AStar::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar::get_point_path);
ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar::get_id_path);
- BIND_VMETHOD(MethodInfo(Variant::FLOAT, "_estimate_cost", PropertyInfo(Variant::INT, "from_id"), PropertyInfo(Variant::INT, "to_id")));
- BIND_VMETHOD(MethodInfo(Variant::FLOAT, "_compute_cost", PropertyInfo(Variant::INT, "from_id"), PropertyInfo(Variant::INT, "to_id")));
+ GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id")
+ GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id")
}
AStar::~AStar() {
@@ -660,8 +656,9 @@ Vector2 AStar2D::get_closest_position_in_segment(const Vector2 &p_point) const {
}
real_t AStar2D::_estimate_cost(int p_from_id, int p_to_id) {
- if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost)) {
- return get_script_instance()->call(SceneStringNames::get_singleton()->_estimate_cost, p_from_id, p_to_id);
+ real_t scost;
+ if (GDVIRTUAL_CALL(_estimate_cost, p_from_id, p_to_id, scost)) {
+ return scost;
}
AStar::Point *from_point;
@@ -676,8 +673,9 @@ real_t AStar2D::_estimate_cost(int p_from_id, int p_to_id) {
}
real_t AStar2D::_compute_cost(int p_from_id, int p_to_id) {
- if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost)) {
- return get_script_instance()->call(SceneStringNames::get_singleton()->_compute_cost, p_from_id, p_to_id);
+ real_t scost;
+ if (GDVIRTUAL_CALL(_compute_cost, p_from_id, p_to_id, scost)) {
+ return scost;
}
AStar::Point *from_point;
@@ -881,6 +879,6 @@ void AStar2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar2D::get_point_path);
ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar2D::get_id_path);
- BIND_VMETHOD(MethodInfo(Variant::FLOAT, "_estimate_cost", PropertyInfo(Variant::INT, "from_id"), PropertyInfo(Variant::INT, "to_id")));
- BIND_VMETHOD(MethodInfo(Variant::FLOAT, "_compute_cost", PropertyInfo(Variant::INT, "from_id"), PropertyInfo(Variant::INT, "to_id")));
+ GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id")
+ GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id")
}
diff --git a/core/math/a_star.h b/core/math/a_star.h
index 4c61abd91c..64fa32a325 100644
--- a/core/math/a_star.h
+++ b/core/math/a_star.h
@@ -31,7 +31,9 @@
#ifndef A_STAR_H
#define A_STAR_H
-#include "core/object/reference.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/ref_counted.h"
+#include "core/object/script_language.h"
#include "core/templates/oa_hash_map.h"
/**
@@ -40,8 +42,8 @@
@author Juan Linietsky <reduzio@gmail.com>
*/
-class AStar : public Reference {
- GDCLASS(AStar, Reference);
+class AStar : public RefCounted {
+ GDCLASS(AStar, RefCounted);
friend class AStar2D;
struct Point {
@@ -122,6 +124,9 @@ protected:
virtual real_t _estimate_cost(int p_from_id, int p_to_id);
virtual real_t _compute_cost(int p_from_id, int p_to_id);
+ GDVIRTUAL2RC(real_t, _estimate_cost, int64_t, int64_t)
+ GDVIRTUAL2RC(real_t, _compute_cost, int64_t, int64_t)
+
public:
int get_available_point_id() const;
@@ -157,8 +162,8 @@ public:
~AStar();
};
-class AStar2D : public Reference {
- GDCLASS(AStar2D, Reference);
+class AStar2D : public RefCounted {
+ GDCLASS(AStar2D, RefCounted);
AStar astar;
bool _solve(AStar::Point *begin_point, AStar::Point *end_point);
@@ -169,6 +174,9 @@ protected:
virtual real_t _estimate_cost(int p_from_id, int p_to_id);
virtual real_t _compute_cost(int p_from_id, int p_to_id);
+ GDVIRTUAL2RC(real_t, _estimate_cost, int64_t, int64_t)
+ GDVIRTUAL2RC(real_t, _compute_cost, int64_t, int64_t)
+
public:
int get_available_point_id() const;
diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp
index 2c721997d8..33aa65f15d 100644
--- a/core/math/aabb.cpp
+++ b/core/math/aabb.cpp
@@ -392,5 +392,5 @@ Variant AABB::intersects_ray_bind(const Vector3 &p_from, const Vector3 &p_dir) c
}
AABB::operator String() const {
- return String() + position + " - " + size;
+ return "[P: " + position.operator String() + ", S: " + size + "]";
}
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index 037378b9d7..eec9caf149 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -345,12 +345,12 @@ void Basis::rotate(const Vector3 &p_euler) {
*this = rotated(p_euler);
}
-Basis Basis::rotated(const Quat &p_quat) const {
- return Basis(p_quat) * (*this);
+Basis Basis::rotated(const Quaternion &p_quaternion) const {
+ return Basis(p_quaternion) * (*this);
}
-void Basis::rotate(const Quat &p_quat) {
- *this = rotated(p_quat);
+void Basis::rotate(const Quaternion &p_quaternion) {
+ *this = rotated(p_quaternion);
}
Vector3 Basis::get_rotation_euler() const {
@@ -367,7 +367,7 @@ Vector3 Basis::get_rotation_euler() const {
return m.get_euler();
}
-Quat Basis::get_rotation_quat() const {
+Quaternion Basis::get_rotation_quaternion() const {
// Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S,
// and returns the Euler angles corresponding to the rotation part, complementing get_scale().
// See the comment in get_scale() for further information.
@@ -378,7 +378,19 @@ Quat Basis::get_rotation_quat() const {
m.scale(Vector3(-1, -1, -1));
}
- return m.get_quat();
+ return m.get_quaternion();
+}
+
+void Basis::rotate_to_align(Vector3 p_start_direction, Vector3 p_end_direction) {
+ // Takes two vectors and rotates the basis from the first vector to the second vector.
+ // Adopted from: https://gist.github.com/kevinmoran/b45980723e53edeb8a5a43c49f134724
+ const Vector3 axis = p_start_direction.cross(p_end_direction).normalized();
+ if (axis.length_squared() != 0) {
+ real_t dot = p_start_direction.dot(p_end_direction);
+ dot = CLAMP(dot, -1.0, 1.0);
+ const real_t angle_rads = Math::acos(dot);
+ set_axis_angle(axis, angle_rads);
+ }
}
void Basis::get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const {
@@ -756,23 +768,14 @@ bool Basis::operator!=(const Basis &p_matrix) const {
}
Basis::operator String() const {
- String mtx;
- for (int i = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++) {
- if (i != 0 || j != 0) {
- mtx += ", ";
- }
-
- mtx += rtos(elements[j][i]); //matrix is stored transposed for performance, so print it transposed
- }
- }
-
- return mtx;
+ return "[X: " + get_axis(0).operator String() +
+ ", Y: " + get_axis(1).operator String() +
+ ", Z: " + get_axis(2).operator String() + "]";
}
-Quat Basis::get_quat() const {
+Quaternion Basis::get_quaternion() const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!is_rotation(), Quat(), "Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quat() or call orthonormalized() instead.");
+ ERR_FAIL_COND_V_MSG(!is_rotation(), Quaternion(), "Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quaternion() or call orthonormalized() if the Basis contains linearly independent vectors.");
#endif
/* Allow getting a quaternion from an unnormalized transform */
Basis m = *this;
@@ -803,7 +806,7 @@ Quat Basis::get_quat() const {
temp[k] = (m.elements[k][i] + m.elements[i][k]) * s;
}
- return Quat(temp[0], temp[1], temp[2], temp[3]);
+ return Quaternion(temp[0], temp[1], temp[2], temp[3]);
}
static const Basis _ortho_bases[24] = {
@@ -945,13 +948,13 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
r_angle = angle;
}
-void Basis::set_quat(const Quat &p_quat) {
- real_t d = p_quat.length_squared();
+void Basis::set_quaternion(const Quaternion &p_quaternion) {
+ real_t d = p_quaternion.length_squared();
real_t s = 2.0 / d;
- real_t xs = p_quat.x * s, ys = p_quat.y * s, zs = p_quat.z * s;
- real_t wx = p_quat.w * xs, wy = p_quat.w * ys, wz = p_quat.w * zs;
- real_t xx = p_quat.x * xs, xy = p_quat.x * ys, xz = p_quat.x * zs;
- real_t yy = p_quat.y * ys, yz = p_quat.y * zs, zz = p_quat.z * zs;
+ real_t xs = p_quaternion.x * s, ys = p_quaternion.y * s, zs = p_quaternion.z * s;
+ real_t wx = p_quaternion.w * xs, wy = p_quaternion.w * ys, wz = p_quaternion.w * zs;
+ real_t xx = p_quaternion.x * xs, xy = p_quaternion.x * ys, xz = p_quaternion.x * zs;
+ real_t yy = p_quaternion.y * ys, yz = p_quaternion.y * zs, zz = p_quaternion.z * zs;
set(1.0 - (yy + zz), xy - wz, xz + wy,
xy + wz, 1.0 - (xx + zz), yz - wx,
xz - wy, yz + wx, 1.0 - (xx + yy));
@@ -997,9 +1000,9 @@ void Basis::set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale) {
rotate(p_euler);
}
-void Basis::set_quat_scale(const Quat &p_quat, const Vector3 &p_scale) {
+void Basis::set_quaternion_scale(const Quaternion &p_quaternion, const Vector3 &p_scale) {
set_diagonal(p_scale);
- rotate(p_quat);
+ rotate(p_quaternion);
}
void Basis::set_diagonal(const Vector3 &p_diag) {
@@ -1018,8 +1021,8 @@ void Basis::set_diagonal(const Vector3 &p_diag) {
Basis Basis::slerp(const Basis &p_to, const real_t &p_weight) const {
//consider scale
- Quat from(*this);
- Quat to(p_to);
+ Quaternion from(*this);
+ Quaternion to(p_to);
Basis b(from.slerp(to, p_weight));
b.elements[0] *= Math::lerp(elements[0].length(), p_to.elements[0].length(), p_weight);
@@ -1138,3 +1141,21 @@ void Basis::rotate_sh(real_t *p_values) {
p_values[7] = -d3;
p_values[8] = d4 * s_scale_dst4;
}
+
+Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up) {
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V_MSG(p_target.is_equal_approx(Vector3()), Basis(), "The target vector can't be zero.");
+ ERR_FAIL_COND_V_MSG(p_up.is_equal_approx(Vector3()), Basis(), "The up vector can't be zero.");
+#endif
+ Vector3 v_z = -p_target.normalized();
+ Vector3 v_x = p_up.cross(v_z);
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND_V_MSG(v_x.is_equal_approx(Vector3()), Basis(), "The target vector and up vector can't be parallel to each other.");
+#endif
+ v_x.normalize();
+ Vector3 v_y = v_z.cross(v_x);
+
+ Basis basis;
+ basis.set(v_x, v_y, v_z);
+ return basis;
+}
diff --git a/core/math/basis.h b/core/math/basis.h
index 56f6227313..9d8ed16e29 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -31,7 +31,7 @@
#ifndef BASIS_H
#define BASIS_H
-#include "core/math/quat.h"
+#include "core/math/quaternion.h"
#include "core/math/vector3.h"
class Basis {
@@ -79,15 +79,17 @@ public:
void rotate(const Vector3 &p_euler);
Basis rotated(const Vector3 &p_euler) const;
- void rotate(const Quat &p_quat);
- Basis rotated(const Quat &p_quat) const;
+ void rotate(const Quaternion &p_quaternion);
+ Basis rotated(const Quaternion &p_quaternion) const;
Vector3 get_rotation_euler() const;
void get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const;
void get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const;
- Quat get_rotation_quat() const;
+ Quaternion get_rotation_quaternion() const;
Vector3 get_rotation() const { return get_rotation_euler(); };
+ void rotate_to_align(Vector3 p_start_direction, Vector3 p_end_direction);
+
Vector3 rotref_posscale_decomposition(Basis &rotref) const;
Vector3 get_euler_xyz() const;
@@ -108,8 +110,8 @@ public:
Vector3 get_euler_zyx() const;
void set_euler_zyx(const Vector3 &p_euler);
- Quat get_quat() const;
- void set_quat(const Quat &p_quat);
+ Quaternion get_quaternion() const;
+ void set_quaternion(const Quaternion &p_quaternion);
Vector3 get_euler() const { return get_euler_yxz(); }
void set_euler(const Vector3 &p_euler) { set_euler_yxz(p_euler); }
@@ -132,7 +134,7 @@ public:
void set_axis_angle_scale(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale);
void set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale);
- void set_quat_scale(const Quat &p_quat, const Vector3 &p_scale);
+ void set_quaternion_scale(const Quaternion &p_quaternion, const Vector3 &p_scale);
// transposed dot products
_FORCE_INLINE_ real_t tdotx(const Vector3 &v) const {
@@ -158,8 +160,8 @@ public:
_FORCE_INLINE_ Basis operator+(const Basis &p_matrix) const;
_FORCE_INLINE_ void operator-=(const Basis &p_matrix);
_FORCE_INLINE_ Basis operator-(const Basis &p_matrix) const;
- _FORCE_INLINE_ void operator*=(real_t p_val);
- _FORCE_INLINE_ Basis operator*(real_t p_val) const;
+ _FORCE_INLINE_ void operator*=(const real_t p_val);
+ _FORCE_INLINE_ Basis operator*(const real_t p_val) const;
int get_orthogonal_index() const;
void set_orthogonal_index(int p_index);
@@ -240,10 +242,12 @@ public:
#endif
Basis diagonalize();
- operator Quat() const { return get_quat(); }
+ operator Quaternion() const { return get_quaternion(); }
+
+ static Basis looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0));
- Basis(const Quat &p_quat) { set_quat(p_quat); };
- Basis(const Quat &p_quat, const Vector3 &p_scale) { set_quat_scale(p_quat, p_scale); }
+ Basis(const Quaternion &p_quaternion) { set_quaternion(p_quaternion); };
+ Basis(const Quaternion &p_quaternion, const Vector3 &p_scale) { set_quaternion_scale(p_quaternion, p_scale); }
Basis(const Vector3 &p_euler) { set_euler(p_euler); }
Basis(const Vector3 &p_euler, const Vector3 &p_scale) { set_euler_scale(p_euler, p_scale); }
@@ -298,13 +302,13 @@ _FORCE_INLINE_ Basis Basis::operator-(const Basis &p_matrix) const {
return ret;
}
-_FORCE_INLINE_ void Basis::operator*=(real_t p_val) {
+_FORCE_INLINE_ void Basis::operator*=(const real_t p_val) {
elements[0] *= p_val;
elements[1] *= p_val;
elements[2] *= p_val;
}
-_FORCE_INLINE_ Basis Basis::operator*(real_t p_val) const {
+_FORCE_INLINE_ Basis Basis::operator*(const real_t p_val) const {
Basis ret(*this);
ret *= p_val;
return ret;
diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp
index 1066cf5e30..8066a59281 100644
--- a/core/math/camera_matrix.cpp
+++ b/core/math/camera_matrix.cpp
@@ -315,8 +315,8 @@ Vector2 CameraMatrix::get_far_plane_half_extents() const {
return Vector2(res.x, res.y);
}
-bool CameraMatrix::get_endpoints(const Transform &p_transform, Vector3 *p_8points) const {
- Vector<Plane> planes = get_projection_planes(Transform());
+bool CameraMatrix::get_endpoints(const Transform3D &p_transform, Vector3 *p_8points) const {
+ Vector<Plane> planes = get_projection_planes(Transform3D());
const Planes intersections[8][3] = {
{ PLANE_FAR, PLANE_LEFT, PLANE_TOP },
{ PLANE_FAR, PLANE_LEFT, PLANE_BOTTOM },
@@ -338,11 +338,11 @@ bool CameraMatrix::get_endpoints(const Transform &p_transform, Vector3 *p_8point
return true;
}
-Vector<Plane> CameraMatrix::get_projection_planes(const Transform &p_transform) const {
+Vector<Plane> CameraMatrix::get_projection_planes(const Transform3D &p_transform) const {
/** Fast Plane Extraction from combined modelview/projection matrices.
* References:
- * https://web.archive.org/web/20011221205252/http://www.markmorley.com/opengl/frustumculling.html
- * https://web.archive.org/web/20061020020112/http://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf
+ * https://web.archive.org/web/20011221205252/https://www.markmorley.com/opengl/frustumculling.html
+ * https://web.archive.org/web/20061020020112/https://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf
*/
Vector<Plane> planes;
@@ -707,8 +707,8 @@ void CameraMatrix::scale_translate_to_fit(const AABB &p_aabb) {
matrix[3][3] = 1;
}
-CameraMatrix::operator Transform() const {
- Transform tr;
+CameraMatrix::operator Transform3D() const {
+ Transform3D tr;
const real_t *m = &matrix[0][0];
tr.basis.elements[0][0] = m[0];
@@ -730,8 +730,8 @@ CameraMatrix::operator Transform() const {
return tr;
}
-CameraMatrix::CameraMatrix(const Transform &p_transform) {
- const Transform &tr = p_transform;
+CameraMatrix::CameraMatrix(const Transform3D &p_transform) {
+ const Transform3D &tr = p_transform;
real_t *m = &matrix[0][0];
m[0] = tr.basis.elements[0][0];
diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h
index 3f327d3bc4..786d46055a 100644
--- a/core/math/camera_matrix.h
+++ b/core/math/camera_matrix.h
@@ -32,7 +32,7 @@
#define CAMERA_MATRIX_H
#include "core/math/rect2.h"
-#include "core/math/transform.h"
+#include "core/math/transform_3d.h"
struct CameraMatrix {
enum Planes {
@@ -71,9 +71,9 @@ struct CameraMatrix {
real_t get_fov() const;
bool is_orthogonal() const;
- Vector<Plane> get_projection_planes(const Transform &p_transform) const;
+ Vector<Plane> get_projection_planes(const Transform3D &p_transform) const;
- bool get_endpoints(const Transform &p_transform, Vector3 *p_8points) const;
+ bool get_endpoints(const Transform3D &p_transform, Vector3 *p_8points) const;
Vector2 get_viewport_half_extents() const;
Vector2 get_far_plane_half_extents() const;
@@ -90,7 +90,7 @@ struct CameraMatrix {
void scale_translate_to_fit(const AABB &p_aabb);
void make_scale(const Vector3 &p_scale);
int get_pixels_per_meter(int p_for_pixel_width) const;
- operator Transform() const;
+ operator Transform3D() const;
void flip_y();
@@ -112,7 +112,7 @@ struct CameraMatrix {
float get_lod_multiplier() const;
CameraMatrix();
- CameraMatrix(const Transform &p_transform);
+ CameraMatrix(const Transform3D &p_transform);
~CameraMatrix();
};
diff --git a/core/math/color.cpp b/core/math/color.cpp
index 64abd6dd08..dc86cacf8f 100644
--- a/core/math/color.cpp
+++ b/core/math/color.cpp
@@ -211,6 +211,14 @@ bool Color::is_equal_approx(const Color &p_color) const {
return Math::is_equal_approx(r, p_color.r) && Math::is_equal_approx(g, p_color.g) && Math::is_equal_approx(b, p_color.b) && Math::is_equal_approx(a, p_color.a);
}
+Color Color::clamp(const Color &p_min, const Color &p_max) const {
+ return Color(
+ CLAMP(r, p_min.r, p_max.r),
+ CLAMP(g, p_min.g, p_max.g),
+ CLAMP(b, p_min.b, p_max.b),
+ CLAMP(a, p_min.a, p_max.a));
+}
+
void Color::invert() {
r = 1.0 - r;
g = 1.0 - g;
@@ -360,7 +368,7 @@ Color Color::named(const String &p_name) {
ERR_FAIL_V_MSG(Color(), "Invalid color name: " + p_name + ".");
return Color();
}
- return get_named_color(idx);
+ return named_colors[idx].color;
}
Color Color::named(const String &p_name, const Color &p_default) {
@@ -368,7 +376,7 @@ Color Color::named(const String &p_name, const Color &p_default) {
if (idx == -1) {
return p_default;
}
- return get_named_color(idx);
+ return named_colors[idx].color;
}
int Color::find_named_color(const String &p_name) {
@@ -401,10 +409,12 @@ int Color::get_named_color_count() {
}
String Color::get_named_color_name(int p_idx) {
+ ERR_FAIL_INDEX_V(p_idx, get_named_color_count(), "");
return named_colors[p_idx].name;
}
Color Color::get_named_color(int p_idx) {
+ ERR_FAIL_INDEX_V(p_idx, get_named_color_count(), Color());
return named_colors[p_idx].color;
}
@@ -458,7 +468,7 @@ Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const {
}
Color::operator String() const {
- return rtos(r) + ", " + rtos(g) + ", " + rtos(b) + ", " + rtos(a);
+ return "(" + String::num(r, 4) + ", " + String::num(g, 4) + ", " + String::num(b, 4) + ", " + String::num(a, 4) + ")";
}
Color Color::operator+(const Color &p_color) const {
diff --git a/core/math/color.h b/core/math/color.h
index e404d80c8a..a95dbf4f60 100644
--- a/core/math/color.h
+++ b/core/math/color.h
@@ -89,6 +89,7 @@ struct Color {
bool is_equal_approx(const Color &p_color) const;
+ Color clamp(const Color &p_min = Color(0, 0, 0, 0), const Color &p_max = Color(1, 1, 1, 1)) const;
void invert();
Color inverted() const;
diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp
index 682a7ea39e..f67035c803 100644
--- a/core/math/convex_hull.cpp
+++ b/core/math/convex_hull.cpp
@@ -2260,10 +2260,21 @@ Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry3
r_mesh.vertices = ch.vertices;
- r_mesh.edges.resize(ch.edges.size());
+ // Copy the edges over. There's two "half-edges" for every edge, so we pick only one of them.
+ r_mesh.edges.resize(ch.edges.size() / 2);
+ uint32_t edges_copied = 0;
for (uint32_t i = 0; i < ch.edges.size(); i++) {
- r_mesh.edges.write[i].a = (&ch.edges[i])->get_source_vertex();
- r_mesh.edges.write[i].b = (&ch.edges[i])->get_target_vertex();
+ uint32_t a = (&ch.edges[i])->get_source_vertex();
+ uint32_t b = (&ch.edges[i])->get_target_vertex();
+ if (a < b) { // Copy only the "canonical" edge. For the reverse edge, this will be false.
+ ERR_BREAK(edges_copied >= (uint32_t)r_mesh.edges.size());
+ r_mesh.edges.write[edges_copied].a = a;
+ r_mesh.edges.write[edges_copied].b = b;
+ edges_copied++;
+ }
+ }
+ if (edges_copied != (uint32_t)r_mesh.edges.size()) {
+ ERR_PRINT("Invalid edge count.");
}
r_mesh.faces.resize(ch.faces.size());
@@ -2278,9 +2289,18 @@ Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry3
e = e->get_next_edge_of_face();
} while (e != e_start);
+ // reverse indices: Godot wants clockwise, but this is counter-clockwise
+ if (face.indices.size() > 2) {
+ // reverse all but the first index.
+ int *indices = face.indices.ptrw();
+ for (int c = 0; c < (face.indices.size() - 1) / 2; c++) {
+ SWAP(indices[c + 1], indices[face.indices.size() - 1 - c]);
+ }
+ }
+
// compute normal
if (face.indices.size() >= 3) {
- face.plane = Plane(r_mesh.vertices[face.indices[0]], r_mesh.vertices[face.indices[2]], r_mesh.vertices[face.indices[1]]);
+ face.plane = Plane(r_mesh.vertices[face.indices[0]], r_mesh.vertices[face.indices[1]], r_mesh.vertices[face.indices[2]]);
} else {
WARN_PRINT("Too few vertices per face.");
}
diff --git a/core/math/convex_hull.h b/core/math/convex_hull.h
index ba7be9c5e8..806c6cc3fb 100644
--- a/core/math/convex_hull.h
+++ b/core/math/convex_hull.h
@@ -49,7 +49,7 @@ subject to the following restrictions:
#include "core/templates/vector.h"
/// Convex hull implementation based on Preparata and Hong
-/// See http://code.google.com/p/bullet/issues/detail?id=275
+/// See https://code.google.com/archive/p/bullet/issues/275
/// Ole Kniemeyer, MAXON Computer GmbH
class ConvexHullComputer {
public:
diff --git a/core/math/delaunay_3d.h b/core/math/delaunay_3d.h
index 25cc1125db..81adf4d19a 100644
--- a/core/math/delaunay_3d.h
+++ b/core/math/delaunay_3d.h
@@ -31,10 +31,10 @@
#ifndef DELAUNAY_3D_H
#define DELAUNAY_3D_H
+#include "core/io/file_access.h"
#include "core/math/aabb.h"
#include "core/math/camera_matrix.h"
#include "core/math/vector3.h"
-#include "core/os/file_access.h"
#include "core/string/print_string.h"
#include "core/templates/local_vector.h"
#include "core/templates/oa_hash_map.h"
@@ -375,8 +375,7 @@ public:
OutputSimplex *ret_simplicesw = ret_simplices.ptrw();
uint32_t simplices_written = 0;
- for (List<Simplex *>::Element *E = simplex_list.front(); E; E = E->next()) {
- Simplex *simplex = E->get();
+ for (Simplex *simplex : simplex_list) {
bool invalid = false;
for (int j = 0; j < 4; j++) {
if (simplex->points[j] >= point_count) {
diff --git a/core/math/dynamic_bvh.cpp b/core/math/dynamic_bvh.cpp
index 8e596f0f9d..f3fb473981 100644
--- a/core/math/dynamic_bvh.cpp
+++ b/core/math/dynamic_bvh.cpp
@@ -181,7 +181,7 @@ DynamicBVH::Volume DynamicBVH::_bounds(Node **leaves, int p_count) {
void DynamicBVH::_bottom_up(Node **leaves, int p_count) {
while (p_count > 1) {
- real_t minsize = Math_INF;
+ real_t minsize = INFINITY;
int minidx[2] = { -1, -1 };
for (int i = 0; i < p_count; ++i) {
for (int j = i + 1; j < p_count; ++j) {
diff --git a/core/math/expression.cpp b/core/math/expression.cpp
index f7ac44d321..05f2c8dac9 100644
--- a/core/math/expression.cpp
+++ b/core/math/expression.cpp
@@ -33,7 +33,7 @@
#include "core/io/marshalls.h"
#include "core/math/math_funcs.h"
#include "core/object/class_db.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
#include "core/os/os.h"
#include "core/variant/variant_parser.h"
@@ -397,10 +397,10 @@ Error Expression::_get_token(Token &r_token) {
r_token.value = Math_TAU;
} else if (id == "INF") {
r_token.type = TK_CONSTANT;
- r_token.value = Math_INF;
+ r_token.value = INFINITY;
} else if (id == "NAN") {
r_token.type = TK_CONSTANT;
- r_token.value = Math_NAN;
+ r_token.value = NAN;
} else if (id == "not") {
r_token.type = TK_OP_NOT;
} else if (id == "or") {
diff --git a/core/math/expression.h b/core/math/expression.h
index a6b288ed6e..aecf662d0a 100644
--- a/core/math/expression.h
+++ b/core/math/expression.h
@@ -31,10 +31,10 @@
#ifndef EXPRESSION_H
#define EXPRESSION_H
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
-class Expression : public Reference {
- GDCLASS(Expression, Reference);
+class Expression : public RefCounted {
+ GDCLASS(Expression, RefCounted);
private:
struct Input {
diff --git a/core/math/face3.cpp b/core/math/face3.cpp
index 20c316c322..9af3f868d2 100644
--- a/core/math/face3.cpp
+++ b/core/math/face3.cpp
@@ -230,7 +230,7 @@ bool Face3::intersects_aabb(const AABB &p_aabb) const {
real_t minA, maxA, minB, maxB;
p_aabb.project_range_in_plane(Plane(axis, 0), minA, maxA);
- project_range(axis, Transform(), minB, maxB);
+ project_range(axis, Transform3D(), minB, maxB);
if (maxA < minB || maxB < minA) {
return false;
@@ -244,7 +244,7 @@ Face3::operator String() const {
return String() + vertex[0] + ", " + vertex[1] + ", " + vertex[2];
}
-void Face3::project_range(const Vector3 &p_normal, const Transform &p_transform, real_t &r_min, real_t &r_max) const {
+void Face3::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const {
for (int i = 0; i < 3; i++) {
Vector3 v = p_transform.xform(vertex[i]);
real_t d = p_normal.dot(v);
@@ -259,7 +259,7 @@ void Face3::project_range(const Vector3 &p_normal, const Transform &p_transform,
}
}
-void Face3::get_support(const Vector3 &p_normal, const Transform &p_transform, Vector3 *p_vertices, int *p_count, int p_max) const {
+void Face3::get_support(const Vector3 &p_normal, const Transform3D &p_transform, Vector3 *p_vertices, int *p_count, int p_max) const {
#define _FACE_IS_VALID_SUPPORT_THRESHOLD 0.98
#define _EDGE_IS_VALID_SUPPORT_THRESHOLD 0.05
diff --git a/core/math/face3.h b/core/math/face3.h
index 2e86b0a904..9e9026e54e 100644
--- a/core/math/face3.h
+++ b/core/math/face3.h
@@ -33,7 +33,7 @@
#include "core/math/aabb.h"
#include "core/math/plane.h"
-#include "core/math/transform.h"
+#include "core/math/transform_3d.h"
#include "core/math/vector3.h"
class Face3 {
@@ -50,8 +50,8 @@ public:
/**
*
* @param p_plane plane used to split the face
- * @param p_res array of at least 3 faces, amount used in functio return
- * @param p_is_point_over array of at least 3 booleans, determining which face is over the plane, amount used in functio return
+ * @param p_res array of at least 3 faces, amount used in function return
+ * @param p_is_point_over array of at least 3 booleans, determining which face is over the plane, amount used in function return
* @param _epsilon constant used for numerical error rounding, to add "thickness" to the plane (so coplanar points can happen)
* @return amount of faces generated by the split, either 0 (means no split possible), 2 or 3
*/
@@ -74,8 +74,8 @@ public:
ClockDirection get_clock_dir() const; ///< todo, test if this is returning the proper clockwisity
- void get_support(const Vector3 &p_normal, const Transform &p_transform, Vector3 *p_vertices, int *p_count, int p_max) const;
- void project_range(const Vector3 &p_normal, const Transform &p_transform, real_t &r_min, real_t &r_max) const;
+ void get_support(const Vector3 &p_normal, const Transform3D &p_transform, Vector3 *p_vertices, int *p_count, int p_max) const;
+ void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const;
AABB get_aabb() const {
AABB aabb(vertex[0], Vector3());
diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h
index 4958b5ac6a..8e5830f9b3 100644
--- a/core/math/geometry_2d.h
+++ b/core/math/geometry_2d.h
@@ -32,9 +32,8 @@
#define GEOMETRY_2D_H
#include "core/math/delaunay_2d.h"
-#include "core/math/rect2.h"
#include "core/math/triangulate.h"
-#include "core/object/object.h"
+#include "core/math/vector3i.h"
#include "core/templates/vector.h"
class Geometry2D {
@@ -183,7 +182,15 @@ public:
C = Vector2(C.x * Bn.x + C.y * Bn.y, C.y * Bn.x - C.x * Bn.y);
D = Vector2(D.x * Bn.x + D.y * Bn.y, D.y * Bn.x - D.x * Bn.y);
- if ((C.y < 0 && D.y < 0) || (C.y >= 0 && D.y >= 0)) {
+ // Fail if C x B and D x B have the same sign (segments don't intersect).
+ // (equivalent to condition (C.y < 0 && D.y < CMP_EPSILON) || (C.y > 0 && D.y > CMP_EPSILON))
+ if (C.y * D.y > CMP_EPSILON) {
+ return false;
+ }
+
+ // Fail if segments are parallel or colinear.
+ // (when A x B == zero, i.e (C - D) x B == zero, i.e C x B == D x B)
+ if (Math::is_equal_approx(C.y, D.y)) {
return false;
}
@@ -194,7 +201,7 @@ public:
return false;
}
- // (4) Apply the discovered position to line A-B in the original coordinate system.
+ // Apply the discovered position to line A-B in the original coordinate system.
if (r_result) {
*r_result = p_from_a + B * ABpos;
}
@@ -354,14 +361,33 @@ public:
for (int i = 0; i < c; i++) {
const Vector2 &v1 = p[i];
const Vector2 &v2 = p[(i + 1) % c];
- if (segment_intersects_segment(v1, v2, p_point, further_away, nullptr)) {
+
+ Vector2 res;
+ if (segment_intersects_segment(v1, v2, p_point, further_away, &res)) {
intersections++;
+ if (res.is_equal_approx(p_point)) {
+ // Point is in one of the polygon edges.
+ return true;
+ }
}
}
return (intersections & 1);
}
+ static bool is_segment_intersecting_polygon(const Vector2 &p_from, const Vector2 &p_to, const Vector<Vector2> &p_polygon) {
+ int c = p_polygon.size();
+ const Vector2 *p = p_polygon.ptr();
+ for (int i = 0; i < c; i++) {
+ const Vector2 &v1 = p[i];
+ const Vector2 &v2 = p[(i + 1) % c];
+ if (segment_intersects_segment(p_from, p_to, v1, v2, nullptr)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
static real_t vec2_cross(const Point2 &O, const Point2 &A, const Point2 &B) {
return (real_t)(A.x - O.x) * (B.y - O.y) - (real_t)(A.y - O.y) * (B.x - O.x);
}
diff --git a/core/math/geometry_3d.h b/core/math/geometry_3d.h
index 4ef9b4dbe6..766689e222 100644
--- a/core/math/geometry_3d.h
+++ b/core/math/geometry_3d.h
@@ -40,7 +40,7 @@ class Geometry3D {
public:
static void get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2, Vector3 &c1, Vector3 &c2) {
-// Do the function 'd' as defined by pb. I think is is dot product of some sort.
+// Do the function 'd' as defined by pb. I think it's a dot product of some sort.
#define d_of(m, n, o, p) ((m.x - n.x) * (o.x - p.x) + (m.y - n.y) * (o.y - p.y) + (m.z - n.z) * (o.z - p.z))
// Calculate the parametric position on the 2 curves, mua and mub.
diff --git a/core/math/math_defs.h b/core/math/math_defs.h
index df2223fb78..c3a8f910c0 100644
--- a/core/math/math_defs.h
+++ b/core/math/math_defs.h
@@ -43,8 +43,6 @@
#define Math_TAU 6.2831853071795864769252867666
#define Math_PI 3.1415926535897932384626433833
#define Math_E 2.7182818284590452353602874714
-#define Math_INF INFINITY
-#define Math_NAN NAN
#ifdef DEBUG_ENABLED
#define MATH_CHECKS
@@ -83,6 +81,26 @@ enum VAlign {
VALIGN_BOTTOM
};
+enum InlineAlign {
+ // Image alignment points.
+ INLINE_ALIGN_TOP_TO = 0b0000,
+ INLINE_ALIGN_CENTER_TO = 0b0001,
+ INLINE_ALIGN_BOTTOM_TO = 0b0010,
+ INLINE_ALIGN_IMAGE_MASK = 0b0011,
+
+ // Text alignment points.
+ INLINE_ALIGN_TO_TOP = 0b0000,
+ INLINE_ALIGN_TO_CENTER = 0b0100,
+ INLINE_ALIGN_TO_BASELINE = 0b1000,
+ INLINE_ALIGN_TO_BOTTOM = 0b1100,
+ INLINE_ALIGN_TEXT_MASK = 0b1100,
+
+ // Presets.
+ INLINE_ALIGN_TOP = INLINE_ALIGN_TOP_TO | INLINE_ALIGN_TO_TOP,
+ INLINE_ALIGN_CENTER = INLINE_ALIGN_CENTER_TO | INLINE_ALIGN_TO_CENTER,
+ INLINE_ALIGN_BOTTOM = INLINE_ALIGN_BOTTOM_TO | INLINE_ALIGN_TO_BOTTOM
+};
+
enum Side {
SIDE_LEFT,
SIDE_TOP,
diff --git a/core/math/math_fieldwise.cpp b/core/math/math_fieldwise.cpp
index 0985a727f2..570c57e254 100644
--- a/core/math/math_fieldwise.cpp
+++ b/core/math/math_fieldwise.cpp
@@ -88,8 +88,8 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const
return target;
}
- case Variant::QUAT: {
- SETUP_TYPE(Quat)
+ case Variant::QUATERNION: {
+ SETUP_TYPE(Quaternion)
/**/ TRY_TRANSFER_FIELD("x", x)
else TRY_TRANSFER_FIELD("y", y)
@@ -141,8 +141,8 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const
return target;
}
- case Variant::TRANSFORM: {
- SETUP_TYPE(Transform)
+ case Variant::TRANSFORM3D: {
+ SETUP_TYPE(Transform3D)
/**/ TRY_TRANSFER_FIELD("xx", basis.elements[0][0])
else TRY_TRANSFER_FIELD("xy", basis.elements[0][1])
diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp
index e92bb9f4aa..bbed257f60 100644
--- a/core/math/math_funcs.cpp
+++ b/core/math/math_funcs.cpp
@@ -88,16 +88,6 @@ int Math::range_step_decimals(double p_step) {
return step_decimals(p_step);
}
-double Math::dectime(double p_value, double p_amount, double p_step) {
- double sgn = p_value < 0 ? -1.0 : 1.0;
- double val = Math::abs(p_value);
- val -= p_amount * p_step;
- if (val < 0.0) {
- val = 0.0;
- }
- return val * sgn;
-}
-
double Math::ease(double p_x, double p_c) {
if (p_x < 0) {
p_x = 0;
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 3389407e72..4e4f566517 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -296,7 +296,6 @@ public:
static int step_decimals(double p_step);
static int range_step_decimals(double p_step);
static double snapped(double p_value, double p_step);
- static double dectime(double p_value, double p_amount, double p_step);
static uint32_t larger_prime(uint32_t p_val);
diff --git a/core/math/plane.cpp b/core/math/plane.cpp
index f1d3bbbd54..3c78b55b90 100644
--- a/core/math/plane.cpp
+++ b/core/math/plane.cpp
@@ -175,5 +175,5 @@ bool Plane::is_equal_approx(const Plane &p_plane) const {
}
Plane::operator String() const {
- return normal.operator String() + ", " + rtos(d);
+ return "[N: " + normal.operator String() + ", D: " + String::num_real(d, false) + "]";
}
diff --git a/core/math/quat.cpp b/core/math/quaternion.cpp
index 3982a0b993..3f1d2c58e5 100644
--- a/core/math/quat.cpp
+++ b/core/math/quaternion.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* quat.cpp */
+/* quaternion.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,16 +28,21 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "quat.h"
+#include "quaternion.h"
#include "core/math/basis.h"
#include "core/string/print_string.h"
+real_t Quaternion::angle_to(const Quaternion &p_to) const {
+ real_t d = dot(p_to);
+ return Math::acos(CLAMP(d * d * 2 - 1, -1, 1));
+}
+
// get_euler_xyz returns a vector containing the Euler angles in the format
// (ax,ay,az), where ax is the angle of rotation around x axis,
// and similar for other axes.
// This implementation uses XYZ convention (Z is the first rotation).
-Vector3 Quat::get_euler_xyz() const {
+Vector3 Quaternion::get_euler_xyz() const {
Basis m(*this);
return m.get_euler_xyz();
}
@@ -46,7 +51,7 @@ Vector3 Quat::get_euler_xyz() const {
// (ax,ay,az), where ax is the angle of rotation around x axis,
// and similar for other axes.
// This implementation uses YXZ convention (Z is the first rotation).
-Vector3 Quat::get_euler_yxz() const {
+Vector3 Quaternion::get_euler_yxz() const {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(!is_normalized(), Vector3(0, 0, 0), "The quaternion must be normalized.");
#endif
@@ -54,7 +59,7 @@ Vector3 Quat::get_euler_yxz() const {
return m.get_euler_yxz();
}
-void Quat::operator*=(const Quat &p_q) {
+void Quaternion::operator*=(const Quaternion &p_q) {
real_t xx = w * p_q.x + x * p_q.w + y * p_q.z - z * p_q.y;
real_t yy = w * p_q.y + y * p_q.w + z * p_q.x - x * p_q.z;
real_t zz = w * p_q.z + z * p_q.w + x * p_q.y - y * p_q.x;
@@ -64,45 +69,45 @@ void Quat::operator*=(const Quat &p_q) {
z = zz;
}
-Quat Quat::operator*(const Quat &p_q) const {
- Quat r = *this;
+Quaternion Quaternion::operator*(const Quaternion &p_q) const {
+ Quaternion r = *this;
r *= p_q;
return r;
}
-bool Quat::is_equal_approx(const Quat &p_quat) const {
- return Math::is_equal_approx(x, p_quat.x) && Math::is_equal_approx(y, p_quat.y) && Math::is_equal_approx(z, p_quat.z) && Math::is_equal_approx(w, p_quat.w);
+bool Quaternion::is_equal_approx(const Quaternion &p_quaternion) const {
+ return Math::is_equal_approx(x, p_quaternion.x) && Math::is_equal_approx(y, p_quaternion.y) && Math::is_equal_approx(z, p_quaternion.z) && Math::is_equal_approx(w, p_quaternion.w);
}
-real_t Quat::length() const {
+real_t Quaternion::length() const {
return Math::sqrt(length_squared());
}
-void Quat::normalize() {
+void Quaternion::normalize() {
*this /= length();
}
-Quat Quat::normalized() const {
+Quaternion Quaternion::normalized() const {
return *this / length();
}
-bool Quat::is_normalized() const {
+bool Quaternion::is_normalized() const {
return Math::is_equal_approx(length_squared(), 1, (real_t)UNIT_EPSILON); //use less epsilon
}
-Quat Quat::inverse() const {
+Quaternion Quaternion::inverse() const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!is_normalized(), Quat(), "The quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The quaternion must be normalized.");
#endif
- return Quat(-x, -y, -z, w);
+ return Quaternion(-x, -y, -z, w);
}
-Quat Quat::slerp(const Quat &p_to, const real_t &p_weight) const {
+Quaternion Quaternion::slerp(const Quaternion &p_to, const real_t &p_weight) const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!is_normalized(), Quat(), "The start quaternion must be normalized.");
- ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quat(), "The end quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
#endif
- Quat to1;
+ Quaternion to1;
real_t omega, cosom, sinom, scale0, scale1;
// calc cosine
@@ -137,19 +142,19 @@ Quat Quat::slerp(const Quat &p_to, const real_t &p_weight) const {
scale1 = p_weight;
}
// calculate final values
- return Quat(
+ return Quaternion(
scale0 * x + scale1 * to1.x,
scale0 * y + scale1 * to1.y,
scale0 * z + scale1 * to1.z,
scale0 * w + scale1 * to1.w);
}
-Quat Quat::slerpni(const Quat &p_to, const real_t &p_weight) const {
+Quaternion Quaternion::slerpni(const Quaternion &p_to, const real_t &p_weight) const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!is_normalized(), Quat(), "The start quaternion must be normalized.");
- ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quat(), "The end quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
#endif
- const Quat &from = *this;
+ const Quaternion &from = *this;
real_t dot = from.dot(p_to);
@@ -162,29 +167,29 @@ Quat Quat::slerpni(const Quat &p_to, const real_t &p_weight) const {
newFactor = Math::sin(p_weight * theta) * sinT,
invFactor = Math::sin((1.0 - p_weight) * theta) * sinT;
- return Quat(invFactor * from.x + newFactor * p_to.x,
+ return Quaternion(invFactor * from.x + newFactor * p_to.x,
invFactor * from.y + newFactor * p_to.y,
invFactor * from.z + newFactor * p_to.z,
invFactor * from.w + newFactor * p_to.w);
}
-Quat Quat::cubic_slerp(const Quat &p_b, const Quat &p_pre_a, const Quat &p_post_b, const real_t &p_weight) const {
+Quaternion Quaternion::cubic_slerp(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!is_normalized(), Quat(), "The start quaternion must be normalized.");
- ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quat(), "The end quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
#endif
//the only way to do slerp :|
real_t t2 = (1.0 - p_weight) * p_weight * 2;
- Quat sp = this->slerp(p_b, p_weight);
- Quat sq = p_pre_a.slerpni(p_post_b, p_weight);
+ Quaternion sp = this->slerp(p_b, p_weight);
+ Quaternion sq = p_pre_a.slerpni(p_post_b, p_weight);
return sp.slerpni(sq, t2);
}
-Quat::operator String() const {
- return String::num(x) + ", " + String::num(y) + ", " + String::num(z) + ", " + String::num(w);
+Quaternion::operator String() const {
+ return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ", " + String::num_real(w, false) + ")";
}
-Quat::Quat(const Vector3 &p_axis, real_t p_angle) {
+Quaternion::Quaternion(const Vector3 &p_axis, real_t p_angle) {
#ifdef MATH_CHECKS
ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 must be normalized.");
#endif
@@ -209,7 +214,7 @@ Quat::Quat(const Vector3 &p_axis, real_t p_angle) {
// (ax, ay, az), where ax is the angle of rotation around x axis,
// and similar for other axes.
// This implementation uses YXZ convention (Z is the first rotation).
-Quat::Quat(const Vector3 &p_euler) {
+Quaternion::Quaternion(const Vector3 &p_euler) {
real_t half_a1 = p_euler.y * 0.5;
real_t half_a2 = p_euler.x * 0.5;
real_t half_a3 = p_euler.z * 0.5;
diff --git a/core/math/quat.h b/core/math/quaternion.h
index d9b130c050..35324323b3 100644
--- a/core/math/quat.h
+++ b/core/math/quaternion.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* quat.h */
+/* quaternion.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,15 +28,15 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef QUAT_H
-#define QUAT_H
+#ifndef QUATERNION_H
+#define QUATERNION_H
#include "core/math/math_defs.h"
#include "core/math/math_funcs.h"
#include "core/math/vector3.h"
#include "core/string/ustring.h"
-class Quat {
+class Quaternion {
public:
union {
struct {
@@ -55,21 +55,22 @@ public:
return components[idx];
}
_FORCE_INLINE_ real_t length_squared() const;
- bool is_equal_approx(const Quat &p_quat) const;
+ bool is_equal_approx(const Quaternion &p_quaternion) const;
real_t length() const;
void normalize();
- Quat normalized() const;
+ Quaternion normalized() const;
bool is_normalized() const;
- Quat inverse() const;
- _FORCE_INLINE_ real_t dot(const Quat &p_q) const;
+ Quaternion inverse() const;
+ _FORCE_INLINE_ real_t dot(const Quaternion &p_q) const;
+ real_t angle_to(const Quaternion &p_to) const;
Vector3 get_euler_xyz() const;
Vector3 get_euler_yxz() const;
Vector3 get_euler() const { return get_euler_yxz(); };
- Quat slerp(const Quat &p_to, const real_t &p_weight) const;
- Quat slerpni(const Quat &p_to, const real_t &p_weight) const;
- Quat cubic_slerp(const Quat &p_b, const Quat &p_pre_a, const Quat &p_post_b, const real_t &p_weight) const;
+ Quaternion slerp(const Quaternion &p_to, const real_t &p_weight) const;
+ Quaternion slerpni(const Quaternion &p_to, const real_t &p_weight) const;
+ Quaternion cubic_slerp(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const;
_FORCE_INLINE_ void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
r_angle = 2 * Math::acos(w);
@@ -79,11 +80,11 @@ public:
r_axis.z = z * r;
}
- void operator*=(const Quat &p_q);
- Quat operator*(const Quat &p_q) const;
+ void operator*=(const Quaternion &p_q);
+ Quaternion operator*(const Quaternion &p_q) const;
- Quat operator*(const Vector3 &v) const {
- return Quat(w * v.x + y * v.z - z * v.y,
+ Quaternion operator*(const Vector3 &v) const {
+ return Quaternion(w * v.x + y * v.z - z * v.y,
w * v.y + z * v.x - x * v.z,
w * v.z + x * v.y - y * v.x,
-x * v.x - y * v.y - z * v.z);
@@ -102,42 +103,42 @@ public:
return inverse().xform(v);
}
- _FORCE_INLINE_ void operator+=(const Quat &p_q);
- _FORCE_INLINE_ void operator-=(const Quat &p_q);
+ _FORCE_INLINE_ void operator+=(const Quaternion &p_q);
+ _FORCE_INLINE_ void operator-=(const Quaternion &p_q);
_FORCE_INLINE_ void operator*=(const real_t &s);
_FORCE_INLINE_ void operator/=(const real_t &s);
- _FORCE_INLINE_ Quat operator+(const Quat &q2) const;
- _FORCE_INLINE_ Quat operator-(const Quat &q2) const;
- _FORCE_INLINE_ Quat operator-() const;
- _FORCE_INLINE_ Quat operator*(const real_t &s) const;
- _FORCE_INLINE_ Quat operator/(const real_t &s) const;
+ _FORCE_INLINE_ Quaternion operator+(const Quaternion &q2) const;
+ _FORCE_INLINE_ Quaternion operator-(const Quaternion &q2) const;
+ _FORCE_INLINE_ Quaternion operator-() const;
+ _FORCE_INLINE_ Quaternion operator*(const real_t &s) const;
+ _FORCE_INLINE_ Quaternion operator/(const real_t &s) const;
- _FORCE_INLINE_ bool operator==(const Quat &p_quat) const;
- _FORCE_INLINE_ bool operator!=(const Quat &p_quat) const;
+ _FORCE_INLINE_ bool operator==(const Quaternion &p_quaternion) const;
+ _FORCE_INLINE_ bool operator!=(const Quaternion &p_quaternion) const;
operator String() const;
- _FORCE_INLINE_ Quat() {}
+ _FORCE_INLINE_ Quaternion() {}
- _FORCE_INLINE_ Quat(real_t p_x, real_t p_y, real_t p_z, real_t p_w) :
+ _FORCE_INLINE_ Quaternion(real_t p_x, real_t p_y, real_t p_z, real_t p_w) :
x(p_x),
y(p_y),
z(p_z),
w(p_w) {
}
- Quat(const Vector3 &p_axis, real_t p_angle);
+ Quaternion(const Vector3 &p_axis, real_t p_angle);
- Quat(const Vector3 &p_euler);
+ Quaternion(const Vector3 &p_euler);
- Quat(const Quat &p_q) :
+ Quaternion(const Quaternion &p_q) :
x(p_q.x),
y(p_q.y),
z(p_q.z),
w(p_q.w) {
}
- Quat &operator=(const Quat &p_q) {
+ Quaternion &operator=(const Quaternion &p_q) {
x = p_q.x;
y = p_q.y;
z = p_q.z;
@@ -145,7 +146,7 @@ public:
return *this;
}
- Quat(const Vector3 &v0, const Vector3 &v1) // shortest arc
+ Quaternion(const Vector3 &v0, const Vector3 &v1) // shortest arc
{
Vector3 c = v0.cross(v1);
real_t d = v0.dot(v1);
@@ -167,72 +168,72 @@ public:
}
};
-real_t Quat::dot(const Quat &p_q) const {
+real_t Quaternion::dot(const Quaternion &p_q) const {
return x * p_q.x + y * p_q.y + z * p_q.z + w * p_q.w;
}
-real_t Quat::length_squared() const {
+real_t Quaternion::length_squared() const {
return dot(*this);
}
-void Quat::operator+=(const Quat &p_q) {
+void Quaternion::operator+=(const Quaternion &p_q) {
x += p_q.x;
y += p_q.y;
z += p_q.z;
w += p_q.w;
}
-void Quat::operator-=(const Quat &p_q) {
+void Quaternion::operator-=(const Quaternion &p_q) {
x -= p_q.x;
y -= p_q.y;
z -= p_q.z;
w -= p_q.w;
}
-void Quat::operator*=(const real_t &s) {
+void Quaternion::operator*=(const real_t &s) {
x *= s;
y *= s;
z *= s;
w *= s;
}
-void Quat::operator/=(const real_t &s) {
+void Quaternion::operator/=(const real_t &s) {
*this *= 1.0 / s;
}
-Quat Quat::operator+(const Quat &q2) const {
- const Quat &q1 = *this;
- return Quat(q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w);
+Quaternion Quaternion::operator+(const Quaternion &q2) const {
+ const Quaternion &q1 = *this;
+ return Quaternion(q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w);
}
-Quat Quat::operator-(const Quat &q2) const {
- const Quat &q1 = *this;
- return Quat(q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w);
+Quaternion Quaternion::operator-(const Quaternion &q2) const {
+ const Quaternion &q1 = *this;
+ return Quaternion(q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w);
}
-Quat Quat::operator-() const {
- const Quat &q2 = *this;
- return Quat(-q2.x, -q2.y, -q2.z, -q2.w);
+Quaternion Quaternion::operator-() const {
+ const Quaternion &q2 = *this;
+ return Quaternion(-q2.x, -q2.y, -q2.z, -q2.w);
}
-Quat Quat::operator*(const real_t &s) const {
- return Quat(x * s, y * s, z * s, w * s);
+Quaternion Quaternion::operator*(const real_t &s) const {
+ return Quaternion(x * s, y * s, z * s, w * s);
}
-Quat Quat::operator/(const real_t &s) const {
+Quaternion Quaternion::operator/(const real_t &s) const {
return *this * (1.0 / s);
}
-bool Quat::operator==(const Quat &p_quat) const {
- return x == p_quat.x && y == p_quat.y && z == p_quat.z && w == p_quat.w;
+bool Quaternion::operator==(const Quaternion &p_quaternion) const {
+ return x == p_quaternion.x && y == p_quaternion.y && z == p_quaternion.z && w == p_quaternion.w;
}
-bool Quat::operator!=(const Quat &p_quat) const {
- return x != p_quat.x || y != p_quat.y || z != p_quat.z || w != p_quat.w;
+bool Quaternion::operator!=(const Quaternion &p_quaternion) const {
+ return x != p_quaternion.x || y != p_quaternion.y || z != p_quaternion.z || w != p_quaternion.w;
}
-_FORCE_INLINE_ Quat operator*(const real_t &p_real, const Quat &p_quat) {
- return p_quat * p_real;
+_FORCE_INLINE_ Quaternion operator*(const real_t &p_real, const Quaternion &p_quaternion) {
+ return p_quaternion * p_real;
}
-#endif // QUAT_H
+#endif // QUATERNION_H
diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp
index fe18cc3d41..0960fe19a6 100644
--- a/core/math/quick_hull.cpp
+++ b/core/math/quick_hull.cpp
@@ -112,7 +112,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
}
}
- //fourth vertex is the one most further away from the plane
+ //fourth vertex is the one most further away from the plane
{
real_t maxd = 0;
@@ -192,9 +192,9 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
continue;
}
- for (List<Face>::Element *E = faces.front(); E; E = E->next()) {
- if (E->get().plane.distance_to(p_points[i]) > over_tolerance) {
- E->get().points_over.push_back(i);
+ for (Face &E : faces) {
+ if (E.plane.distance_to(p_points[i]) > over_tolerance) {
+ E.points_over.push_back(i);
break;
}
}
@@ -292,8 +292,8 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
//distribute points into new faces
- for (List<List<Face>::Element *>::Element *F = lit_faces.front(); F; F = F->next()) {
- Face &lf = F->get()->get();
+ for (List<Face>::Element *&F : lit_faces) {
+ Face &lf = F->get();
for (int i = 0; i < lf.points_over.size(); i++) {
if (lf.points_over[i] == f.points_over[next]) { //do not add current one
@@ -301,8 +301,8 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
}
Vector3 p = p_points[lf.points_over[i]];
- for (List<List<Face>::Element *>::Element *E = new_faces.front(); E; E = E->next()) {
- Face &f2 = E->get()->get();
+ for (List<Face>::Element *&E : new_faces) {
+ Face &f2 = E->get();
if (f2.plane.distance_to(p) > over_tolerance) {
f2.points_over.push_back(lf.points_over[i]);
break;
@@ -320,10 +320,10 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
//put faces that contain no points on the front
- for (List<List<Face>::Element *>::Element *E = new_faces.front(); E; E = E->next()) {
- Face &f2 = E->get()->get();
+ for (List<Face>::Element *&E : new_faces) {
+ Face &f2 = E->get();
if (f2.points_over.size() == 0) {
- faces.move_to_front(E->get());
+ faces.move_to_front(E);
}
}
@@ -336,19 +336,19 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
Map<Edge, RetFaceConnect> ret_edges;
List<Geometry3D::MeshData::Face> ret_faces;
- for (List<Face>::Element *E = faces.front(); E; E = E->next()) {
+ for (const Face &E : faces) {
Geometry3D::MeshData::Face f;
- f.plane = E->get().plane;
+ f.plane = E.plane;
for (int i = 0; i < 3; i++) {
- f.indices.push_back(E->get().vertices[i]);
+ f.indices.push_back(E.vertices[i]);
}
List<Geometry3D::MeshData::Face>::Element *F = ret_faces.push_back(f);
for (int i = 0; i < 3; i++) {
- uint32_t a = E->get().vertices[i];
- uint32_t b = E->get().vertices[(i + 1) % 3];
+ uint32_t a = E.vertices[i];
+ uint32_t b = E.vertices[(i + 1) % 3];
Edge e(a, b);
Map<Edge, RetFaceConnect>::Element *G = ret_edges.find(e);
@@ -439,8 +439,8 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
r_mesh.faces.resize(ret_faces.size());
int idx = 0;
- for (List<Geometry3D::MeshData::Face>::Element *E = ret_faces.front(); E; E = E->next()) {
- r_mesh.faces.write[idx++] = E->get();
+ for (const Geometry3D::MeshData::Face &E : ret_faces) {
+ r_mesh.faces.write[idx++] = E;
}
r_mesh.edges.resize(ret_edges.size());
idx = 0;
diff --git a/core/math/random_number_generator.h b/core/math/random_number_generator.h
index a396c2b7d7..06cd3999f3 100644
--- a/core/math/random_number_generator.h
+++ b/core/math/random_number_generator.h
@@ -32,10 +32,10 @@
#define RANDOM_NUMBER_GENERATOR_H
#include "core/math/random_pcg.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
-class RandomNumberGenerator : public Reference {
- GDCLASS(RandomNumberGenerator, Reference);
+class RandomNumberGenerator : public RefCounted {
+ GDCLASS(RandomNumberGenerator, RefCounted);
protected:
RandomPCG randbase;
diff --git a/core/math/rect2.cpp b/core/math/rect2.cpp
index 60c44999f7..f64bf560c8 100644
--- a/core/math/rect2.cpp
+++ b/core/math/rect2.cpp
@@ -263,3 +263,11 @@ next4:
return true;
}
+
+Rect2::operator String() const {
+ return "[P: " + position.operator String() + ", S: " + size + "]";
+}
+
+Rect2i::operator String() const {
+ return "[P: " + position.operator String() + ", S: " + size + "]";
+}
diff --git a/core/math/rect2.h b/core/math/rect2.h
index 1dc027cf72..ab0b489b4a 100644
--- a/core/math/rect2.h
+++ b/core/math/rect2.h
@@ -320,7 +320,7 @@ struct Rect2 {
return position + size;
}
- operator String() const { return String(position) + ", " + String(size); }
+ operator String() const;
Rect2() {}
Rect2(real_t p_x, real_t p_y, real_t p_width, real_t p_height) :
@@ -498,7 +498,7 @@ struct Rect2i {
return position + size;
}
- operator String() const { return String(position) + ", " + String(size); }
+ operator String() const;
operator Rect2() const { return Rect2(position, size); }
diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp
index 4a521b96ae..16934d67df 100644
--- a/core/math/transform_2d.cpp
+++ b/core/math/transform_2d.cpp
@@ -158,6 +158,13 @@ bool Transform2D::is_equal_approx(const Transform2D &p_transform) const {
return elements[0].is_equal_approx(p_transform.elements[0]) && elements[1].is_equal_approx(p_transform.elements[1]) && elements[2].is_equal_approx(p_transform.elements[2]);
}
+Transform2D Transform2D::looking_at(const Vector2 &p_target) const {
+ Transform2D return_trans = Transform2D(get_rotation(), get_origin());
+ Vector2 target_position = affine_inverse().xform(p_target);
+ return_trans.set_rotation(return_trans.get_rotation() + (target_position * get_scale()).angle());
+ return return_trans;
+}
+
bool Transform2D::operator==(const Transform2D &p_transform) const {
for (int i = 0; i < 3; i++) {
if (elements[i] != p_transform.elements[i]) {
@@ -269,6 +276,20 @@ Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, real_t
return res;
}
+void Transform2D::operator*=(const real_t p_val) {
+ elements[0] *= p_val;
+ elements[1] *= p_val;
+ elements[2] *= p_val;
+}
+
+Transform2D Transform2D::operator*(const real_t p_val) const {
+ Transform2D ret(*this);
+ ret *= p_val;
+ return ret;
+}
+
Transform2D::operator String() const {
- return String(String() + elements[0] + ", " + elements[1] + ", " + elements[2]);
+ return "[X: " + elements[0].operator String() +
+ ", Y: " + elements[1].operator String() +
+ ", O: " + elements[2].operator String() + "]";
}
diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h
index 327d0f244f..34cfd0c1a9 100644
--- a/core/math/transform_2d.h
+++ b/core/math/transform_2d.h
@@ -100,11 +100,15 @@ struct Transform2D {
Transform2D orthonormalized() const;
bool is_equal_approx(const Transform2D &p_transform) const;
+ Transform2D looking_at(const Vector2 &p_target) const;
+
bool operator==(const Transform2D &p_transform) const;
bool operator!=(const Transform2D &p_transform) const;
void operator*=(const Transform2D &p_transform);
Transform2D operator*(const Transform2D &p_transform) const;
+ void operator*=(const real_t p_val);
+ Transform2D operator*(const real_t p_val) const;
Transform2D interpolate_with(const Transform2D &p_transform, real_t p_c) const;
diff --git a/core/math/transform.cpp b/core/math/transform_3d.cpp
index d4d7ff6d28..4f4943c8ef 100644
--- a/core/math/transform.cpp
+++ b/core/math/transform_3d.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* transform.cpp */
+/* transform_3d.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,185 +28,171 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "transform.h"
+#include "transform_3d.h"
#include "core/math/math_funcs.h"
#include "core/string/print_string.h"
-void Transform::affine_invert() {
+void Transform3D::affine_invert() {
basis.invert();
origin = basis.xform(-origin);
}
-Transform Transform::affine_inverse() const {
- Transform ret = *this;
+Transform3D Transform3D::affine_inverse() const {
+ Transform3D ret = *this;
ret.affine_invert();
return ret;
}
-void Transform::invert() {
+void Transform3D::invert() {
basis.transpose();
origin = basis.xform(-origin);
}
-Transform Transform::inverse() const {
+Transform3D Transform3D::inverse() const {
// FIXME: this function assumes the basis is a rotation matrix, with no scaling.
- // Transform::affine_inverse can handle matrices with scaling, so GDScript should eventually use that.
- Transform ret = *this;
+ // Transform3D::affine_inverse can handle matrices with scaling, so GDScript should eventually use that.
+ Transform3D ret = *this;
ret.invert();
return ret;
}
-void Transform::rotate(const Vector3 &p_axis, real_t p_phi) {
+void Transform3D::rotate(const Vector3 &p_axis, real_t p_phi) {
*this = rotated(p_axis, p_phi);
}
-Transform Transform::rotated(const Vector3 &p_axis, real_t p_phi) const {
- return Transform(Basis(p_axis, p_phi), Vector3()) * (*this);
+Transform3D Transform3D::rotated(const Vector3 &p_axis, real_t p_phi) const {
+ return Transform3D(Basis(p_axis, p_phi), Vector3()) * (*this);
}
-void Transform::rotate_basis(const Vector3 &p_axis, real_t p_phi) {
+void Transform3D::rotate_basis(const Vector3 &p_axis, real_t p_phi) {
basis.rotate(p_axis, p_phi);
}
-Transform Transform::looking_at(const Vector3 &p_target, const Vector3 &p_up) const {
- Transform t = *this;
- t.set_look_at(origin, p_target, p_up);
+Transform3D Transform3D::looking_at(const Vector3 &p_target, const Vector3 &p_up) const {
+ Transform3D t = *this;
+ t.basis = Basis::looking_at(p_target - origin, p_up);
return t;
}
-void Transform::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up) {
-#ifdef MATH_CHECKS
- ERR_FAIL_COND(p_eye == p_target);
- ERR_FAIL_COND(p_up.length() == 0);
-#endif
- // Reference: MESA source code
- Vector3 v_x, v_y, v_z;
-
- /* Make rotation matrix */
-
- /* Z vector */
- v_z = p_eye - p_target;
-
- v_z.normalize();
-
- v_y = p_up;
-
- v_x = v_y.cross(v_z);
-#ifdef MATH_CHECKS
- ERR_FAIL_COND(v_x.length() == 0);
-#endif
-
- /* Recompute Y = Z cross X */
- v_y = v_z.cross(v_x);
-
- v_x.normalize();
- v_y.normalize();
-
- basis.set(v_x, v_y, v_z);
-
+void Transform3D::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up) {
+ basis = Basis::looking_at(p_target - p_eye, p_up);
origin = p_eye;
}
-Transform Transform::interpolate_with(const Transform &p_transform, real_t p_c) const {
+Transform3D Transform3D::interpolate_with(const Transform3D &p_transform, real_t p_c) const {
/* not sure if very "efficient" but good enough? */
Vector3 src_scale = basis.get_scale();
- Quat src_rot = basis.get_rotation_quat();
+ Quaternion src_rot = basis.get_rotation_quaternion();
Vector3 src_loc = origin;
Vector3 dst_scale = p_transform.basis.get_scale();
- Quat dst_rot = p_transform.basis.get_rotation_quat();
+ Quaternion dst_rot = p_transform.basis.get_rotation_quaternion();
Vector3 dst_loc = p_transform.origin;
- Transform interp;
- interp.basis.set_quat_scale(src_rot.slerp(dst_rot, p_c).normalized(), src_scale.lerp(dst_scale, p_c));
+ Transform3D interp;
+ interp.basis.set_quaternion_scale(src_rot.slerp(dst_rot, p_c).normalized(), src_scale.lerp(dst_scale, p_c));
interp.origin = src_loc.lerp(dst_loc, p_c);
return interp;
}
-void Transform::scale(const Vector3 &p_scale) {
+void Transform3D::scale(const Vector3 &p_scale) {
basis.scale(p_scale);
origin *= p_scale;
}
-Transform Transform::scaled(const Vector3 &p_scale) const {
- Transform t = *this;
+Transform3D Transform3D::scaled(const Vector3 &p_scale) const {
+ Transform3D t = *this;
t.scale(p_scale);
return t;
}
-void Transform::scale_basis(const Vector3 &p_scale) {
+void Transform3D::scale_basis(const Vector3 &p_scale) {
basis.scale(p_scale);
}
-void Transform::translate(real_t p_tx, real_t p_ty, real_t p_tz) {
+void Transform3D::translate(real_t p_tx, real_t p_ty, real_t p_tz) {
translate(Vector3(p_tx, p_ty, p_tz));
}
-void Transform::translate(const Vector3 &p_translation) {
+void Transform3D::translate(const Vector3 &p_translation) {
for (int i = 0; i < 3; i++) {
origin[i] += basis[i].dot(p_translation);
}
}
-Transform Transform::translated(const Vector3 &p_translation) const {
- Transform t = *this;
+Transform3D Transform3D::translated(const Vector3 &p_translation) const {
+ Transform3D t = *this;
t.translate(p_translation);
return t;
}
-void Transform::orthonormalize() {
+void Transform3D::orthonormalize() {
basis.orthonormalize();
}
-Transform Transform::orthonormalized() const {
- Transform _copy = *this;
+Transform3D Transform3D::orthonormalized() const {
+ Transform3D _copy = *this;
_copy.orthonormalize();
return _copy;
}
-bool Transform::is_equal_approx(const Transform &p_transform) const {
+bool Transform3D::is_equal_approx(const Transform3D &p_transform) const {
return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin);
}
-bool Transform::operator==(const Transform &p_transform) const {
+bool Transform3D::operator==(const Transform3D &p_transform) const {
return (basis == p_transform.basis && origin == p_transform.origin);
}
-bool Transform::operator!=(const Transform &p_transform) const {
+bool Transform3D::operator!=(const Transform3D &p_transform) const {
return (basis != p_transform.basis || origin != p_transform.origin);
}
-void Transform::operator*=(const Transform &p_transform) {
+void Transform3D::operator*=(const Transform3D &p_transform) {
origin = xform(p_transform.origin);
basis *= p_transform.basis;
}
-Transform Transform::operator*(const Transform &p_transform) const {
- Transform t = *this;
+Transform3D Transform3D::operator*(const Transform3D &p_transform) const {
+ Transform3D t = *this;
t *= p_transform;
return t;
}
-Transform::operator String() const {
- return basis.operator String() + " - " + origin.operator String();
+void Transform3D::operator*=(const real_t p_val) {
+ origin *= p_val;
+ basis *= p_val;
+}
+
+Transform3D Transform3D::operator*(const real_t p_val) const {
+ Transform3D ret(*this);
+ ret *= p_val;
+ return ret;
+}
+
+Transform3D::operator String() const {
+ return "[X: " + basis.get_axis(0).operator String() +
+ ", Y: " + basis.get_axis(1).operator String() +
+ ", Z: " + basis.get_axis(2).operator String() +
+ ", O: " + origin.operator String() + "]";
}
-Transform::Transform(const Basis &p_basis, const Vector3 &p_origin) :
+Transform3D::Transform3D(const Basis &p_basis, const Vector3 &p_origin) :
basis(p_basis),
origin(p_origin) {
}
-Transform::Transform(const Vector3 &p_x, const Vector3 &p_y, const Vector3 &p_z, const Vector3 &p_origin) :
+Transform3D::Transform3D(const Vector3 &p_x, const Vector3 &p_y, const Vector3 &p_z, const Vector3 &p_origin) :
origin(p_origin) {
basis.set_axis(0, p_x);
basis.set_axis(1, p_y);
basis.set_axis(2, p_z);
}
-Transform::Transform(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz) {
+Transform3D::Transform3D(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz) {
basis = Basis(xx, xy, xz, yx, yy, yz, zx, zy, zz);
origin = Vector3(ox, oy, oz);
}
diff --git a/core/math/transform.h b/core/math/transform_3d.h
index 1c05dbe554..345e0fade0 100644
--- a/core/math/transform.h
+++ b/core/math/transform_3d.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* transform.h */
+/* transform_3d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -35,31 +35,31 @@
#include "core/math/basis.h"
#include "core/math/plane.h"
-class Transform {
+class Transform3D {
public:
Basis basis;
Vector3 origin;
void invert();
- Transform inverse() const;
+ Transform3D inverse() const;
void affine_invert();
- Transform affine_inverse() const;
+ Transform3D affine_inverse() const;
- Transform rotated(const Vector3 &p_axis, real_t p_phi) const;
+ Transform3D rotated(const Vector3 &p_axis, real_t p_phi) const;
void rotate(const Vector3 &p_axis, real_t p_phi);
void rotate_basis(const Vector3 &p_axis, real_t p_phi);
void set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0));
- Transform looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)) const;
+ Transform3D looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)) const;
void scale(const Vector3 &p_scale);
- Transform scaled(const Vector3 &p_scale) const;
+ Transform3D scaled(const Vector3 &p_scale) const;
void scale_basis(const Vector3 &p_scale);
void translate(real_t p_tx, real_t p_ty, real_t p_tz);
void translate(const Vector3 &p_translation);
- Transform translated(const Vector3 &p_translation) const;
+ Transform3D translated(const Vector3 &p_translation) const;
const Basis &get_basis() const { return basis; }
void set_basis(const Basis &p_basis) { basis = p_basis; }
@@ -68,32 +68,42 @@ public:
void set_origin(const Vector3 &p_origin) { origin = p_origin; }
void orthonormalize();
- Transform orthonormalized() const;
- bool is_equal_approx(const Transform &p_transform) const;
+ Transform3D orthonormalized() const;
+ bool is_equal_approx(const Transform3D &p_transform) const;
- bool operator==(const Transform &p_transform) const;
- bool operator!=(const Transform &p_transform) const;
+ bool operator==(const Transform3D &p_transform) const;
+ bool operator!=(const Transform3D &p_transform) const;
_FORCE_INLINE_ Vector3 xform(const Vector3 &p_vector) const;
+ _FORCE_INLINE_ AABB xform(const AABB &p_aabb) const;
+ _FORCE_INLINE_ Vector<Vector3> xform(const Vector<Vector3> &p_array) const;
+
+ // NOTE: These are UNSAFE with non-uniform scaling, and will produce incorrect results.
+ // They use the transpose.
+ // For safe inverse transforms, xform by the affine_inverse.
_FORCE_INLINE_ Vector3 xform_inv(const Vector3 &p_vector) const;
+ _FORCE_INLINE_ AABB xform_inv(const AABB &p_aabb) const;
+ _FORCE_INLINE_ Vector<Vector3> xform_inv(const Vector<Vector3> &p_array) const;
+ // Safe with non-uniform scaling (uses affine_inverse).
_FORCE_INLINE_ Plane xform(const Plane &p_plane) const;
_FORCE_INLINE_ Plane xform_inv(const Plane &p_plane) const;
- _FORCE_INLINE_ AABB xform(const AABB &p_aabb) const;
- _FORCE_INLINE_ AABB xform_inv(const AABB &p_aabb) const;
+ // These fast versions use precomputed affine inverse, and should be used in bottleneck areas where
+ // multiple planes are to be transformed.
+ _FORCE_INLINE_ Plane xform_fast(const Plane &p_plane, const Basis &p_basis_inverse_transpose) const;
+ static _FORCE_INLINE_ Plane xform_inv_fast(const Plane &p_plane, const Transform3D &p_inverse, const Basis &p_basis_transpose);
- _FORCE_INLINE_ Vector<Vector3> xform(const Vector<Vector3> &p_array) const;
- _FORCE_INLINE_ Vector<Vector3> xform_inv(const Vector<Vector3> &p_array) const;
+ void operator*=(const Transform3D &p_transform);
+ Transform3D operator*(const Transform3D &p_transform) const;
+ void operator*=(const real_t p_val);
+ Transform3D operator*(const real_t p_val) const;
- void operator*=(const Transform &p_transform);
- Transform operator*(const Transform &p_transform) const;
+ Transform3D interpolate_with(const Transform3D &p_transform, real_t p_c) const;
- Transform interpolate_with(const Transform &p_transform, real_t p_c) const;
-
- _FORCE_INLINE_ Transform inverse_xform(const Transform &t) const {
+ _FORCE_INLINE_ Transform3D inverse_xform(const Transform3D &t) const {
Vector3 v = t.origin - origin;
- return Transform(basis.transpose_xform(t.basis),
+ return Transform3D(basis.transpose_xform(t.basis),
basis.xform(v));
}
@@ -106,20 +116,20 @@ public:
operator String() const;
- Transform() {}
- Transform(const Basis &p_basis, const Vector3 &p_origin = Vector3());
- Transform(const Vector3 &p_x, const Vector3 &p_y, const Vector3 &p_z, const Vector3 &p_origin);
- Transform(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz);
+ Transform3D() {}
+ Transform3D(const Basis &p_basis, const Vector3 &p_origin = Vector3());
+ Transform3D(const Vector3 &p_x, const Vector3 &p_y, const Vector3 &p_z, const Vector3 &p_origin);
+ Transform3D(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz);
};
-_FORCE_INLINE_ Vector3 Transform::xform(const Vector3 &p_vector) const {
+_FORCE_INLINE_ Vector3 Transform3D::xform(const Vector3 &p_vector) const {
return Vector3(
basis[0].dot(p_vector) + origin.x,
basis[1].dot(p_vector) + origin.y,
basis[2].dot(p_vector) + origin.z);
}
-_FORCE_INLINE_ Vector3 Transform::xform_inv(const Vector3 &p_vector) const {
+_FORCE_INLINE_ Vector3 Transform3D::xform_inv(const Vector3 &p_vector) const {
Vector3 v = p_vector - origin;
return Vector3(
@@ -128,34 +138,24 @@ _FORCE_INLINE_ Vector3 Transform::xform_inv(const Vector3 &p_vector) const {
(basis.elements[0][2] * v.x) + (basis.elements[1][2] * v.y) + (basis.elements[2][2] * v.z));
}
-_FORCE_INLINE_ Plane Transform::xform(const Plane &p_plane) const {
- Vector3 point = p_plane.normal * p_plane.d;
- Vector3 point_dir = point + p_plane.normal;
- point = xform(point);
- point_dir = xform(point_dir);
-
- Vector3 normal = point_dir - point;
- normal.normalize();
- real_t d = normal.dot(point);
-
- return Plane(normal, d);
+// Neither the plane regular xform or xform_inv are particularly efficient,
+// as they do a basis inverse. For xforming a large number
+// of planes it is better to pre-calculate the inverse transpose basis once
+// and reuse it for each plane, by using the 'fast' version of the functions.
+_FORCE_INLINE_ Plane Transform3D::xform(const Plane &p_plane) const {
+ Basis b = basis.inverse();
+ b.transpose();
+ return xform_fast(p_plane, b);
}
-_FORCE_INLINE_ Plane Transform::xform_inv(const Plane &p_plane) const {
- Vector3 point = p_plane.normal * p_plane.d;
- Vector3 point_dir = point + p_plane.normal;
- point = xform_inv(point);
- point_dir = xform_inv(point_dir);
-
- Vector3 normal = point_dir - point;
- normal.normalize();
- real_t d = normal.dot(point);
-
- return Plane(normal, d);
+_FORCE_INLINE_ Plane Transform3D::xform_inv(const Plane &p_plane) const {
+ Transform3D inv = affine_inverse();
+ Basis basis_transpose = basis.transposed();
+ return xform_inv_fast(p_plane, inv, basis_transpose);
}
-_FORCE_INLINE_ AABB Transform::xform(const AABB &p_aabb) const {
- /* http://dev.theomader.com/transform-bounding-boxes/ */
+_FORCE_INLINE_ AABB Transform3D::xform(const AABB &p_aabb) const {
+ /* https://dev.theomader.com/transform-bounding-boxes/ */
Vector3 min = p_aabb.position;
Vector3 max = p_aabb.position + p_aabb.size;
Vector3 tmin, tmax;
@@ -179,7 +179,7 @@ _FORCE_INLINE_ AABB Transform::xform(const AABB &p_aabb) const {
return r_aabb;
}
-_FORCE_INLINE_ AABB Transform::xform_inv(const AABB &p_aabb) const {
+_FORCE_INLINE_ AABB Transform3D::xform_inv(const AABB &p_aabb) const {
/* define vertices */
Vector3 vertices[8] = {
Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z),
@@ -203,7 +203,7 @@ _FORCE_INLINE_ AABB Transform::xform_inv(const AABB &p_aabb) const {
return ret;
}
-Vector<Vector3> Transform::xform(const Vector<Vector3> &p_array) const {
+Vector<Vector3> Transform3D::xform(const Vector<Vector3> &p_array) const {
Vector<Vector3> array;
array.resize(p_array.size());
@@ -216,7 +216,7 @@ Vector<Vector3> Transform::xform(const Vector<Vector3> &p_array) const {
return array;
}
-Vector<Vector3> Transform::xform_inv(const Vector<Vector3> &p_array) const {
+Vector<Vector3> Transform3D::xform_inv(const Vector<Vector3> &p_array) const {
Vector<Vector3> array;
array.resize(p_array.size());
@@ -229,4 +229,37 @@ Vector<Vector3> Transform::xform_inv(const Vector<Vector3> &p_array) const {
return array;
}
+_FORCE_INLINE_ Plane Transform3D::xform_fast(const Plane &p_plane, const Basis &p_basis_inverse_transpose) const {
+ // Transform a single point on the plane.
+ Vector3 point = p_plane.normal * p_plane.d;
+ point = xform(point);
+
+ // Use inverse transpose for correct normals with non-uniform scaling.
+ Vector3 normal = p_basis_inverse_transpose.xform(p_plane.normal);
+ normal.normalize();
+
+ real_t d = normal.dot(point);
+ return Plane(normal, d);
+}
+
+_FORCE_INLINE_ Plane Transform3D::xform_inv_fast(const Plane &p_plane, const Transform3D &p_inverse, const Basis &p_basis_transpose) {
+ // Transform a single point on the plane.
+ Vector3 point = p_plane.normal * p_plane.d;
+ point = p_inverse.xform(point);
+
+ // Note that instead of precalculating the transpose, an alternative
+ // would be to use the transpose for the basis transform.
+ // However that would be less SIMD friendly (requiring a swizzle).
+ // So the cost is one extra precalced value in the calling code.
+ // This is probably worth it, as this could be used in bottleneck areas. And
+ // where it is not a bottleneck, the non-fast method is fine.
+
+ // Use transpose for correct normals with non-uniform scaling.
+ Vector3 normal = p_basis_transpose.xform(p_plane.normal);
+ normal.normalize();
+
+ real_t d = normal.dot(point);
+ return Plane(normal, d);
+}
+
#endif // TRANSFORM_H
diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp
index 23c0c686a2..bf06c848c5 100644
--- a/core/math/triangle_mesh.cpp
+++ b/core/math/triangle_mesh.cpp
@@ -32,9 +32,9 @@
#include "core/templates/sort_array.h"
-int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc) {
- if (p_depth > max_depth) {
- max_depth = p_depth;
+int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &r_max_depth, int &r_max_alloc) {
+ if (p_depth > r_max_depth) {
+ r_max_depth = p_depth;
}
if (p_size == 1) {
@@ -70,10 +70,10 @@ int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, in
} break;
}
- int left = _create_bvh(p_bvh, p_bb, p_from, p_size / 2, p_depth + 1, max_depth, max_alloc);
- int right = _create_bvh(p_bvh, p_bb, p_from + p_size / 2, p_size - p_size / 2, p_depth + 1, max_depth, max_alloc);
+ int left = _create_bvh(p_bvh, p_bb, p_from, p_size / 2, p_depth + 1, r_max_depth, r_max_alloc);
+ int right = _create_bvh(p_bvh, p_bb, p_from + p_size / 2, p_size - p_size / 2, p_depth + 1, r_max_depth, r_max_alloc);
- int index = max_alloc++;
+ int index = r_max_alloc++;
BVH *_new = &p_bvh[index];
_new->aabb = aabb;
_new->center = aabb.position + aabb.size * 0.5;
@@ -600,7 +600,7 @@ bool TriangleMesh::inside_convex_shape(const Plane *p_planes, int p_plane_count,
const Vector3 *vertexptr = vertices.ptr();
const BVH *bvhptr = bvh.ptr();
- Transform scale(Basis().scaled(p_scale));
+ Transform3D scale(Basis().scaled(p_scale));
int pos = bvh.size() - 1;
diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h
index 1d1dbc114b..2d3b4db4bb 100644
--- a/core/math/triangle_mesh.h
+++ b/core/math/triangle_mesh.h
@@ -32,16 +32,18 @@
#define TRIANGLE_MESH_H
#include "core/math/face3.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
-class TriangleMesh : public Reference {
- GDCLASS(TriangleMesh, Reference);
+class TriangleMesh : public RefCounted {
+ GDCLASS(TriangleMesh, RefCounted);
+public:
struct Triangle {
Vector3 normal;
int indices[3];
};
+private:
Vector<Triangle> triangles;
Vector<Vector3> vertices;
@@ -86,8 +88,8 @@ public:
Vector3 get_area_normal(const AABB &p_aabb) const;
Vector<Face3> get_faces() const;
- Vector<Triangle> get_triangles() const { return triangles; }
- Vector<Vector3> get_vertices() const { return vertices; }
+ const Vector<Triangle> &get_triangles() const { return triangles; }
+ const Vector<Vector3> &get_vertices() const { return vertices; }
void get_indices(Vector<int> *r_triangles_indices) const;
void create(const Vector<Vector3> &p_faces);
diff --git a/core/math/triangulate.h b/core/math/triangulate.h
index 55dc4e8e7d..249ca6238f 100644
--- a/core/math/triangulate.h
+++ b/core/math/triangulate.h
@@ -34,7 +34,7 @@
#include "core/math/vector2.h"
/*
-http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
+https://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
*/
class Triangulate {
diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp
index 46a08b53ab..b53dc05a00 100644
--- a/core/math/vector2.cpp
+++ b/core/math/vector2.cpp
@@ -34,6 +34,10 @@ real_t Vector2::angle() const {
return Math::atan2(y, x);
}
+Vector2 Vector2::from_angle(const real_t p_angle) {
+ return Vector2(Math::cos(p_angle), Math::sin(p_angle));
+}
+
real_t Vector2::length() const {
return Math::sqrt(x * x + y * y);
}
@@ -102,7 +106,7 @@ Vector2 Vector2::round() const {
return Vector2(Math::round(x), Math::round(y));
}
-Vector2 Vector2::rotated(real_t p_by) const {
+Vector2 Vector2::rotated(const real_t p_by) const {
real_t sine = Math::sin(p_by);
real_t cosi = Math::cos(p_by);
return Vector2(
@@ -122,14 +126,20 @@ Vector2 Vector2::project(const Vector2 &p_to) const {
return p_to * (dot(p_to) / p_to.length_squared());
}
+Vector2 Vector2::clamp(const Vector2 &p_min, const Vector2 &p_max) const {
+ return Vector2(
+ CLAMP(x, p_min.x, p_max.x),
+ CLAMP(y, p_min.y, p_max.y));
+}
+
Vector2 Vector2::snapped(const Vector2 &p_step) const {
return Vector2(
Math::snapped(x, p_step.x),
Math::snapped(y, p_step.y));
}
-Vector2 Vector2::clamped(real_t p_len) const {
- real_t l = length();
+Vector2 Vector2::limit_length(const real_t p_len) const {
+ const real_t l = length();
Vector2 v = *this;
if (l > 0 && p_len < l) {
v /= l;
@@ -139,7 +149,7 @@ Vector2 Vector2::clamped(real_t p_len) const {
return v;
}
-Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_weight) const {
+Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const {
Vector2 p0 = p_pre_a;
Vector2 p1 = *this;
Vector2 p2 = p_b;
@@ -187,8 +197,18 @@ bool Vector2::is_equal_approx(const Vector2 &p_v) const {
return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y);
}
+Vector2::operator String() const {
+ return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")";
+}
+
/* Vector2i */
+Vector2i Vector2i::clamp(const Vector2i &p_min, const Vector2i &p_max) const {
+ return Vector2i(
+ CLAMP(x, p_min.x, p_max.x),
+ CLAMP(y, p_min.y, p_max.y));
+}
+
Vector2i Vector2i::operator+(const Vector2i &p_v) const {
return Vector2i(x + p_v.x, y + p_v.y);
}
@@ -257,3 +277,7 @@ bool Vector2i::operator==(const Vector2i &p_vec2) const {
bool Vector2i::operator!=(const Vector2i &p_vec2) const {
return x != p_vec2.x || y != p_vec2.y;
}
+
+Vector2i::operator String() const {
+ return "(" + itos(x) + ", " + itos(y) + ")";
+}
diff --git a/core/math/vector2.h b/core/math/vector2.h
index 6abe0f5ea9..332c0475fa 100644
--- a/core/math/vector2.h
+++ b/core/math/vector2.h
@@ -66,7 +66,7 @@ struct Vector2 {
return p_idx ? y : x;
}
- _FORCE_INLINE_ void set_all(real_t p_value) {
+ _FORCE_INLINE_ void set_all(const real_t p_value) {
x = y = p_value;
}
@@ -84,6 +84,7 @@ struct Vector2 {
real_t length() const;
real_t length_squared() const;
+ Vector2 limit_length(const real_t p_len = 1.0) const;
Vector2 min(const Vector2 &p_vector2) const {
return Vector2(MIN(x, p_vector2.x), MIN(y, p_vector2.y));
@@ -105,13 +106,11 @@ struct Vector2 {
Vector2 posmodv(const Vector2 &p_modv) const;
Vector2 project(const Vector2 &p_to) const;
- Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const;
+ Vector2 plane_project(const real_t p_d, const Vector2 &p_vec) const;
- Vector2 clamped(real_t p_len) const;
-
- _FORCE_INLINE_ Vector2 lerp(const Vector2 &p_to, real_t p_weight) const;
- _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_to, real_t p_weight) const;
- Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_weight) const;
+ _FORCE_INLINE_ Vector2 lerp(const Vector2 &p_to, const real_t p_weight) const;
+ _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_to, const real_t p_weight) const;
+ Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const;
Vector2 move_toward(const Vector2 &p_to, const real_t p_delta) const;
Vector2 slide(const Vector2 &p_normal) const;
@@ -148,12 +147,13 @@ struct Vector2 {
bool operator>=(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y >= p_vec2.y) : (x > p_vec2.x); }
real_t angle() const;
+ static Vector2 from_angle(const real_t p_angle);
_FORCE_INLINE_ Vector2 abs() const {
return Vector2(Math::abs(x), Math::abs(y));
}
- Vector2 rotated(real_t p_by) const;
+ Vector2 rotated(const real_t p_by) const;
Vector2 orthogonal() const {
return Vector2(y, -x);
}
@@ -163,34 +163,35 @@ struct Vector2 {
Vector2 ceil() const;
Vector2 round() const;
Vector2 snapped(const Vector2 &p_by) const;
+ Vector2 clamp(const Vector2 &p_min, const Vector2 &p_max) const;
real_t aspect() const { return width / height; }
- operator String() const { return String::num(x) + ", " + String::num(y); }
+ operator String() const;
_FORCE_INLINE_ Vector2() {}
- _FORCE_INLINE_ Vector2(real_t p_x, real_t p_y) {
+ _FORCE_INLINE_ Vector2(const real_t p_x, const real_t p_y) {
x = p_x;
y = p_y;
}
};
-_FORCE_INLINE_ Vector2 Vector2::plane_project(real_t p_d, const Vector2 &p_vec) const {
+_FORCE_INLINE_ Vector2 Vector2::plane_project(const real_t p_d, const Vector2 &p_vec) const {
return p_vec - *this * (dot(p_vec) - p_d);
}
-_FORCE_INLINE_ Vector2 operator*(float p_scalar, const Vector2 &p_vec) {
+_FORCE_INLINE_ Vector2 operator*(const float p_scalar, const Vector2 &p_vec) {
return p_vec * p_scalar;
}
-_FORCE_INLINE_ Vector2 operator*(double p_scalar, const Vector2 &p_vec) {
+_FORCE_INLINE_ Vector2 operator*(const double p_scalar, const Vector2 &p_vec) {
return p_vec * p_scalar;
}
-_FORCE_INLINE_ Vector2 operator*(int32_t p_scalar, const Vector2 &p_vec) {
+_FORCE_INLINE_ Vector2 operator*(const int32_t p_scalar, const Vector2 &p_vec) {
return p_vec * p_scalar;
}
-_FORCE_INLINE_ Vector2 operator*(int64_t p_scalar, const Vector2 &p_vec) {
+_FORCE_INLINE_ Vector2 operator*(const int64_t p_scalar, const Vector2 &p_vec) {
return p_vec * p_scalar;
}
@@ -250,7 +251,7 @@ _FORCE_INLINE_ bool Vector2::operator!=(const Vector2 &p_vec2) const {
return x != p_vec2.x || y != p_vec2.y;
}
-Vector2 Vector2::lerp(const Vector2 &p_to, real_t p_weight) const {
+Vector2 Vector2::lerp(const Vector2 &p_to, const real_t p_weight) const {
Vector2 res = *this;
res.x += (p_weight * (p_to.x - x));
@@ -259,7 +260,7 @@ Vector2 Vector2::lerp(const Vector2 &p_to, real_t p_weight) const {
return res;
}
-Vector2 Vector2::slerp(const Vector2 &p_to, real_t p_weight) const {
+Vector2 Vector2::slerp(const Vector2 &p_to, const real_t p_weight) const {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(!is_normalized(), Vector2(), "The start Vector2 must be normalized.");
#endif
@@ -300,6 +301,14 @@ struct Vector2i {
return p_idx ? y : x;
}
+ _FORCE_INLINE_ int min_axis() const {
+ return x < y ? 0 : 1;
+ }
+
+ _FORCE_INLINE_ int max_axis() const {
+ return x < y ? 1 : 0;
+ }
+
Vector2i min(const Vector2i &p_vector2i) const {
return Vector2(MIN(x, p_vector2i.x), MIN(y, p_vector2i.y));
}
@@ -338,8 +347,9 @@ struct Vector2i {
real_t aspect() const { return width / (real_t)height; }
Vector2i sign() const { return Vector2i(SGN(x), SGN(y)); }
Vector2i abs() const { return Vector2i(ABS(x), ABS(y)); }
+ Vector2i clamp(const Vector2i &p_min, const Vector2i &p_max) const;
- operator String() const { return String::num(x) + ", " + String::num(y); }
+ operator String() const;
operator Vector2() const { return Vector2(x, y); }
@@ -348,7 +358,7 @@ struct Vector2i {
x = (int32_t)p_vec2.x;
y = (int32_t)p_vec2.y;
}
- inline Vector2i(int32_t p_x, int32_t p_y) {
+ inline Vector2i(const int32_t p_x, const int32_t p_y) {
x = p_x;
y = p_y;
}
diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp
index d4317d506c..401c3ccd9c 100644
--- a/core/math/vector3.cpp
+++ b/core/math/vector3.cpp
@@ -32,39 +32,57 @@
#include "core/math/basis.h"
-void Vector3::rotate(const Vector3 &p_axis, real_t p_phi) {
+void Vector3::rotate(const Vector3 &p_axis, const real_t p_phi) {
*this = Basis(p_axis, p_phi).xform(*this);
}
-Vector3 Vector3::rotated(const Vector3 &p_axis, real_t p_phi) const {
+Vector3 Vector3::rotated(const Vector3 &p_axis, const real_t p_phi) const {
Vector3 r = *this;
r.rotate(p_axis, p_phi);
return r;
}
-void Vector3::set_axis(int p_axis, real_t p_value) {
+void Vector3::set_axis(const int p_axis, const real_t p_value) {
ERR_FAIL_INDEX(p_axis, 3);
coord[p_axis] = p_value;
}
-real_t Vector3::get_axis(int p_axis) const {
+real_t Vector3::get_axis(const int p_axis) const {
ERR_FAIL_INDEX_V(p_axis, 3, 0);
return operator[](p_axis);
}
-void Vector3::snap(Vector3 p_step) {
+Vector3 Vector3::clamp(const Vector3 &p_min, const Vector3 &p_max) const {
+ return Vector3(
+ CLAMP(x, p_min.x, p_max.x),
+ CLAMP(y, p_min.y, p_max.y),
+ CLAMP(z, p_min.z, p_max.z));
+}
+
+void Vector3::snap(const Vector3 p_step) {
x = Math::snapped(x, p_step.x);
y = Math::snapped(y, p_step.y);
z = Math::snapped(z, p_step.z);
}
-Vector3 Vector3::snapped(Vector3 p_step) const {
+Vector3 Vector3::snapped(const Vector3 p_step) const {
Vector3 v = *this;
v.snap(p_step);
return v;
}
-Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_weight) const {
+Vector3 Vector3::limit_length(const real_t p_len) const {
+ const real_t l = length();
+ Vector3 v = *this;
+ if (l > 0 && p_len < l) {
+ v /= l;
+ v *= p_len;
+ }
+
+ return v;
+}
+
+Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const {
Vector3 p0 = p_pre_a;
Vector3 p1 = *this;
Vector3 p2 = p_b;
@@ -108,5 +126,5 @@ bool Vector3::is_equal_approx(const Vector3 &p_v) const {
}
Vector3::operator String() const {
- return (rtos(x) + ", " + rtos(y) + ", " + rtos(z));
+ return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ")";
}
diff --git a/core/math/vector3.h b/core/math/vector3.h
index adfc52566f..6a4c42f41b 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -56,18 +56,18 @@ struct Vector3 {
real_t coord[3] = { 0 };
};
- _FORCE_INLINE_ const real_t &operator[](int p_axis) const {
+ _FORCE_INLINE_ const real_t &operator[](const int p_axis) const {
return coord[p_axis];
}
- _FORCE_INLINE_ real_t &operator[](int p_axis) {
+ _FORCE_INLINE_ real_t &operator[](const int p_axis) {
return coord[p_axis];
}
- void set_axis(int p_axis, real_t p_value);
- real_t get_axis(int p_axis) const;
+ void set_axis(const int p_axis, const real_t p_value);
+ real_t get_axis(const int p_axis) const;
- _FORCE_INLINE_ void set_all(real_t p_value) {
+ _FORCE_INLINE_ void set_all(const real_t p_value) {
x = y = z = p_value;
}
@@ -86,20 +86,21 @@ struct Vector3 {
_FORCE_INLINE_ Vector3 normalized() const;
_FORCE_INLINE_ bool is_normalized() const;
_FORCE_INLINE_ Vector3 inverse() const;
+ Vector3 limit_length(const real_t p_len = 1.0) const;
_FORCE_INLINE_ void zero();
- void snap(Vector3 p_val);
- Vector3 snapped(Vector3 p_val) const;
+ void snap(const Vector3 p_val);
+ Vector3 snapped(const Vector3 p_val) const;
- void rotate(const Vector3 &p_axis, real_t p_phi);
- Vector3 rotated(const Vector3 &p_axis, real_t p_phi) const;
+ void rotate(const Vector3 &p_axis, const real_t p_phi);
+ Vector3 rotated(const Vector3 &p_axis, const real_t p_phi) const;
/* Static Methods between 2 vector3s */
- _FORCE_INLINE_ Vector3 lerp(const Vector3 &p_to, real_t p_weight) const;
- _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_to, real_t p_weight) const;
- Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_weight) const;
+ _FORCE_INLINE_ Vector3 lerp(const Vector3 &p_to, const real_t p_weight) const;
+ _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_to, const real_t p_weight) const;
+ Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const;
Vector3 move_toward(const Vector3 &p_to, const real_t p_delta) const;
_FORCE_INLINE_ Vector3 cross(const Vector3 &p_b) const;
@@ -112,6 +113,7 @@ struct Vector3 {
_FORCE_INLINE_ Vector3 sign() const;
_FORCE_INLINE_ Vector3 ceil() const;
_FORCE_INLINE_ Vector3 round() const;
+ Vector3 clamp(const Vector3 &p_min, const Vector3 &p_max) const;
_FORCE_INLINE_ real_t distance_to(const Vector3 &p_to) const;
_FORCE_INLINE_ real_t distance_squared_to(const Vector3 &p_to) const;
@@ -141,10 +143,10 @@ struct Vector3 {
_FORCE_INLINE_ Vector3 &operator/=(const Vector3 &p_v);
_FORCE_INLINE_ Vector3 operator/(const Vector3 &p_v) const;
- _FORCE_INLINE_ Vector3 &operator*=(real_t p_scalar);
- _FORCE_INLINE_ Vector3 operator*(real_t p_scalar) const;
- _FORCE_INLINE_ Vector3 &operator/=(real_t p_scalar);
- _FORCE_INLINE_ Vector3 operator/(real_t p_scalar) const;
+ _FORCE_INLINE_ Vector3 &operator*=(const real_t p_scalar);
+ _FORCE_INLINE_ Vector3 operator*(const real_t p_scalar) const;
+ _FORCE_INLINE_ Vector3 &operator/=(const real_t p_scalar);
+ _FORCE_INLINE_ Vector3 operator/(const real_t p_scalar) const;
_FORCE_INLINE_ Vector3 operator-() const;
@@ -166,7 +168,7 @@ struct Vector3 {
y = p_ivec.y;
z = p_ivec.z;
}
- _FORCE_INLINE_ Vector3(real_t p_x, real_t p_y, real_t p_z) {
+ _FORCE_INLINE_ Vector3(const real_t p_x, const real_t p_y, const real_t p_z) {
x = p_x;
y = p_y;
z = p_z;
@@ -206,14 +208,14 @@ Vector3 Vector3::round() const {
return Vector3(Math::round(x), Math::round(y), Math::round(z));
}
-Vector3 Vector3::lerp(const Vector3 &p_to, real_t p_weight) const {
+Vector3 Vector3::lerp(const Vector3 &p_to, const real_t p_weight) const {
return Vector3(
x + (p_weight * (p_to.x - x)),
y + (p_weight * (p_to.y - y)),
z + (p_weight * (p_to.z - z)));
}
-Vector3 Vector3::slerp(const Vector3 &p_to, real_t p_weight) const {
+Vector3 Vector3::slerp(const Vector3 &p_to, const real_t p_weight) const {
real_t theta = angle_to(p_to);
return rotated(cross(p_to).normalized(), theta * p_weight);
}
@@ -301,29 +303,41 @@ Vector3 Vector3::operator/(const Vector3 &p_v) const {
return Vector3(x / p_v.x, y / p_v.y, z / p_v.z);
}
-Vector3 &Vector3::operator*=(real_t p_scalar) {
+Vector3 &Vector3::operator*=(const real_t p_scalar) {
x *= p_scalar;
y *= p_scalar;
z *= p_scalar;
return *this;
}
-_FORCE_INLINE_ Vector3 operator*(real_t p_scalar, const Vector3 &p_vec) {
+_FORCE_INLINE_ Vector3 operator*(const float p_scalar, const Vector3 &p_vec) {
return p_vec * p_scalar;
}
-Vector3 Vector3::operator*(real_t p_scalar) const {
+_FORCE_INLINE_ Vector3 operator*(const double p_scalar, const Vector3 &p_vec) {
+ return p_vec * p_scalar;
+}
+
+_FORCE_INLINE_ Vector3 operator*(const int32_t p_scalar, const Vector3 &p_vec) {
+ return p_vec * p_scalar;
+}
+
+_FORCE_INLINE_ Vector3 operator*(const int64_t p_scalar, const Vector3 &p_vec) {
+ return p_vec * p_scalar;
+}
+
+Vector3 Vector3::operator*(const real_t p_scalar) const {
return Vector3(x * p_scalar, y * p_scalar, z * p_scalar);
}
-Vector3 &Vector3::operator/=(real_t p_scalar) {
+Vector3 &Vector3::operator/=(const real_t p_scalar) {
x /= p_scalar;
y /= p_scalar;
z /= p_scalar;
return *this;
}
-Vector3 Vector3::operator/(real_t p_scalar) const {
+Vector3 Vector3::operator/(const real_t p_scalar) const {
return Vector3(x / p_scalar, y / p_scalar, z / p_scalar);
}
diff --git a/core/math/vector3i.cpp b/core/math/vector3i.cpp
index 167fa3221d..d3a57af77c 100644
--- a/core/math/vector3i.cpp
+++ b/core/math/vector3i.cpp
@@ -30,12 +30,12 @@
#include "vector3i.h"
-void Vector3i::set_axis(int p_axis, int32_t p_value) {
+void Vector3i::set_axis(const int p_axis, const int32_t p_value) {
ERR_FAIL_INDEX(p_axis, 3);
coord[p_axis] = p_value;
}
-int32_t Vector3i::get_axis(int p_axis) const {
+int32_t Vector3i::get_axis(const int p_axis) const {
ERR_FAIL_INDEX_V(p_axis, 3, 0);
return operator[](p_axis);
}
@@ -48,6 +48,13 @@ int Vector3i::max_axis() const {
return x < y ? (y < z ? 2 : 1) : (x < z ? 2 : 0);
}
+Vector3i Vector3i::clamp(const Vector3i &p_min, const Vector3i &p_max) const {
+ return Vector3i(
+ CLAMP(x, p_min.x, p_max.x),
+ CLAMP(y, p_min.y, p_max.y),
+ CLAMP(z, p_min.z, p_max.z));
+}
+
Vector3i::operator String() const {
- return (itos(x) + ", " + itos(y) + ", " + itos(z));
+ return "(" + itos(x) + ", " + itos(y) + ", " + itos(z) + ")";
}
diff --git a/core/math/vector3i.h b/core/math/vector3i.h
index b0411fb62e..9308d09045 100644
--- a/core/math/vector3i.h
+++ b/core/math/vector3i.h
@@ -51,16 +51,16 @@ struct Vector3i {
int32_t coord[3] = { 0 };
};
- _FORCE_INLINE_ const int32_t &operator[](int p_axis) const {
+ _FORCE_INLINE_ const int32_t &operator[](const int p_axis) const {
return coord[p_axis];
}
- _FORCE_INLINE_ int32_t &operator[](int p_axis) {
+ _FORCE_INLINE_ int32_t &operator[](const int p_axis) {
return coord[p_axis];
}
- void set_axis(int p_axis, int32_t p_value);
- int32_t get_axis(int p_axis) const;
+ void set_axis(const int p_axis, const int32_t p_value);
+ int32_t get_axis(const int p_axis) const;
int min_axis() const;
int max_axis() const;
@@ -69,6 +69,7 @@ struct Vector3i {
_FORCE_INLINE_ Vector3i abs() const;
_FORCE_INLINE_ Vector3i sign() const;
+ Vector3i clamp(const Vector3i &p_min, const Vector3i &p_max) const;
/* Operators */
@@ -83,12 +84,12 @@ struct Vector3i {
_FORCE_INLINE_ Vector3i &operator%=(const Vector3i &p_v);
_FORCE_INLINE_ Vector3i operator%(const Vector3i &p_v) const;
- _FORCE_INLINE_ Vector3i &operator*=(int32_t p_scalar);
- _FORCE_INLINE_ Vector3i operator*(int32_t p_scalar) const;
- _FORCE_INLINE_ Vector3i &operator/=(int32_t p_scalar);
- _FORCE_INLINE_ Vector3i operator/(int32_t p_scalar) const;
- _FORCE_INLINE_ Vector3i &operator%=(int32_t p_scalar);
- _FORCE_INLINE_ Vector3i operator%(int32_t p_scalar) const;
+ _FORCE_INLINE_ Vector3i &operator*=(const int32_t p_scalar);
+ _FORCE_INLINE_ Vector3i operator*(const int32_t p_scalar) const;
+ _FORCE_INLINE_ Vector3i &operator/=(const int32_t p_scalar);
+ _FORCE_INLINE_ Vector3i operator/(const int32_t p_scalar) const;
+ _FORCE_INLINE_ Vector3i &operator%=(const int32_t p_scalar);
+ _FORCE_INLINE_ Vector3i operator%(const int32_t p_scalar) const;
_FORCE_INLINE_ Vector3i operator-() const;
@@ -102,7 +103,7 @@ struct Vector3i {
operator String() const;
_FORCE_INLINE_ Vector3i() {}
- _FORCE_INLINE_ Vector3i(int32_t p_x, int32_t p_y, int32_t p_z) {
+ _FORCE_INLINE_ Vector3i(const int32_t p_x, const int32_t p_y, const int32_t p_z) {
x = p_x;
y = p_y;
z = p_z;
@@ -174,40 +175,52 @@ Vector3i Vector3i::operator%(const Vector3i &p_v) const {
return Vector3i(x % p_v.x, y % p_v.y, z % p_v.z);
}
-Vector3i &Vector3i::operator*=(int32_t p_scalar) {
+Vector3i &Vector3i::operator*=(const int32_t p_scalar) {
x *= p_scalar;
y *= p_scalar;
z *= p_scalar;
return *this;
}
-_FORCE_INLINE_ Vector3i operator*(int32_t p_scalar, const Vector3i &p_vec) {
- return p_vec * p_scalar;
+_FORCE_INLINE_ Vector3i operator*(const int32_t p_scalar, const Vector3i &p_vector) {
+ return p_vector * p_scalar;
}
-Vector3i Vector3i::operator*(int32_t p_scalar) const {
+_FORCE_INLINE_ Vector3i operator*(const int64_t p_scalar, const Vector3i &p_vector) {
+ return p_vector * p_scalar;
+}
+
+_FORCE_INLINE_ Vector3i operator*(const float p_scalar, const Vector3i &p_vector) {
+ return p_vector * p_scalar;
+}
+
+_FORCE_INLINE_ Vector3i operator*(const double p_scalar, const Vector3i &p_vector) {
+ return p_vector * p_scalar;
+}
+
+Vector3i Vector3i::operator*(const int32_t p_scalar) const {
return Vector3i(x * p_scalar, y * p_scalar, z * p_scalar);
}
-Vector3i &Vector3i::operator/=(int32_t p_scalar) {
+Vector3i &Vector3i::operator/=(const int32_t p_scalar) {
x /= p_scalar;
y /= p_scalar;
z /= p_scalar;
return *this;
}
-Vector3i Vector3i::operator/(int32_t p_scalar) const {
+Vector3i Vector3i::operator/(const int32_t p_scalar) const {
return Vector3i(x / p_scalar, y / p_scalar, z / p_scalar);
}
-Vector3i &Vector3i::operator%=(int32_t p_scalar) {
+Vector3i &Vector3i::operator%=(const int32_t p_scalar) {
x %= p_scalar;
y %= p_scalar;
z %= p_scalar;
return *this;
}
-Vector3i Vector3i::operator%(int32_t p_scalar) const {
+Vector3i Vector3i::operator%(const int32_t p_scalar) const {
return Vector3i(x % p_scalar, y % p_scalar, z % p_scalar);
}
diff --git a/core/multiplayer/SCsub b/core/multiplayer/SCsub
new file mode 100644
index 0000000000..19a6549225
--- /dev/null
+++ b/core/multiplayer/SCsub
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env.add_source_files(env.core_sources, "*.cpp")
diff --git a/core/multiplayer/multiplayer.h b/core/multiplayer/multiplayer.h
new file mode 100644
index 0000000000..00c81d3c9a
--- /dev/null
+++ b/core/multiplayer/multiplayer.h
@@ -0,0 +1,80 @@
+/*************************************************************************/
+/* multiplayer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_H
+#define MULTIPLAYER_H
+
+#include "core/variant/binder_common.h"
+
+#include "core/string/string_name.h"
+
+namespace Multiplayer {
+
+enum TransferMode {
+ TRANSFER_MODE_UNRELIABLE,
+ TRANSFER_MODE_ORDERED,
+ TRANSFER_MODE_RELIABLE
+};
+
+enum RPCMode {
+ RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)
+ RPC_MODE_ANY, // Any peer can call this RPC
+ RPC_MODE_AUTHORITY, // / Only the node's multiplayer authority (server by default) can call this RPC
+};
+
+struct RPCConfig {
+ StringName name;
+ RPCMode rpc_mode = RPC_MODE_DISABLED;
+ bool sync = false;
+ TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
+ int channel = 0;
+
+ bool operator==(RPCConfig const &p_other) const {
+ return name == p_other.name;
+ }
+};
+
+struct SortRPCConfig {
+ StringName::AlphCompare compare;
+ bool operator()(const RPCConfig &p_a, const RPCConfig &p_b) const {
+ return compare(p_a.name, p_b.name);
+ }
+};
+
+}; // namespace Multiplayer
+
+// This is needed for proper docs generation (i.e. not "Multiplayer."-prefixed).
+typedef Multiplayer::RPCMode RPCMode;
+typedef Multiplayer::TransferMode TransferMode;
+
+VARIANT_ENUM_CAST(RPCMode);
+VARIANT_ENUM_CAST(TransferMode);
+
+#endif // MULTIPLAYER_H
diff --git a/core/multiplayer/multiplayer_api.cpp b/core/multiplayer/multiplayer_api.cpp
new file mode 100644
index 0000000000..9543f77c1e
--- /dev/null
+++ b/core/multiplayer/multiplayer_api.cpp
@@ -0,0 +1,668 @@
+/*************************************************************************/
+/* multiplayer_api.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multiplayer_api.h"
+
+#include "core/debugger/engine_debugger.h"
+#include "core/io/marshalls.h"
+#include "core/multiplayer/multiplayer_replicator.h"
+#include "core/multiplayer/rpc_manager.h"
+#include "scene/main/node.h"
+
+#include <stdint.h>
+
+#ifdef DEBUG_ENABLED
+#include "core/os/os.h"
+#endif
+
+#ifdef DEBUG_ENABLED
+void MultiplayerAPI::profile_bandwidth(const String &p_inout, int p_size) {
+ if (EngineDebugger::is_profiling("multiplayer")) {
+ Array values;
+ values.push_back("bandwidth");
+ values.push_back(p_inout);
+ values.push_back(OS::get_singleton()->get_ticks_msec());
+ values.push_back(p_size);
+ EngineDebugger::profiler_add_frame_data("multiplayer", values);
+ }
+}
+#endif
+
+void MultiplayerAPI::poll() {
+ if (!multiplayer_peer.is_valid() || multiplayer_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED) {
+ return;
+ }
+
+ multiplayer_peer->poll();
+
+ if (!multiplayer_peer.is_valid()) { // It's possible that polling might have resulted in a disconnection, so check here.
+ return;
+ }
+
+ while (multiplayer_peer->get_available_packet_count()) {
+ int sender = multiplayer_peer->get_packet_peer();
+ const uint8_t *packet;
+ int len;
+
+ Error err = multiplayer_peer->get_packet(&packet, len);
+ if (err != OK) {
+ ERR_PRINT("Error getting packet!");
+ break; // Something is wrong!
+ }
+
+ remote_sender_id = sender;
+ _process_packet(sender, packet, len);
+ remote_sender_id = 0;
+
+ if (!multiplayer_peer.is_valid()) {
+ break; // It's also possible that a packet or RPC caused a disconnection, so also check here.
+ }
+ }
+ if (multiplayer_peer.is_valid() && multiplayer_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTED) {
+ replicator->poll();
+ }
+}
+
+void MultiplayerAPI::clear() {
+ replicator->clear();
+ connected_peers.clear();
+ path_get_cache.clear();
+ path_send_cache.clear();
+ packet_cache.clear();
+ last_send_cache_id = 1;
+}
+
+void MultiplayerAPI::set_root_node(Node *p_node) {
+ root_node = p_node;
+}
+
+Node *MultiplayerAPI::get_root_node() {
+ return root_node;
+}
+
+void MultiplayerAPI::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) {
+ if (p_peer == multiplayer_peer) {
+ return; // Nothing to do
+ }
+
+ ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED,
+ "Supplied MultiplayerPeer must be connecting or connected.");
+
+ if (multiplayer_peer.is_valid()) {
+ multiplayer_peer->disconnect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer));
+ multiplayer_peer->disconnect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer));
+ multiplayer_peer->disconnect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server));
+ multiplayer_peer->disconnect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed));
+ multiplayer_peer->disconnect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected));
+ clear();
+ }
+
+ multiplayer_peer = p_peer;
+
+ if (multiplayer_peer.is_valid()) {
+ multiplayer_peer->connect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer));
+ multiplayer_peer->connect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer));
+ multiplayer_peer->connect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server));
+ multiplayer_peer->connect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed));
+ multiplayer_peer->connect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected));
+ }
+}
+
+Ref<MultiplayerPeer> MultiplayerAPI::get_multiplayer_peer() const {
+ return multiplayer_peer;
+}
+
+void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ ERR_FAIL_COND_MSG(root_node == nullptr, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it.");
+ ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small.");
+
+#ifdef DEBUG_ENABLED
+ profile_bandwidth("in", p_packet_len);
+#endif
+
+ // Extract the `packet_type` from the LSB three bits:
+ uint8_t packet_type = p_packet[0] & CMD_MASK;
+
+ switch (packet_type) {
+ case NETWORK_COMMAND_SIMPLIFY_PATH: {
+ _process_simplify_path(p_from, p_packet, p_packet_len);
+ } break;
+
+ case NETWORK_COMMAND_CONFIRM_PATH: {
+ _process_confirm_path(p_from, p_packet, p_packet_len);
+ } break;
+
+ case NETWORK_COMMAND_REMOTE_CALL: {
+ rpc_manager->process_rpc(p_from, p_packet, p_packet_len);
+ } break;
+
+ case NETWORK_COMMAND_RAW: {
+ _process_raw(p_from, p_packet, p_packet_len);
+ } break;
+ case NETWORK_COMMAND_SPAWN: {
+ replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, true);
+ } break;
+ case NETWORK_COMMAND_DESPAWN: {
+ replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, false);
+ } break;
+ case NETWORK_COMMAND_SYNC: {
+ replicator->process_sync(p_from, p_packet, p_packet_len);
+ } break;
+ }
+}
+
+void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small.");
+ int ofs = 1;
+
+ String methods_md5;
+ methods_md5.parse_utf8((const char *)(p_packet + ofs), 32);
+ ofs += 33;
+
+ int id = decode_uint32(&p_packet[ofs]);
+ ofs += 4;
+
+ String paths;
+ paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs);
+
+ NodePath path = paths;
+
+ if (!path_get_cache.has(p_from)) {
+ path_get_cache[p_from] = PathGetCache();
+ }
+
+ Node *node = root_node->get_node(path);
+ ERR_FAIL_COND(node == nullptr);
+ const bool valid_rpc_checksum = rpc_manager->get_rpc_md5(node) == methods_md5;
+ if (valid_rpc_checksum == false) {
+ ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
+ }
+
+ PathGetCache::NodeInfo ni;
+ ni.path = path;
+
+ path_get_cache[p_from].nodes[id] = ni;
+
+ // Encode path to send ack.
+ CharString pname = String(path).utf8();
+ int len = encode_cstring(pname.get_data(), nullptr);
+
+ Vector<uint8_t> packet;
+
+ packet.resize(1 + 1 + len);
+ packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH;
+ packet.write[1] = valid_rpc_checksum;
+ encode_cstring(pname.get_data(), &packet.write[2]);
+
+ multiplayer_peer->set_transfer_channel(0);
+ multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
+ multiplayer_peer->set_target_peer(p_from);
+ multiplayer_peer->put_packet(packet.ptr(), packet.size());
+}
+
+void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small.");
+
+ const bool valid_rpc_checksum = p_packet[1];
+
+ String paths;
+ paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2);
+
+ NodePath path = paths;
+
+ if (valid_rpc_checksum == false) {
+ ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
+ }
+
+ PathSentCache *psc = path_send_cache.getptr(path);
+ ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache.");
+
+ Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from);
+ ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path.");
+ E->get() = true;
+}
+
+bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) {
+ bool has_all_peers = true;
+ List<int> peers_to_add; // If one is missing, take note to add it.
+
+ for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
+ if (p_target < 0 && E->get() == -p_target) {
+ continue; // Continue, excluded.
+ }
+
+ if (p_target > 0 && E->get() != p_target) {
+ continue; // Continue, not for this peer.
+ }
+
+ Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get());
+
+ if (!F || !F->get()) {
+ // Path was not cached, or was cached but is unconfirmed.
+ if (!F) {
+ // Not cached at all, take note.
+ peers_to_add.push_back(E->get());
+ }
+
+ has_all_peers = false;
+ }
+ }
+
+ if (peers_to_add.size() > 0) {
+ // Those that need to be added, send a message for this.
+
+ // Encode function name.
+ const CharString path = String(p_path).utf8();
+ const int path_len = encode_cstring(path.get_data(), nullptr);
+
+ // Extract MD5 from rpc methods list.
+ const String methods_md5 = rpc_manager->get_rpc_md5(p_node);
+ const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder.
+
+ Vector<uint8_t> packet;
+ packet.resize(1 + 4 + path_len + methods_md5_len);
+ int ofs = 0;
+
+ packet.write[ofs] = NETWORK_COMMAND_SIMPLIFY_PATH;
+ ofs += 1;
+
+ ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]);
+
+ ofs += encode_uint32(psc->id, &packet.write[ofs]);
+
+ ofs += encode_cstring(path.get_data(), &packet.write[ofs]);
+
+ for (int &E : peers_to_add) {
+ multiplayer_peer->set_target_peer(E); // To all of you.
+ multiplayer_peer->set_transfer_channel(0);
+ multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
+ multiplayer_peer->put_packet(packet.ptr(), packet.size());
+
+ psc->confirmed_peers.insert(E, false); // Insert into confirmed, but as false since it was not confirmed.
+ }
+ }
+
+ return has_all_peers;
+}
+
+// The variant is compressed and encoded; The first byte contains all the meta
+// information and the format is:
+// - The first LSB 5 bits are used for the variant type.
+// - The next two bits are used to store the encoding mode.
+// - The most significant is used to store the boolean value.
+#define VARIANT_META_TYPE_MASK 0x1F
+#define VARIANT_META_EMODE_MASK 0x60
+#define VARIANT_META_BOOL_MASK 0x80
+#define ENCODE_8 0 << 5
+#define ENCODE_16 1 << 5
+#define ENCODE_32 2 << 5
+#define ENCODE_64 3 << 5
+Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) {
+ // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31
+ CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK);
+
+ uint8_t *buf = r_buffer;
+ r_len = 0;
+ uint8_t encode_mode = 0;
+
+ switch (p_variant.get_type()) {
+ case Variant::BOOL: {
+ if (buf) {
+ // We still have 1 free bit in the meta, so let's use it.
+ buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0;
+ buf[0] |= encode_mode | p_variant.get_type();
+ }
+ r_len += 1;
+ } break;
+ case Variant::INT: {
+ if (buf) {
+ // Reserve the first byte for the meta.
+ buf += 1;
+ }
+ r_len += 1;
+ int64_t val = p_variant;
+ if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) {
+ // Use 8 bit
+ encode_mode = ENCODE_8;
+ if (buf) {
+ buf[0] = val;
+ }
+ r_len += 1;
+ } else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) {
+ // Use 16 bit
+ encode_mode = ENCODE_16;
+ if (buf) {
+ encode_uint16(val, buf);
+ }
+ r_len += 2;
+ } else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) {
+ // Use 32 bit
+ encode_mode = ENCODE_32;
+ if (buf) {
+ encode_uint32(val, buf);
+ }
+ r_len += 4;
+ } else {
+ // Use 64 bit
+ encode_mode = ENCODE_64;
+ if (buf) {
+ encode_uint64(val, buf);
+ }
+ r_len += 8;
+ }
+ // Store the meta
+ if (buf) {
+ buf -= 1;
+ buf[0] = encode_mode | p_variant.get_type();
+ }
+ } break;
+ default:
+ // Any other case is not yet compressed.
+ Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding);
+ if (err != OK) {
+ return err;
+ }
+ if (r_buffer) {
+ // The first byte is not used by the marshalling, so store the type
+ // so we know how to decompress and decode this variant.
+ r_buffer[0] = p_variant.get_type();
+ }
+ }
+
+ return OK;
+}
+
+Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) {
+ const uint8_t *buf = p_buffer;
+ int len = p_len;
+
+ ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
+ uint8_t type = buf[0] & VARIANT_META_TYPE_MASK;
+ uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK;
+
+ ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
+
+ switch (type) {
+ case Variant::BOOL: {
+ bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0;
+ r_variant = val;
+ if (r_len) {
+ *r_len = 1;
+ }
+ } break;
+ case Variant::INT: {
+ buf += 1;
+ len -= 1;
+ if (r_len) {
+ *r_len = 1;
+ }
+ if (encode_mode == ENCODE_8) {
+ // 8 bits.
+ ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
+ int8_t val = buf[0];
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 1;
+ }
+ } else if (encode_mode == ENCODE_16) {
+ // 16 bits.
+ ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA);
+ int16_t val = decode_uint16(buf);
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 2;
+ }
+ } else if (encode_mode == ENCODE_32) {
+ // 32 bits.
+ ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+ int32_t val = decode_uint32(buf);
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 4;
+ }
+ } else {
+ // 64 bits.
+ ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
+ int64_t val = decode_uint64(buf);
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 8;
+ }
+ }
+ } break;
+ default:
+ Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+void MultiplayerAPI::_add_peer(int p_id) {
+ connected_peers.insert(p_id);
+ path_get_cache.insert(p_id, PathGetCache());
+ if (is_server()) {
+ replicator->spawn_all(p_id);
+ }
+ emit_signal(SNAME("peer_connected"), p_id);
+}
+
+void MultiplayerAPI::_del_peer(int p_id) {
+ connected_peers.erase(p_id);
+ // Cleanup get cache.
+ path_get_cache.erase(p_id);
+ // Cleanup sent cache.
+ // Some refactoring is needed to make this faster and do paths GC.
+ List<NodePath> keys;
+ path_send_cache.get_key_list(&keys);
+ for (const NodePath &E : keys) {
+ PathSentCache *psc = path_send_cache.getptr(E);
+ psc->confirmed_peers.erase(p_id);
+ }
+ emit_signal(SNAME("peer_disconnected"), p_id);
+}
+
+void MultiplayerAPI::_connected_to_server() {
+ emit_signal(SNAME("connected_to_server"));
+}
+
+void MultiplayerAPI::_connection_failed() {
+ emit_signal(SNAME("connection_failed"));
+}
+
+void MultiplayerAPI::_server_disconnected() {
+ emit_signal(SNAME("server_disconnected"));
+}
+
+Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, Multiplayer::TransferMode p_mode, int p_channel) {
+ ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet.");
+ ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no multiplayer peer is active.");
+ ERR_FAIL_COND_V_MSG(multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a multiplayer peer which is not connected.");
+
+ if (packet_cache.size() < p_data.size() + 1) {
+ packet_cache.resize(p_data.size() + 1);
+ }
+
+ const uint8_t *r = p_data.ptr();
+ packet_cache.write[0] = NETWORK_COMMAND_RAW;
+ memcpy(&packet_cache.write[1], &r[0], p_data.size());
+
+ multiplayer_peer->set_target_peer(p_to);
+ multiplayer_peer->set_transfer_channel(p_channel);
+ multiplayer_peer->set_transfer_mode(p_mode);
+
+ return multiplayer_peer->put_packet(packet_cache.ptr(), p_data.size() + 1);
+}
+
+void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small.");
+
+ Vector<uint8_t> out;
+ int len = p_packet_len - 1;
+ out.resize(len);
+ {
+ uint8_t *w = out.ptrw();
+ memcpy(&w[0], &p_packet[1], len);
+ }
+ emit_signal(SNAME("peer_packet"), p_from, out);
+}
+
+bool MultiplayerAPI::is_cache_confirmed(NodePath p_path, int p_peer) {
+ const PathSentCache *psc = path_send_cache.getptr(p_path);
+ ERR_FAIL_COND_V(!psc, false);
+ const Map<int, bool>::Element *F = psc->confirmed_peers.find(p_peer);
+ ERR_FAIL_COND_V(!F, false); // Should never happen.
+ return F->get();
+}
+
+bool MultiplayerAPI::send_confirm_path(Node *p_node, NodePath p_path, int p_peer_id, int &r_id) {
+ // See if the path is cached.
+ PathSentCache *psc = path_send_cache.getptr(p_path);
+ if (!psc) {
+ // Path is not cached, create.
+ path_send_cache[p_path] = PathSentCache();
+ psc = path_send_cache.getptr(p_path);
+ psc->id = last_send_cache_id++;
+ }
+ r_id = psc->id;
+
+ // See if all peers have cached path (if so, call can be fast).
+ return _send_confirm_path(p_node, p_path, psc, p_peer_id);
+}
+
+Node *MultiplayerAPI::get_cached_node(int p_from, uint32_t p_node_id) {
+ Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from);
+ ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d.", p_from));
+
+ Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(p_node_id);
+ ERR_FAIL_COND_V_MSG(!F, nullptr, vformat("ID %d not found in cache of peer %d.", p_node_id, p_from));
+
+ PathGetCache::NodeInfo *ni = &F->get();
+ Node *node = root_node->get_node(ni->path);
+ if (!node) {
+ ERR_PRINT("Failed to get cached path: " + String(ni->path) + ".");
+ }
+ return node;
+}
+
+int MultiplayerAPI::get_unique_id() const {
+ ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), 0, "No multiplayer peer is assigned. Unable to get unique ID.");
+ return multiplayer_peer->get_unique_id();
+}
+
+bool MultiplayerAPI::is_server() const {
+ return multiplayer_peer.is_valid() && multiplayer_peer->is_server();
+}
+
+void MultiplayerAPI::set_refuse_new_connections(bool p_refuse) {
+ ERR_FAIL_COND_MSG(!multiplayer_peer.is_valid(), "No multiplayer peer is assigned. Unable to set 'refuse_new_connections'.");
+ multiplayer_peer->set_refuse_new_connections(p_refuse);
+}
+
+bool MultiplayerAPI::is_refusing_new_connections() const {
+ ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), false, "No multiplayer peer is assigned. Unable to get 'refuse_new_connections'.");
+ return multiplayer_peer->is_refusing_new_connections();
+}
+
+Vector<int> MultiplayerAPI::get_peer_ids() const {
+ ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), Vector<int>(), "No multiplayer peer is assigned. Assume no peers are connected.");
+
+ Vector<int> ret;
+ for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
+ ret.push_back(E->get());
+ }
+
+ return ret;
+}
+
+void MultiplayerAPI::set_allow_object_decoding(bool p_enable) {
+ allow_object_decoding = p_enable;
+}
+
+bool MultiplayerAPI::is_object_decoding_allowed() const {
+ return allow_object_decoding;
+}
+
+void MultiplayerAPI::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) {
+ replicator->scene_enter_exit_notify(p_scene, p_node, p_enter);
+}
+
+void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+ rpc_manager->rpcp(p_node, p_peer_id, p_method, p_arg, p_argcount);
+}
+
+void MultiplayerAPI::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
+ ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node);
+ ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &MultiplayerAPI::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("has_multiplayer_peer"), &MultiplayerAPI::has_multiplayer_peer);
+ ClassDB::bind_method(D_METHOD("get_multiplayer_peer"), &MultiplayerAPI::get_multiplayer_peer);
+ ClassDB::bind_method(D_METHOD("set_multiplayer_peer", "peer"), &MultiplayerAPI::set_multiplayer_peer);
+ ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerAPI::get_unique_id);
+ ClassDB::bind_method(D_METHOD("is_server"), &MultiplayerAPI::is_server);
+ ClassDB::bind_method(D_METHOD("get_remote_sender_id"), &MultiplayerAPI::get_remote_sender_id);
+ ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll);
+ ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear);
+
+ ClassDB::bind_method(D_METHOD("get_peers"), &MultiplayerAPI::get_peer_ids);
+ ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "refuse"), &MultiplayerAPI::set_refuse_new_connections);
+ ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerAPI::is_refusing_new_connections);
+ ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding);
+ ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed);
+ ClassDB::bind_method(D_METHOD("get_replicator"), &MultiplayerAPI::get_replicator);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_multiplayer_peer", "get_multiplayer_peer");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_root_node", "get_root_node");
+ ADD_PROPERTY_DEFAULT("refuse_new_connections", false);
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replicator", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerReplicator", PROPERTY_USAGE_NONE), "", "get_replicator");
+
+ ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet")));
+ ADD_SIGNAL(MethodInfo("connected_to_server"));
+ ADD_SIGNAL(MethodInfo("connection_failed"));
+ ADD_SIGNAL(MethodInfo("server_disconnected"));
+}
+
+MultiplayerAPI::MultiplayerAPI() {
+ replicator = memnew(MultiplayerReplicator(this));
+ rpc_manager = memnew(RPCManager(this));
+ clear();
+}
+
+MultiplayerAPI::~MultiplayerAPI() {
+ clear();
+ memdelete(replicator);
+ memdelete(rpc_manager);
+}
diff --git a/core/io/multiplayer_api.h b/core/multiplayer/multiplayer_api.h
index 7f88b53a27..1fb0318403 100644
--- a/core/io/multiplayer_api.h
+++ b/core/multiplayer/multiplayer_api.h
@@ -31,11 +31,39 @@
#ifndef MULTIPLAYER_API_H
#define MULTIPLAYER_API_H
-#include "core/io/networked_multiplayer_peer.h"
-#include "core/object/reference.h"
+#include "core/multiplayer/multiplayer.h"
+#include "core/multiplayer/multiplayer_peer.h"
+#include "core/object/ref_counted.h"
-class MultiplayerAPI : public Reference {
- GDCLASS(MultiplayerAPI, Reference);
+class MultiplayerReplicator;
+class RPCManager;
+
+class MultiplayerAPI : public RefCounted {
+ GDCLASS(MultiplayerAPI, RefCounted);
+
+public:
+ enum NetworkCommands {
+ NETWORK_COMMAND_REMOTE_CALL = 0,
+ NETWORK_COMMAND_SIMPLIFY_PATH,
+ NETWORK_COMMAND_CONFIRM_PATH,
+ NETWORK_COMMAND_RAW,
+ NETWORK_COMMAND_SPAWN,
+ NETWORK_COMMAND_DESPAWN,
+ NETWORK_COMMAND_SYNC,
+ };
+
+ // For each command, the 4 MSB can contain custom flags, as defined by subsystems.
+ enum {
+ CMD_FLAG_0_SHIFT = 4,
+ CMD_FLAG_1_SHIFT = 5,
+ CMD_FLAG_2_SHIFT = 6,
+ CMD_FLAG_3_SHIFT = 7,
+ };
+
+ // This is the mask that will be used to extract the command.
+ enum {
+ CMD_MASK = 7, // 0x7 -> 0b00001111
+ };
private:
//path sent caches
@@ -54,75 +82,52 @@ private:
Map<int, NodeInfo> nodes;
};
- Ref<NetworkedMultiplayerPeer> network_peer;
- int rpc_sender_id = 0;
+ Ref<MultiplayerPeer> multiplayer_peer;
Set<int> connected_peers;
+ int remote_sender_id = 0;
+ int remote_sender_override = 0;
+
HashMap<NodePath, PathSentCache> path_send_cache;
Map<int, PathGetCache> path_get_cache;
int last_send_cache_id;
Vector<uint8_t> packet_cache;
+
Node *root_node = nullptr;
bool allow_object_decoding = false;
+ MultiplayerReplicator *replicator = nullptr;
+ RPCManager *rpc_manager = nullptr;
+
protected:
static void _bind_methods();
+ bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target);
void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len);
void _process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len);
void _process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len);
- Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len);
- void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
- void _process_rset(Node *p_node, const uint16_t p_rpc_property_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len);
- void _send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount);
- bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target);
-
- Error _encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len);
- Error _decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len);
-
public:
- enum NetworkCommands {
- NETWORK_COMMAND_REMOTE_CALL = 0,
- NETWORK_COMMAND_REMOTE_SET,
- NETWORK_COMMAND_SIMPLIFY_PATH,
- NETWORK_COMMAND_CONFIRM_PATH,
- NETWORK_COMMAND_RAW,
- };
-
- enum NetworkNodeIdCompression {
- NETWORK_NODE_ID_COMPRESSION_8 = 0,
- NETWORK_NODE_ID_COMPRESSION_16,
- NETWORK_NODE_ID_COMPRESSION_32,
- };
-
- enum NetworkNameIdCompression {
- NETWORK_NAME_ID_COMPRESSION_8 = 0,
- NETWORK_NAME_ID_COMPRESSION_16,
- };
-
- enum RPCMode {
- RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)
- RPC_MODE_REMOTE, // Using rpc() on it will call method / set property in all remote peers
- RPC_MODE_MASTER, // Using rpc() on it will call method on wherever the master is, be it local or remote
- RPC_MODE_PUPPET, // Using rpc() on it will call method for all puppets
- RPC_MODE_REMOTESYNC, // Using rpc() on it will call method / set property in all remote peers and locally
- RPC_MODE_MASTERSYNC, // Using rpc() on it will call method / set property in the master peer and locally
- RPC_MODE_PUPPETSYNC, // Using rpc() on it will call method / set property in all puppets peers and locally
- };
-
void poll();
void clear();
void set_root_node(Node *p_node);
Node *get_root_node();
- void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer);
- Ref<NetworkedMultiplayerPeer> get_network_peer() const;
- Error send_bytes(Vector<uint8_t> p_data, int p_to = NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST, NetworkedMultiplayerPeer::TransferMode p_mode = NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer);
+ Ref<MultiplayerPeer> get_multiplayer_peer() const;
+
+ Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, Multiplayer::TransferMode p_mode = Multiplayer::TRANSFER_MODE_RELIABLE, int p_channel = 0);
+
+ Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len);
+ Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len);
// Called by Node.rpc
- void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount);
- // Called by Node.rset
- void rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value);
+ void rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
+ // Called by Node._notification
+ void scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter);
+ // Called by replicator
+ bool send_confirm_path(Node *p_node, NodePath p_path, int p_target, int &p_id);
+ Node *get_cached_node(int p_from, uint32_t p_node_id);
+ bool is_cache_confirmed(NodePath p_path, int p_peer);
void _add_peer(int p_id);
void _del_peer(int p_id);
@@ -130,21 +135,28 @@ public:
void _connection_failed();
void _server_disconnected();
- bool has_network_peer() const { return network_peer.is_valid(); }
- Vector<int> get_network_connected_peers() const;
- int get_rpc_sender_id() const { return rpc_sender_id; }
- int get_network_unique_id() const;
- bool is_network_server() const;
- void set_refuse_new_network_connections(bool p_refuse);
- bool is_refusing_new_network_connections() const;
+ bool has_multiplayer_peer() const { return multiplayer_peer.is_valid(); }
+ Vector<int> get_peer_ids() const;
+ const Set<int> get_connected_peers() const { return connected_peers; }
+ int get_remote_sender_id() const { return remote_sender_override ? remote_sender_override : remote_sender_id; }
+ void set_remote_sender_override(int p_id) { remote_sender_override = p_id; }
+ int get_unique_id() const;
+ bool is_server() const;
+ void set_refuse_new_connections(bool p_refuse);
+ bool is_refusing_new_connections() const;
void set_allow_object_decoding(bool p_enable);
bool is_object_decoding_allowed() const;
+ MultiplayerReplicator *get_replicator() const { return replicator; }
+ RPCManager *get_rpc_manager() const { return rpc_manager; }
+
+#ifdef DEBUG_ENABLED
+ void profile_bandwidth(const String &p_inout, int p_size);
+#endif
+
MultiplayerAPI();
~MultiplayerAPI();
};
-VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode);
-
#endif // MULTIPLAYER_API_H
diff --git a/core/io/networked_multiplayer_peer.cpp b/core/multiplayer/multiplayer_peer.cpp
index b6af046e77..40847102d8 100644
--- a/core/io/networked_multiplayer_peer.cpp
+++ b/core/multiplayer/multiplayer_peer.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* networked_multiplayer_peer.cpp */
+/* multiplayer_peer.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,29 +28,52 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "networked_multiplayer_peer.h"
+#include "multiplayer_peer.h"
-void NetworkedMultiplayerPeer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &NetworkedMultiplayerPeer::set_transfer_mode);
- ClassDB::bind_method(D_METHOD("get_transfer_mode"), &NetworkedMultiplayerPeer::get_transfer_mode);
- ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &NetworkedMultiplayerPeer::set_target_peer);
+#include "core/os/os.h"
- ClassDB::bind_method(D_METHOD("get_packet_peer"), &NetworkedMultiplayerPeer::get_packet_peer);
+uint32_t MultiplayerPeer::generate_unique_id() const {
+ uint32_t hash = 0;
- ClassDB::bind_method(D_METHOD("poll"), &NetworkedMultiplayerPeer::poll);
+ while (hash == 0 || hash == 1) {
+ hash = hash_djb2_one_32(
+ (uint32_t)OS::get_singleton()->get_ticks_usec());
+ hash = hash_djb2_one_32(
+ (uint32_t)OS::get_singleton()->get_unix_time(), hash);
+ hash = hash_djb2_one_32(
+ (uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash);
+ hash = hash_djb2_one_32(
+ (uint32_t)((uint64_t)this), hash); // Rely on ASLR heap
+ hash = hash_djb2_one_32(
+ (uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack
- ClassDB::bind_method(D_METHOD("get_connection_status"), &NetworkedMultiplayerPeer::get_connection_status);
- ClassDB::bind_method(D_METHOD("get_unique_id"), &NetworkedMultiplayerPeer::get_unique_id);
+ hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion
+ }
- ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "enable"), &NetworkedMultiplayerPeer::set_refuse_new_connections);
- ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &NetworkedMultiplayerPeer::is_refusing_new_connections);
+ return hash;
+}
+
+void MultiplayerPeer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &MultiplayerPeer::set_transfer_channel);
+ ClassDB::bind_method(D_METHOD("get_transfer_channel"), &MultiplayerPeer::get_transfer_channel);
+ ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &MultiplayerPeer::set_transfer_mode);
+ ClassDB::bind_method(D_METHOD("get_transfer_mode"), &MultiplayerPeer::get_transfer_mode);
+ ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &MultiplayerPeer::set_target_peer);
+
+ ClassDB::bind_method(D_METHOD("get_packet_peer"), &MultiplayerPeer::get_packet_peer);
+
+ ClassDB::bind_method(D_METHOD("poll"), &MultiplayerPeer::poll);
+
+ ClassDB::bind_method(D_METHOD("get_connection_status"), &MultiplayerPeer::get_connection_status);
+ ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerPeer::get_unique_id);
+ ClassDB::bind_method(D_METHOD("generate_unique_id"), &MultiplayerPeer::generate_unique_id);
+
+ ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "enable"), &MultiplayerPeer::set_refuse_new_connections);
+ ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerPeer::is_refusing_new_connections);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode");
-
- BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE);
- BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE_ORDERED);
- BIND_ENUM_CONSTANT(TRANSFER_MODE_RELIABLE);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel", PROPERTY_HINT_RANGE, "0,255,1"), "set_transfer_channel", "get_transfer_channel");
BIND_ENUM_CONSTANT(CONNECTION_DISCONNECTED);
BIND_ENUM_CONSTANT(CONNECTION_CONNECTING);
diff --git a/core/io/networked_multiplayer_peer.h b/core/multiplayer/multiplayer_peer.h
index 7c90f97d88..ba00c3b41b 100644
--- a/core/io/networked_multiplayer_peer.h
+++ b/core/multiplayer/multiplayer_peer.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* networked_multiplayer_peer.h */
+/* multiplayer_peer.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -32,9 +32,10 @@
#define NETWORKED_MULTIPLAYER_PEER_H
#include "core/io/packet_peer.h"
+#include "core/multiplayer/multiplayer.h"
-class NetworkedMultiplayerPeer : public PacketPeer {
- GDCLASS(NetworkedMultiplayerPeer, PacketPeer);
+class MultiplayerPeer : public PacketPeer {
+ GDCLASS(MultiplayerPeer, PacketPeer);
protected:
static void _bind_methods();
@@ -44,11 +45,6 @@ public:
TARGET_PEER_BROADCAST = 0,
TARGET_PEER_SERVER = 1
};
- enum TransferMode {
- TRANSFER_MODE_UNRELIABLE,
- TRANSFER_MODE_UNRELIABLE_ORDERED,
- TRANSFER_MODE_RELIABLE,
- };
enum ConnectionStatus {
CONNECTION_DISCONNECTED,
@@ -56,8 +52,10 @@ public:
CONNECTION_CONNECTED,
};
- virtual void set_transfer_mode(TransferMode p_mode) = 0;
- virtual TransferMode get_transfer_mode() const = 0;
+ virtual void set_transfer_channel(int p_channel) = 0;
+ virtual int get_transfer_channel() const = 0;
+ virtual void set_transfer_mode(Multiplayer::TransferMode p_mode) = 0;
+ virtual Multiplayer::TransferMode get_transfer_mode() const = 0;
virtual void set_target_peer(int p_peer_id) = 0;
virtual int get_packet_peer() const = 0;
@@ -72,11 +70,11 @@ public:
virtual bool is_refusing_new_connections() const = 0;
virtual ConnectionStatus get_connection_status() const = 0;
+ uint32_t generate_unique_id() const;
- NetworkedMultiplayerPeer() {}
+ MultiplayerPeer() {}
};
-VARIANT_ENUM_CAST(NetworkedMultiplayerPeer::TransferMode)
-VARIANT_ENUM_CAST(NetworkedMultiplayerPeer::ConnectionStatus)
+VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus)
#endif // NETWORKED_MULTIPLAYER_PEER_H
diff --git a/core/multiplayer/multiplayer_replicator.cpp b/core/multiplayer/multiplayer_replicator.cpp
new file mode 100644
index 0000000000..a4ea74327c
--- /dev/null
+++ b/core/multiplayer/multiplayer_replicator.cpp
@@ -0,0 +1,791 @@
+/*************************************************************************/
+/* multiplayer_replicator.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "core/multiplayer/multiplayer_replicator.h"
+
+#include "core/io/marshalls.h"
+#include "scene/main/node.h"
+#include "scene/resources/packed_scene.h"
+
+#define MAKE_ROOM(m_amount) \
+ if (packet_cache.size() < m_amount) \
+ packet_cache.resize(m_amount);
+
+Error MultiplayerReplicator::_sync_all_default(const ResourceUID::ID &p_scene_id, int p_peer) {
+ ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER);
+ SceneConfig &cfg = replications[p_scene_id];
+ int full_size = 0;
+ bool same_size = true;
+ int last_size = 0;
+ bool all_raw = true;
+ struct EncodeInfo {
+ int size = 0;
+ bool raw = false;
+ List<Variant> state;
+ };
+ Map<ObjectID, struct EncodeInfo> state;
+ if (tracked_objects.has(p_scene_id)) {
+ for (const ObjectID &obj_id : tracked_objects[p_scene_id]) {
+ Object *obj = ObjectDB::get_instance(obj_id);
+ if (obj) {
+ struct EncodeInfo info;
+ Error err = _get_state(cfg.sync_properties, obj, info.state);
+ ERR_CONTINUE(err);
+ err = _encode_state(info.state, nullptr, info.size, &info.raw);
+ ERR_CONTINUE(err);
+ state[obj_id] = info;
+ full_size += info.size;
+ if (last_size && info.size != last_size) {
+ same_size = false;
+ }
+ all_raw = all_raw && info.raw;
+ last_size = info.size;
+ }
+ }
+ }
+ // Default implementation do not send empty updates.
+ if (!full_size) {
+ return OK;
+ }
+#ifdef DEBUG_ENABLED
+ if (full_size > 4096 && cfg.sync_interval) {
+ WARN_PRINT_ONCE(vformat("The timed state update for scene %d is big (%d bytes) consider optimizing it", p_scene_id));
+ }
+#endif
+ if (same_size) {
+ // This is fast and small. Should we allow more than 256 objects per type?
+ // This costs us 1 byte.
+ MAKE_ROOM(SYNC_CMD_OFFSET + 1 + 2 + 2 + full_size);
+ } else {
+ MAKE_ROOM(SYNC_CMD_OFFSET + 1 + 2 + state.size() * 2 + full_size);
+ }
+ int ofs = 0;
+ uint8_t *ptr = packet_cache.ptrw();
+ ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC | (same_size ? BYTE_OR_ZERO_FLAG : 0);
+ ofs = 1;
+ ofs += encode_uint64(p_scene_id, &ptr[ofs]);
+ ptr[ofs] = cfg.sync_recv++;
+ ofs += 1;
+ ofs += encode_uint16(state.size(), &ptr[ofs]);
+ if (same_size) {
+ ofs += encode_uint16(last_size + (all_raw ? 1 << 15 : 0), &ptr[ofs]);
+ }
+ for (const ObjectID &obj_id : tracked_objects[p_scene_id]) {
+ if (!state.has(obj_id)) {
+ continue;
+ }
+ struct EncodeInfo &info = state[obj_id];
+ Object *obj = ObjectDB::get_instance(obj_id);
+ ERR_CONTINUE(!obj);
+ int size = 0;
+ if (!same_size) {
+ // We need to encode the size of every object.
+ ofs += encode_uint16(info.size + (info.raw ? 1 << 15 : 0), &ptr[ofs]);
+ }
+ Error err = _encode_state(info.state, &ptr[ofs], size, &info.raw);
+ ERR_CONTINUE(err);
+ ofs += size;
+ }
+ Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
+ peer->set_target_peer(p_peer);
+ peer->set_transfer_channel(0);
+ peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_UNRELIABLE);
+ return peer->put_packet(ptr, ofs);
+}
+
+void MultiplayerReplicator::_process_default_sync(const ResourceUID::ID &p_id, const uint8_t *p_packet, int p_packet_len) {
+ ERR_FAIL_COND_MSG(p_packet_len < SYNC_CMD_OFFSET + 5, "Invalid spawn packet received");
+ ERR_FAIL_COND_MSG(!replications.has(p_id), "Invalid spawn ID received " + itos(p_id));
+ SceneConfig &cfg = replications[p_id];
+ ERR_FAIL_COND_MSG(cfg.mode != REPLICATION_MODE_SERVER || multiplayer->is_server(), "The defualt implementation only allows sync packets from the server");
+ const bool same_size = p_packet[0] & BYTE_OR_ZERO_FLAG;
+ int ofs = SYNC_CMD_OFFSET;
+ int time = p_packet[ofs];
+ // Skip old update.
+ if (time < cfg.sync_recv && cfg.sync_recv - time < 127) {
+ return;
+ }
+ cfg.sync_recv = time;
+ ofs += 1;
+ int count = decode_uint16(&p_packet[ofs]);
+ ofs += 2;
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!tracked_objects.has(p_id) || tracked_objects[p_id].size() != count);
+#else
+ if (!tracked_objects.has(p_id) || tracked_objects[p_id].size() != count) {
+ return;
+ }
+#endif
+ int data_size = 0;
+ bool raw = false;
+ if (same_size) {
+ // This is fast and optimized.
+ data_size = decode_uint16(&p_packet[ofs]);
+ raw = (data_size & (1 << 15)) != 0;
+ data_size = data_size & ~(1 << 15);
+ ofs += 2;
+ ERR_FAIL_COND(p_packet_len - ofs < data_size * count);
+ }
+ for (const ObjectID &obj_id : tracked_objects[p_id]) {
+ Object *obj = ObjectDB::get_instance(obj_id);
+ ERR_CONTINUE(!obj);
+ if (!same_size) {
+ // This is slow and wasteful.
+ data_size = decode_uint16(&p_packet[ofs]);
+ raw = (data_size & (1 << 15)) != 0;
+ data_size = data_size & ~(1 << 15);
+ ofs += 2;
+ ERR_FAIL_COND(p_packet_len - ofs < data_size);
+ }
+ int size = 0;
+ Error err = _decode_state(cfg.sync_properties, obj, &p_packet[ofs], data_size, size, raw);
+ ofs += data_size;
+ ERR_CONTINUE(err);
+ ERR_CONTINUE(size != data_size);
+ }
+}
+
+Error MultiplayerReplicator::_send_default_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, Object *p_obj, const NodePath &p_path, bool p_spawn) {
+ ERR_FAIL_COND_V(p_spawn && !p_obj, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER);
+ Error err;
+ // Prepare state
+ List<Variant> state_variants;
+ int state_len = 0;
+ const SceneConfig &cfg = replications[p_scene_id];
+ if (p_spawn) {
+ if ((err = _get_state(cfg.properties, p_obj, state_variants)) != OK) {
+ return err;
+ }
+ }
+
+ bool is_raw = false;
+ if (state_variants.size() == 1 && state_variants[0].get_type() == Variant::PACKED_BYTE_ARRAY) {
+ is_raw = true;
+ const PackedByteArray pba = state_variants[0];
+ state_len = pba.size();
+ } else if (state_variants.size()) {
+ err = _encode_state(state_variants, nullptr, state_len);
+ ERR_FAIL_COND_V(err, err);
+ } else {
+ is_raw = true;
+ }
+
+ int ofs = 0;
+
+ // Prepare simplified path
+ const Node *root_node = multiplayer->get_root_node();
+ ERR_FAIL_COND_V(!root_node, ERR_UNCONFIGURED);
+ NodePath rel_path = (root_node->get_path()).rel_path_to(p_path);
+ const Vector<StringName> names = rel_path.get_names();
+ ERR_FAIL_COND_V(names.size() < 2, ERR_INVALID_PARAMETER);
+
+ NodePath parent = NodePath(names.subarray(0, names.size() - 2), false);
+ ERR_FAIL_COND_V_MSG(!root_node->has_node(parent), ERR_INVALID_PARAMETER, "Path not found: " + parent);
+
+ int path_id = 0;
+ multiplayer->send_confirm_path(root_node->get_node(parent), parent, p_peer_id, path_id);
+
+ // Encode name and parent ID.
+ CharString cname = String(names[names.size() - 1]).utf8();
+ int nlen = encode_cstring(cname.get_data(), nullptr);
+ MAKE_ROOM(SPAWN_CMD_OFFSET + 4 + 4 + nlen + state_len);
+ uint8_t *ptr = packet_cache.ptrw();
+ ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) | (is_raw ? BYTE_OR_ZERO_FLAG : 0);
+ ofs = 1;
+ ofs += encode_uint64(p_scene_id, &ptr[ofs]);
+ ofs += encode_uint32(path_id, &ptr[ofs]);
+ ofs += encode_uint32(nlen, &ptr[ofs]);
+ ofs += encode_cstring(cname.get_data(), &ptr[ofs]);
+
+ // Encode state.
+ if (!is_raw) {
+ _encode_state(state_variants, &ptr[ofs], state_len);
+ } else if (state_len) {
+ PackedByteArray pba = state_variants[0];
+ memcpy(&ptr[ofs], pba.ptr(), state_len);
+ }
+
+ Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
+ peer->set_target_peer(p_peer_id);
+ peer->set_transfer_channel(0);
+ peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
+ return peer->put_packet(ptr, ofs + state_len);
+}
+
+void MultiplayerReplicator::_process_default_spawn_despawn(int p_from, const ResourceUID::ID &p_scene_id, const uint8_t *p_packet, int p_packet_len, bool p_spawn) {
+ ERR_FAIL_COND_MSG(p_packet_len < SPAWN_CMD_OFFSET + 9, "Invalid spawn packet received");
+ int ofs = SPAWN_CMD_OFFSET;
+ uint32_t node_target = decode_uint32(&p_packet[ofs]);
+ Node *parent = multiplayer->get_cached_node(p_from, node_target);
+ ofs += 4;
+ ERR_FAIL_COND_MSG(parent == nullptr, "Invalid packet received. Requested node was not found.");
+
+ uint32_t name_len = decode_uint32(&p_packet[ofs]);
+ ofs += 4;
+ ERR_FAIL_COND_MSG(name_len > uint32_t(p_packet_len - ofs), vformat("Invalid spawn packet size: %d, wants: %d", p_packet_len, ofs + name_len));
+ ERR_FAIL_COND_MSG(name_len < 1, "Zero spawn name size.");
+
+ const String name = String::utf8((const char *)&p_packet[ofs], name_len);
+ // We need to make sure no trickery happens here (e.g. despawning a subpath), but we want to allow autogenerated ("@") node names.
+ ERR_FAIL_COND_MSG(name.validate_node_name() != name.replace("@", ""), vformat("Invalid node name received: '%s'", name));
+ ofs += name_len;
+
+ const SceneConfig &cfg = replications[p_scene_id];
+ if (cfg.mode == REPLICATION_MODE_SERVER && p_from == 1) {
+ String scene_path = ResourceUID::get_singleton()->get_id_path(p_scene_id);
+ if (p_spawn) {
+ const bool is_raw = ((p_packet[0] & BYTE_OR_ZERO_FLAG) >> BYTE_OR_ZERO_SHIFT) == 1;
+
+ ERR_FAIL_COND_MSG(parent->has_node(name), vformat("Unable to spawn node. Node already exists: %s/%s", parent->get_path(), name));
+ RES res = ResourceLoader::load(scene_path);
+ ERR_FAIL_COND_MSG(!res.is_valid(), "Unable to load scene to spawn at path: " + scene_path);
+ PackedScene *scene = Object::cast_to<PackedScene>(res.ptr());
+ ERR_FAIL_COND(!scene);
+ Node *node = scene->instantiate();
+ ERR_FAIL_COND(!node);
+ replicated_nodes[node->get_instance_id()] = p_scene_id;
+ _track(p_scene_id, node);
+ int size;
+ _decode_state(cfg.properties, node, &p_packet[ofs], p_packet_len - ofs, size, is_raw);
+ parent->_add_child_nocheck(node, name);
+ emit_signal(SNAME("spawned"), p_scene_id, node);
+ } else {
+ ERR_FAIL_COND_MSG(!parent->has_node(name), vformat("Path not found: %s/%s", parent->get_path(), name));
+ Node *node = parent->get_node(name);
+ ERR_FAIL_COND_MSG(!replicated_nodes.has(node->get_instance_id()), vformat("Trying to despawn a Node that was not replicated: %s/%s", parent->get_path(), name));
+ emit_signal(SNAME("despawned"), p_scene_id, node);
+ _untrack(p_scene_id, node);
+ replicated_nodes.erase(node->get_instance_id());
+ node->queue_delete();
+ }
+ } else {
+ PackedByteArray data;
+ if (p_packet_len > ofs) {
+ data.resize(p_packet_len - ofs);
+ memcpy(data.ptrw(), &p_packet[ofs], data.size());
+ }
+ if (p_spawn) {
+ emit_signal(SNAME("spawn_requested"), p_from, p_scene_id, parent, name, data);
+ } else {
+ emit_signal(SNAME("despawn_requested"), p_from, p_scene_id, parent, name, data);
+ }
+ }
+}
+
+void MultiplayerReplicator::process_spawn_despawn(int p_from, const uint8_t *p_packet, int p_packet_len, bool p_spawn) {
+ ERR_FAIL_COND_MSG(p_packet_len < SPAWN_CMD_OFFSET, "Invalid spawn packet received");
+ ResourceUID::ID id = decode_uint64(&p_packet[1]);
+ ERR_FAIL_COND_MSG(!replications.has(id), "Invalid spawn ID received " + itos(id));
+
+ const SceneConfig &cfg = replications[id];
+ if (cfg.on_spawn_despawn_receive.is_valid()) {
+ int ofs = SPAWN_CMD_OFFSET;
+ bool is_raw = ((p_packet[0] & BYTE_OR_ZERO_FLAG) >> BYTE_OR_ZERO_SHIFT) == 1;
+ Variant data;
+ int left = p_packet_len - ofs;
+ if (is_raw && left) {
+ PackedByteArray pba;
+ pba.resize(left);
+ memcpy(pba.ptrw(), &p_packet[ofs], pba.size());
+ data = pba;
+ } else if (left) {
+ ERR_FAIL_COND(decode_variant(data, &p_packet[ofs], left) != OK);
+ }
+
+ Variant args[4];
+ args[0] = p_from;
+ args[1] = id;
+ args[2] = data;
+ args[3] = p_spawn;
+ const Variant *argp[] = { &args[0], &args[1], &args[2], &args[3] };
+ Callable::CallError ce;
+ Variant ret;
+ cfg.on_spawn_despawn_receive.call(argp, 4, ret, ce);
+ ERR_FAIL_COND_MSG(ce.error != Callable::CallError::CALL_OK, "Custom receive function failed");
+ } else {
+ _process_default_spawn_despawn(p_from, id, p_packet, p_packet_len, p_spawn);
+ }
+}
+
+void MultiplayerReplicator::process_sync(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ ERR_FAIL_COND_MSG(p_packet_len < SPAWN_CMD_OFFSET, "Invalid spawn packet received");
+ ResourceUID::ID id = decode_uint64(&p_packet[1]);
+ ERR_FAIL_COND_MSG(!replications.has(id), "Invalid spawn ID received " + itos(id));
+ const SceneConfig &cfg = replications[id];
+ if (cfg.on_sync_receive.is_valid()) {
+ Array objs;
+ if (tracked_objects.has(id)) {
+ objs.resize(tracked_objects[id].size());
+ int idx = 0;
+ for (const ObjectID &obj_id : tracked_objects[id]) {
+ objs[idx++] = ObjectDB::get_instance(obj_id);
+ }
+ }
+ PackedByteArray pba;
+ pba.resize(p_packet_len - SYNC_CMD_OFFSET);
+ if (pba.size()) {
+ memcpy(pba.ptrw(), p_packet + SYNC_CMD_OFFSET, p_packet_len - SYNC_CMD_OFFSET);
+ }
+ Variant args[4] = { p_from, id, objs, pba };
+ Variant *argp[4] = { args, &args[1], &args[2], &args[3] };
+ Callable::CallError ce;
+ Variant ret;
+ cfg.on_sync_receive.call((const Variant **)argp, 4, ret, ce);
+ ERR_FAIL_COND_MSG(ce.error != Callable::CallError::CALL_OK, "Custom sync function failed");
+ } else {
+ ERR_FAIL_COND_MSG(p_from != 1, "Default sync implementation only allow syncing from server to client");
+ _process_default_sync(id, p_packet, p_packet_len);
+ }
+}
+
+Error MultiplayerReplicator::_get_state(const List<StringName> &p_properties, const Object *p_obj, List<Variant> &r_variant) {
+ ERR_FAIL_COND_V_MSG(!p_obj, ERR_INVALID_PARAMETER, "Cannot encode null object");
+ for (const StringName &prop : p_properties) {
+ bool valid = false;
+ const Variant v = p_obj->get(prop, &valid);
+ ERR_FAIL_COND_V_MSG(!valid, ERR_INVALID_DATA, vformat("Property '%s' not found.", prop));
+ r_variant.push_back(v);
+ }
+ return OK;
+}
+
+Error MultiplayerReplicator::_encode_state(const List<Variant> &p_variants, uint8_t *p_buffer, int &r_len, bool *r_raw) {
+ r_len = 0;
+ int size = 0;
+
+ // Try raw encoding optimization.
+ if (r_raw && p_variants.size() == 1) {
+ *r_raw = false;
+ const Variant v = p_variants[0];
+ if (v.get_type() == Variant::PACKED_BYTE_ARRAY) {
+ *r_raw = true;
+ const PackedByteArray pba = v;
+ if (p_buffer) {
+ memcpy(p_buffer, pba.ptr(), pba.size());
+ }
+ r_len += pba.size();
+ } else {
+ multiplayer->encode_and_compress_variant(v, p_buffer, size);
+ r_len += size;
+ }
+ return OK;
+ }
+
+ // Regular encoding.
+ for (const Variant &v : p_variants) {
+ multiplayer->encode_and_compress_variant(v, p_buffer ? p_buffer + r_len : nullptr, size);
+ r_len += size;
+ }
+ return OK;
+}
+
+Error MultiplayerReplicator::_decode_state(const List<StringName> &p_properties, Object *p_obj, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw) {
+ r_len = 0;
+ int argc = p_properties.size();
+ if (argc == 0 && p_raw) {
+ ERR_FAIL_COND_V_MSG(p_len != 0, ERR_INVALID_DATA, "Buffer has trailing bytes.");
+ return OK;
+ }
+ ERR_FAIL_COND_V(p_raw && argc != 1, ERR_INVALID_DATA);
+ if (p_raw) {
+ r_len = p_len;
+ PackedByteArray pba;
+ pba.resize(p_len);
+ memcpy(pba.ptrw(), p_buffer, p_len);
+ p_obj->set(p_properties[0], pba);
+ return OK;
+ }
+
+ Vector<Variant> args;
+ Vector<const Variant *> argp;
+ args.resize(argc);
+
+ for (int i = 0; i < argc; i++) {
+ ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small.");
+
+ int vlen;
+ Error err = multiplayer->decode_and_decompress_variant(args.write[i], &p_buffer[r_len], p_len - r_len, &vlen);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid packet received. Unable to decode state variable.");
+ r_len += vlen;
+ }
+ ERR_FAIL_COND_V_MSG(p_len - r_len != 0, ERR_INVALID_DATA, "Buffer has trailing bytes.");
+
+ int i = 0;
+ for (const StringName &prop : p_properties) {
+ p_obj->set(prop, args[i]);
+ i += 1;
+ }
+ return OK;
+}
+
+Error MultiplayerReplicator::spawn_config(const ResourceUID::ID &p_id, ReplicationMode p_mode, const TypedArray<StringName> &p_props, const Callable &p_on_send, const Callable &p_on_recv) {
+ ERR_FAIL_COND_V(p_mode < REPLICATION_MODE_NONE || p_mode > REPLICATION_MODE_CUSTOM, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!ResourceUID::get_singleton()->has_id(p_id), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V_MSG(p_on_send.is_valid() != p_on_recv.is_valid(), ERR_INVALID_PARAMETER, "Send and receive custom callables must be both valid or both empty");
+#ifdef TOOLS_ENABLED
+ if (!p_on_send.is_valid()) {
+ // We allow non scene spawning with custom callables.
+ String path = ResourceUID::get_singleton()->get_id_path(p_id);
+ RES res = ResourceLoader::load(path);
+ ERR_FAIL_COND_V(!res->is_class("PackedScene"), ERR_INVALID_PARAMETER);
+ }
+#endif
+ if (p_mode == REPLICATION_MODE_NONE) {
+ if (replications.has(p_id)) {
+ replications.erase(p_id);
+ }
+ } else {
+ SceneConfig cfg;
+ cfg.mode = p_mode;
+ for (int i = 0; i < p_props.size(); i++) {
+ cfg.properties.push_back(p_props[i]);
+ }
+ cfg.on_spawn_despawn_send = p_on_send;
+ cfg.on_spawn_despawn_receive = p_on_recv;
+ replications[p_id] = cfg;
+ }
+ return OK;
+}
+
+Error MultiplayerReplicator::sync_config(const ResourceUID::ID &p_id, uint64_t p_interval, const TypedArray<StringName> &p_props, const Callable &p_on_send, const Callable &p_on_recv) {
+ ERR_FAIL_COND_V(!ResourceUID::get_singleton()->has_id(p_id), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V_MSG(p_on_send.is_valid() != p_on_recv.is_valid(), ERR_INVALID_PARAMETER, "Send and receive custom callables must be both valid or both empty");
+ ERR_FAIL_COND_V(!replications.has(p_id), ERR_UNCONFIGURED);
+ SceneConfig &cfg = replications[p_id];
+ ERR_FAIL_COND_V_MSG(p_interval && cfg.mode != REPLICATION_MODE_SERVER && !p_on_send.is_valid(), ERR_INVALID_PARAMETER, "Timed updates in custom mode are only allowed if custom callbacks are also specified");
+ for (int i = 0; i < p_props.size(); i++) {
+ cfg.sync_properties.push_back(p_props[i]);
+ }
+ cfg.on_sync_send = p_on_send;
+ cfg.on_sync_receive = p_on_recv;
+ cfg.sync_interval = p_interval * 1000;
+ return OK;
+}
+
+Error MultiplayerReplicator::_send_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, bool p_spawn) {
+ int data_size = 0;
+ int is_raw = false;
+ if (p_data.get_type() == Variant::PACKED_BYTE_ARRAY) {
+ const PackedByteArray pba = p_data;
+ is_raw = true;
+ data_size = p_data.operator PackedByteArray().size();
+ } else if (p_data.get_type() == Variant::NIL) {
+ is_raw = true;
+ } else {
+ Error err = encode_variant(p_data, nullptr, data_size);
+ ERR_FAIL_COND_V(err, err);
+ }
+ MAKE_ROOM(SPAWN_CMD_OFFSET + data_size);
+ uint8_t *ptr = packet_cache.ptrw();
+ ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) + ((is_raw ? 1 : 0) << BYTE_OR_ZERO_SHIFT);
+ encode_uint64(p_scene_id, &ptr[1]);
+ if (p_data.get_type() == Variant::PACKED_BYTE_ARRAY) {
+ const PackedByteArray pba = p_data;
+ memcpy(&ptr[SPAWN_CMD_OFFSET], pba.ptr(), pba.size());
+ } else if (data_size) {
+ encode_variant(p_data, &ptr[SPAWN_CMD_OFFSET], data_size);
+ }
+ Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
+ peer->set_target_peer(p_peer_id);
+ peer->set_transfer_channel(0);
+ peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
+ return peer->put_packet(ptr, SPAWN_CMD_OFFSET + data_size);
+}
+
+Error MultiplayerReplicator::send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, const NodePath &p_path) {
+ ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));
+ const SceneConfig &cfg = replications[p_scene_id];
+ if (cfg.on_spawn_despawn_send.is_valid()) {
+ return _send_spawn_despawn(p_peer_id, p_scene_id, p_data, true);
+ } else {
+ ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server(), ERR_UNAVAILABLE, "Manual despawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests.");
+ NodePath path = p_path;
+ Object *obj = p_data.get_type() == Variant::OBJECT ? p_data.get_validated_object() : nullptr;
+ if (path.is_empty() && obj) {
+ Node *node = Object::cast_to<Node>(obj);
+ if (node && node->is_inside_tree()) {
+ path = node->get_path();
+ }
+ }
+ ERR_FAIL_COND_V_MSG(path.is_empty(), ERR_INVALID_PARAMETER, "Despawn default implementation requires a despawn path, or the data to be a node inside the SceneTree");
+ return _send_default_spawn_despawn(p_peer_id, p_scene_id, obj, path, false);
+ }
+}
+
+Error MultiplayerReplicator::send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, const NodePath &p_path) {
+ ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));
+ const SceneConfig &cfg = replications[p_scene_id];
+ if (cfg.on_spawn_despawn_send.is_valid()) {
+ return _send_spawn_despawn(p_peer_id, p_scene_id, p_data, false);
+ } else {
+ ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server(), ERR_UNAVAILABLE, "Manual spawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests.");
+ NodePath path = p_path;
+ Object *obj = p_data.get_type() == Variant::OBJECT ? p_data.get_validated_object() : nullptr;
+ ERR_FAIL_COND_V_MSG(!obj, ERR_INVALID_PARAMETER, "Spawn default implementation requires the data to be an object.");
+ if (path.is_empty()) {
+ Node *node = Object::cast_to<Node>(obj);
+ if (node && node->is_inside_tree()) {
+ path = node->get_path();
+ }
+ }
+ ERR_FAIL_COND_V_MSG(path.is_empty(), ERR_INVALID_PARAMETER, "Spawn default implementation requires a spawn path, or the data to be a node inside the SceneTree");
+ return _send_default_spawn_despawn(p_peer_id, p_scene_id, obj, path, true);
+ }
+}
+
+Error MultiplayerReplicator::_spawn_despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer, bool p_spawn) {
+ ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));
+
+ const SceneConfig &cfg = replications[p_scene_id];
+ if (cfg.on_spawn_despawn_send.is_valid()) {
+ Variant args[4];
+ args[0] = p_peer;
+ args[1] = p_scene_id;
+ args[2] = p_obj;
+ args[3] = true;
+ const Variant *argp[] = { &args[0], &args[1], &args[2], &args[3] };
+ Callable::CallError ce;
+ Variant ret;
+ cfg.on_spawn_despawn_send.call(argp, 4, ret, ce);
+ ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, FAILED, "Custom send function failed");
+ return OK;
+ } else {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V_MSG(!p_obj, ERR_INVALID_PARAMETER, "Only nodes can be replicated by the default implementation");
+ return _send_default_spawn_despawn(p_peer, p_scene_id, node, node->get_path(), p_spawn);
+ }
+}
+
+Error MultiplayerReplicator::spawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer) {
+ return _spawn_despawn(p_scene_id, p_obj, p_peer, true);
+}
+
+Error MultiplayerReplicator::despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer) {
+ return _spawn_despawn(p_scene_id, p_obj, p_peer, false);
+}
+
+PackedByteArray MultiplayerReplicator::encode_state(const ResourceUID::ID &p_scene_id, const Object *p_obj, bool p_initial) {
+ PackedByteArray state;
+ ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), state, vformat("Spawnable not found: %d", p_scene_id));
+ const SceneConfig &cfg = replications[p_scene_id];
+ int len = 0;
+ List<Variant> state_vars;
+ const List<StringName> props = p_initial ? cfg.properties : cfg.sync_properties;
+ Error err = _get_state(props, p_obj, state_vars);
+ ERR_FAIL_COND_V_MSG(err != OK, state, "Unable to retrieve object state.");
+ err = _encode_state(state_vars, nullptr, len);
+ ERR_FAIL_COND_V_MSG(err != OK, state, "Unable to encode object state.");
+ state.resize(len);
+ _encode_state(state_vars, state.ptrw(), len);
+ return state;
+}
+
+Error MultiplayerReplicator::decode_state(const ResourceUID::ID &p_scene_id, Object *p_obj, const PackedByteArray p_data, bool p_initial) {
+ ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));
+ const SceneConfig &cfg = replications[p_scene_id];
+ const List<StringName> props = p_initial ? cfg.properties : cfg.sync_properties;
+ int size;
+ return _decode_state(props, p_obj, p_data.ptr(), p_data.size(), size);
+}
+
+void MultiplayerReplicator::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) {
+ if (!multiplayer->has_multiplayer_peer()) {
+ return;
+ }
+ Node *root_node = multiplayer->get_root_node();
+ ERR_FAIL_COND(!p_node || !p_node->get_parent() || !root_node);
+ NodePath path = (root_node->get_path()).rel_path_to(p_node->get_parent()->get_path());
+ if (path.is_empty()) {
+ return;
+ }
+ ResourceUID::ID id = ResourceLoader::get_resource_uid(p_scene);
+ if (!replications.has(id)) {
+ return;
+ }
+ const SceneConfig &cfg = replications[id];
+ if (p_enter) {
+ if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server()) {
+ replicated_nodes[p_node->get_instance_id()] = id;
+ _track(id, p_node);
+ spawn(id, p_node, 0);
+ }
+ emit_signal(SNAME("replicated_instance_added"), id, p_node);
+ } else {
+ if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server() && replicated_nodes.has(p_node->get_instance_id())) {
+ replicated_nodes.erase(p_node->get_instance_id());
+ _untrack(id, p_node);
+ despawn(id, p_node, 0);
+ }
+ emit_signal(SNAME("replicated_instance_removed"), id, p_node);
+ }
+}
+
+void MultiplayerReplicator::spawn_all(int p_peer) {
+ for (const KeyValue<ObjectID, ResourceUID::ID> &E : replicated_nodes) {
+ // Only server mode adds to replicated_nodes, no need to check it.
+ Object *obj = ObjectDB::get_instance(E.key);
+ ERR_CONTINUE(!obj);
+ Node *node = Object::cast_to<Node>(obj);
+ ERR_CONTINUE(!node);
+ spawn(E.value, node, p_peer);
+ }
+}
+
+void MultiplayerReplicator::poll() {
+ for (KeyValue<ResourceUID::ID, SceneConfig> &E : replications) {
+ if (!E.value.sync_interval) {
+ continue;
+ }
+ if (E.value.mode == REPLICATION_MODE_SERVER && !multiplayer->is_server()) {
+ continue;
+ }
+ uint64_t time = OS::get_singleton()->get_ticks_usec();
+ if (E.value.sync_last + E.value.sync_interval <= time) {
+ sync_all(E.key, 0);
+ E.value.sync_last = time;
+ }
+ // Handle wrapping.
+ if (E.value.sync_last > time) {
+ E.value.sync_last = time;
+ }
+ }
+}
+
+void MultiplayerReplicator::track(const ResourceUID::ID &p_scene_id, Object *p_obj) {
+ ERR_FAIL_COND(!replications.has(p_scene_id));
+ const SceneConfig &cfg = replications[p_scene_id];
+ ERR_FAIL_COND_MSG(cfg.mode == REPLICATION_MODE_SERVER, "Manual object tracking is not allowed in server mode.");
+ _track(p_scene_id, p_obj);
+}
+
+void MultiplayerReplicator::_track(const ResourceUID::ID &p_scene_id, Object *p_obj) {
+ ERR_FAIL_COND(!p_obj);
+ ERR_FAIL_COND(!replications.has(p_scene_id));
+ if (!tracked_objects.has(p_scene_id)) {
+ tracked_objects[p_scene_id] = List<ObjectID>();
+ }
+ tracked_objects[p_scene_id].push_back(p_obj->get_instance_id());
+}
+
+void MultiplayerReplicator::untrack(const ResourceUID::ID &p_scene_id, Object *p_obj) {
+ ERR_FAIL_COND(!replications.has(p_scene_id));
+ const SceneConfig &cfg = replications[p_scene_id];
+ ERR_FAIL_COND_MSG(cfg.mode == REPLICATION_MODE_SERVER, "Manual object tracking is not allowed in server mode.");
+ _untrack(p_scene_id, p_obj);
+}
+
+void MultiplayerReplicator::_untrack(const ResourceUID::ID &p_scene_id, Object *p_obj) {
+ ERR_FAIL_COND(!p_obj);
+ ERR_FAIL_COND(!replications.has(p_scene_id));
+ if (tracked_objects.has(p_scene_id)) {
+ tracked_objects[p_scene_id].erase(p_obj->get_instance_id());
+ }
+}
+
+Error MultiplayerReplicator::sync_all(const ResourceUID::ID &p_scene_id, int p_peer) {
+ ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER);
+ if (!tracked_objects.has(p_scene_id)) {
+ return OK;
+ }
+ const SceneConfig &cfg = replications[p_scene_id];
+ if (cfg.on_sync_send.is_valid()) {
+ Array objs;
+ if (tracked_objects.has(p_scene_id)) {
+ objs.resize(tracked_objects[p_scene_id].size());
+ int idx = 0;
+ for (const ObjectID &obj_id : tracked_objects[p_scene_id]) {
+ objs[idx++] = ObjectDB::get_instance(obj_id);
+ }
+ }
+ Variant args[3] = { p_scene_id, objs, p_peer };
+ Variant *argp[3] = { args, &args[1], &args[2] };
+ Callable::CallError ce;
+ Variant ret;
+ cfg.on_sync_send.call((const Variant **)argp, 3, ret, ce);
+ ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, FAILED, "Custom sync function failed");
+ return OK;
+ } else if (cfg.sync_properties.size()) {
+ return _sync_all_default(p_scene_id, p_peer);
+ }
+ return OK;
+}
+
+Error MultiplayerReplicator::send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_transfer_mode, int p_channel) {
+ ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER);
+ const SceneConfig &cfg = replications[p_scene_id];
+ ERR_FAIL_COND_V_MSG(!cfg.on_sync_send.is_valid(), ERR_UNCONFIGURED, "Sending raw sync messages is only available with custom functions");
+ MAKE_ROOM(SYNC_CMD_OFFSET + p_data.size());
+ uint8_t *ptr = packet_cache.ptrw();
+ ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC;
+ encode_uint64(p_scene_id, &ptr[1]);
+ if (p_data.size()) {
+ memcpy(&ptr[SYNC_CMD_OFFSET], p_data.ptr(), p_data.size());
+ }
+ Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
+ peer->set_target_peer(p_peer_id);
+ peer->set_transfer_channel(p_channel);
+ peer->set_transfer_mode(p_transfer_mode);
+ return peer->put_packet(ptr, SYNC_CMD_OFFSET + p_data.size());
+}
+
+void MultiplayerReplicator::clear() {
+ tracked_objects.clear();
+ replicated_nodes.clear();
+}
+
+void MultiplayerReplicator::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("spawn_config", "scene_id", "spawn_mode", "properties", "custom_send", "custom_receive"), &MultiplayerReplicator::spawn_config, DEFVAL(TypedArray<StringName>()), DEFVAL(Callable()), DEFVAL(Callable()));
+ ClassDB::bind_method(D_METHOD("sync_config", "scene_id", "interval", "properties", "custom_send", "custom_receive"), &MultiplayerReplicator::sync_config, DEFVAL(TypedArray<StringName>()), DEFVAL(Callable()), DEFVAL(Callable()));
+ ClassDB::bind_method(D_METHOD("despawn", "scene_id", "object", "peer_id"), &MultiplayerReplicator::despawn, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("spawn", "scene_id", "object", "peer_id"), &MultiplayerReplicator::spawn, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("send_despawn", "peer_id", "scene_id", "data", "path"), &MultiplayerReplicator::send_despawn, DEFVAL(Variant()), DEFVAL(NodePath()));
+ ClassDB::bind_method(D_METHOD("send_spawn", "peer_id", "scene_id", "data", "path"), &MultiplayerReplicator::send_spawn, DEFVAL(Variant()), DEFVAL(NodePath()));
+ ClassDB::bind_method(D_METHOD("send_sync", "peer_id", "scene_id", "data", "transfer_mode", "channel"), &MultiplayerReplicator::send_sync, DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("sync_all", "scene_id", "peer_id"), &MultiplayerReplicator::sync_all, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("track", "scene_id", "object"), &MultiplayerReplicator::track);
+ ClassDB::bind_method(D_METHOD("untrack", "scene_id", "object"), &MultiplayerReplicator::untrack);
+ ClassDB::bind_method(D_METHOD("encode_state", "scene_id", "object", "initial"), &MultiplayerReplicator::encode_state, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("decode_state", "scene_id", "object", "data", "initial"), &MultiplayerReplicator::decode_state, DEFVAL(true));
+
+ ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
+ ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
+ ADD_SIGNAL(MethodInfo("despawn_requested", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "parent", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
+ ADD_SIGNAL(MethodInfo("spawn_requested", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "parent", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
+ ADD_SIGNAL(MethodInfo("replicated_instance_added", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
+ ADD_SIGNAL(MethodInfo("replicated_instance_removed", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
+
+ BIND_ENUM_CONSTANT(REPLICATION_MODE_NONE);
+ BIND_ENUM_CONSTANT(REPLICATION_MODE_SERVER);
+ BIND_ENUM_CONSTANT(REPLICATION_MODE_CUSTOM);
+}
diff --git a/core/multiplayer/multiplayer_replicator.h b/core/multiplayer/multiplayer_replicator.h
new file mode 100644
index 0000000000..7fa774fdf4
--- /dev/null
+++ b/core/multiplayer/multiplayer_replicator.h
@@ -0,0 +1,138 @@
+/*************************************************************************/
+/* multiplayer_replicator.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_REPLICATOR_H
+#define MULTIPLAYER_REPLICATOR_H
+
+#include "core/multiplayer/multiplayer_api.h"
+
+#include "core/io/resource_uid.h"
+#include "core/templates/hash_map.h"
+#include "core/variant/typed_array.h"
+
+class MultiplayerReplicator : public Object {
+ GDCLASS(MultiplayerReplicator, Object);
+
+public:
+ enum {
+ SPAWN_CMD_OFFSET = 9,
+ SYNC_CMD_OFFSET = 9,
+ };
+
+ enum ReplicationMode {
+ REPLICATION_MODE_NONE,
+ REPLICATION_MODE_SERVER,
+ REPLICATION_MODE_CUSTOM,
+ };
+
+ struct SceneConfig {
+ ReplicationMode mode;
+ uint64_t sync_interval = 0;
+ uint64_t sync_last = 0;
+ uint8_t sync_recv = 0;
+ List<StringName> properties;
+ List<StringName> sync_properties;
+ Callable on_spawn_despawn_send;
+ Callable on_spawn_despawn_receive;
+ Callable on_sync_send;
+ Callable on_sync_receive;
+ };
+
+protected:
+ static void _bind_methods();
+
+private:
+ enum {
+ BYTE_OR_ZERO_SHIFT = MultiplayerAPI::CMD_FLAG_0_SHIFT,
+ };
+
+ enum {
+ BYTE_OR_ZERO_FLAG = 1 << BYTE_OR_ZERO_SHIFT,
+ };
+
+ MultiplayerAPI *multiplayer = nullptr;
+ Vector<uint8_t> packet_cache;
+ Map<ResourceUID::ID, SceneConfig> replications;
+ Map<ObjectID, ResourceUID::ID> replicated_nodes;
+ HashMap<ResourceUID::ID, List<ObjectID>> tracked_objects;
+
+ // Encoding
+ Error _get_state(const List<StringName> &p_properties, const Object *p_obj, List<Variant> &r_variant);
+ Error _encode_state(const List<Variant> &p_variants, uint8_t *p_buffer, int &r_len, bool *r_raw = nullptr);
+ Error _decode_state(const List<StringName> &p_cfg, Object *p_obj, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw = false);
+
+ // Spawn
+ Error _spawn_despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer, bool p_spawn);
+ Error _send_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, bool p_spawn);
+ void _process_default_spawn_despawn(int p_from, const ResourceUID::ID &p_scene_id, const uint8_t *p_packet, int p_packet_len, bool p_spawn);
+ Error _send_default_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, Object *p_obj, const NodePath &p_path, bool p_spawn);
+
+ // Sync
+ void _process_default_sync(const ResourceUID::ID &p_id, const uint8_t *p_packet, int p_packet_len);
+ Error _sync_all_default(const ResourceUID::ID &p_scene_id, int p_peer);
+ void _track(const ResourceUID::ID &p_scene_id, Object *p_object);
+ void _untrack(const ResourceUID::ID &p_scene_id, Object *p_object);
+
+public:
+ void clear();
+
+ // Encoding
+ PackedByteArray encode_state(const ResourceUID::ID &p_scene_id, const Object *p_node, bool p_initial);
+ Error decode_state(const ResourceUID::ID &p_scene_id, Object *p_node, PackedByteArray p_data, bool p_initial);
+
+ // Spawn
+ Error spawn_config(const ResourceUID::ID &p_id, ReplicationMode p_mode, const TypedArray<StringName> &p_props = TypedArray<StringName>(), const Callable &p_on_send = Callable(), const Callable &p_on_recv = Callable());
+ Error spawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer = 0);
+ Error despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer = 0);
+ Error send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data = Variant(), const NodePath &p_path = NodePath());
+ Error send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data = Variant(), const NodePath &p_path = NodePath());
+
+ // Sync
+ Error sync_config(const ResourceUID::ID &p_id, uint64_t p_interval, const TypedArray<StringName> &p_props = TypedArray<StringName>(), const Callable &p_on_send = Callable(), const Callable &p_on_recv = Callable());
+ Error sync_all(const ResourceUID::ID &p_scene_id, int p_peer);
+ Error send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_mode, int p_channel);
+ void track(const ResourceUID::ID &p_scene_id, Object *p_object);
+ void untrack(const ResourceUID::ID &p_scene_id, Object *p_object);
+
+ // Used by MultiplayerAPI
+ void spawn_all(int p_peer);
+ void process_spawn_despawn(int p_from, const uint8_t *p_packet, int p_packet_len, bool p_spawn);
+ void process_sync(int p_from, const uint8_t *p_packet, int p_packet_len);
+ void scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter);
+ void poll();
+
+ MultiplayerReplicator(MultiplayerAPI *p_multiplayer) {
+ multiplayer = p_multiplayer;
+ }
+};
+
+VARIANT_ENUM_CAST(MultiplayerReplicator::ReplicationMode);
+
+#endif // MULTIPLAYER_REPLICATOR_H
diff --git a/core/multiplayer/rpc_manager.cpp b/core/multiplayer/rpc_manager.cpp
new file mode 100644
index 0000000000..915571375e
--- /dev/null
+++ b/core/multiplayer/rpc_manager.cpp
@@ -0,0 +1,525 @@
+/*************************************************************************/
+/* rpc_manager.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "core/multiplayer/rpc_manager.h"
+
+#include "core/debugger/engine_debugger.h"
+#include "core/io/marshalls.h"
+#include "core/multiplayer/multiplayer_api.h"
+#include "scene/main/node.h"
+
+#ifdef DEBUG_ENABLED
+_FORCE_INLINE_ void RPCManager::_profile_node_data(const String &p_what, ObjectID p_id) {
+ if (EngineDebugger::is_profiling("multiplayer")) {
+ Array values;
+ values.push_back("node");
+ values.push_back(p_id);
+ values.push_back(p_what);
+ EngineDebugger::profiler_add_frame_data("multiplayer", values);
+ }
+}
+#else
+_FORCE_INLINE_ void RPCManager::_profile_node_data(const String &p_what, ObjectID p_id) {}
+#endif
+
+// Returns the packet size stripping the node path added when the node is not yet cached.
+int get_packet_len(uint32_t p_node_target, int p_packet_len) {
+ if (p_node_target & 0x80000000) {
+ int ofs = p_node_target & 0x7FFFFFFF;
+ return p_packet_len - (p_packet_len - ofs);
+ } else {
+ return p_packet_len;
+ }
+}
+
+const Multiplayer::RPCConfig _get_rpc_config(const Node *p_node, const StringName &p_method, uint16_t &r_id) {
+ const Vector<Multiplayer::RPCConfig> node_config = p_node->get_node_rpc_methods();
+ for (int i = 0; i < node_config.size(); i++) {
+ if (node_config[i].name == p_method) {
+ r_id = ((uint16_t)i) | (1 << 15);
+ return node_config[i];
+ }
+ }
+ if (p_node->get_script_instance()) {
+ const Vector<Multiplayer::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods();
+ for (int i = 0; i < script_config.size(); i++) {
+ if (script_config[i].name == p_method) {
+ r_id = (uint16_t)i;
+ return script_config[i];
+ }
+ }
+ }
+ return Multiplayer::RPCConfig();
+}
+
+const Multiplayer::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_id) {
+ Vector<Multiplayer::RPCConfig> config;
+ uint16_t id = p_id;
+ if (id & (1 << 15)) {
+ id = id & ~(1 << 15);
+ config = p_node->get_node_rpc_methods();
+ } else if (p_node->get_script_instance()) {
+ config = p_node->get_script_instance()->get_rpc_methods();
+ }
+ if (id < config.size()) {
+ return config[id];
+ }
+ return Multiplayer::RPCConfig();
+}
+
+_FORCE_INLINE_ bool _can_call_mode(Node *p_node, Multiplayer::RPCMode mode, int p_remote_id) {
+ switch (mode) {
+ case Multiplayer::RPC_MODE_DISABLED: {
+ return false;
+ } break;
+ case Multiplayer::RPC_MODE_ANY: {
+ return true;
+ } break;
+ case Multiplayer::RPC_MODE_AUTHORITY: {
+ return !p_node->is_multiplayer_authority() && p_remote_id == p_node->get_multiplayer_authority();
+ } break;
+ }
+
+ return false;
+}
+
+String RPCManager::get_rpc_md5(const Node *p_node) {
+ String rpc_list;
+ const Vector<Multiplayer::RPCConfig> node_config = p_node->get_node_rpc_methods();
+ for (int i = 0; i < node_config.size(); i++) {
+ rpc_list += String(node_config[i].name);
+ }
+ if (p_node->get_script_instance()) {
+ const Vector<Multiplayer::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods();
+ for (int i = 0; i < script_config.size(); i++) {
+ rpc_list += String(script_config[i].name);
+ }
+ }
+ return rpc_list.md5_text();
+}
+
+Node *RPCManager::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) {
+ Node *node = nullptr;
+
+ if (p_node_target & 0x80000000) {
+ // Use full path (not cached yet).
+ int ofs = p_node_target & 0x7FFFFFFF;
+
+ ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared.");
+
+ String paths;
+ paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs);
+
+ NodePath np = paths;
+
+ node = multiplayer->get_root_node()->get_node(np);
+
+ if (!node) {
+ ERR_PRINT("Failed to get path from RPC: " + String(np) + ".");
+ }
+ return node;
+ } else {
+ // Use cached path.
+ return multiplayer->get_cached_node(p_from, p_node_target);
+ }
+}
+
+void RPCManager::process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ // Extract packet meta
+ int packet_min_size = 1;
+ int name_id_offset = 1;
+ ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
+ // Compute the meta size, which depends on the compression level.
+ int node_id_compression = (p_packet[0] & NODE_ID_COMPRESSION_FLAG) >> NODE_ID_COMPRESSION_SHIFT;
+ int name_id_compression = (p_packet[0] & NAME_ID_COMPRESSION_FLAG) >> NAME_ID_COMPRESSION_SHIFT;
+
+ switch (node_id_compression) {
+ case NETWORK_NODE_ID_COMPRESSION_8:
+ packet_min_size += 1;
+ name_id_offset += 1;
+ break;
+ case NETWORK_NODE_ID_COMPRESSION_16:
+ packet_min_size += 2;
+ name_id_offset += 2;
+ break;
+ case NETWORK_NODE_ID_COMPRESSION_32:
+ packet_min_size += 4;
+ name_id_offset += 4;
+ break;
+ default:
+ ERR_FAIL_MSG("Was not possible to extract the node id compression mode.");
+ }
+ switch (name_id_compression) {
+ case NETWORK_NAME_ID_COMPRESSION_8:
+ packet_min_size += 1;
+ break;
+ case NETWORK_NAME_ID_COMPRESSION_16:
+ packet_min_size += 2;
+ break;
+ default:
+ ERR_FAIL_MSG("Was not possible to extract the name id compression mode.");
+ }
+ ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
+
+ uint32_t node_target = 0;
+ switch (node_id_compression) {
+ case NETWORK_NODE_ID_COMPRESSION_8:
+ node_target = p_packet[1];
+ break;
+ case NETWORK_NODE_ID_COMPRESSION_16:
+ node_target = decode_uint16(p_packet + 1);
+ break;
+ case NETWORK_NODE_ID_COMPRESSION_32:
+ node_target = decode_uint32(p_packet + 1);
+ break;
+ default:
+ // Unreachable, checked before.
+ CRASH_NOW();
+ }
+
+ Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len);
+ ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found.");
+
+ uint16_t name_id = 0;
+ switch (name_id_compression) {
+ case NETWORK_NAME_ID_COMPRESSION_8:
+ name_id = p_packet[name_id_offset];
+ break;
+ case NETWORK_NAME_ID_COMPRESSION_16:
+ name_id = decode_uint16(p_packet + name_id_offset);
+ break;
+ default:
+ // Unreachable, checked before.
+ CRASH_NOW();
+ }
+
+ const int packet_len = get_packet_len(node_target, p_packet_len);
+ _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size);
+}
+
+void RPCManager::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
+ ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small.");
+
+ // Check that remote can call the RPC on this node.
+ const Multiplayer::RPCConfig config = _get_rpc_config_by_id(p_node, p_rpc_method_id);
+ ERR_FAIL_COND(config.name == StringName());
+
+ bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from);
+ ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_multiplayer_authority()) + ".");
+
+ int argc = 0;
+ bool byte_only = false;
+
+ const bool byte_only_or_no_args = p_packet[0] & BYTE_ONLY_OR_NO_ARGS_FLAG;
+ if (byte_only_or_no_args) {
+ if (p_offset < p_packet_len) {
+ // This packet contains only bytes.
+ argc = 1;
+ byte_only = true;
+ } else {
+ // This rpc calls a method without parameters.
+ }
+ } else {
+ // Normal variant, takes the argument count from the packet.
+ ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
+ argc = p_packet[p_offset];
+ p_offset += 1;
+ }
+
+ Vector<Variant> args;
+ Vector<const Variant *> argp;
+ args.resize(argc);
+ argp.resize(argc);
+
+#ifdef DEBUG_ENABLED
+ _profile_node_data("in_rpc", p_node->get_instance_id());
+#endif
+
+ if (byte_only) {
+ Vector<uint8_t> pure_data;
+ const int len = p_packet_len - p_offset;
+ pure_data.resize(len);
+ memcpy(pure_data.ptrw(), &p_packet[p_offset], len);
+ args.write[0] = pure_data;
+ argp.write[0] = &args[0];
+ p_offset += len;
+ } else {
+ for (int i = 0; i < argc; i++) {
+ ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
+
+ int vlen;
+ Error err = multiplayer->decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen);
+ ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument.");
+
+ argp.write[i] = &args[i];
+ p_offset += vlen;
+ }
+ }
+
+ Callable::CallError ce;
+
+ p_node->call(config.name, (const Variant **)argp.ptr(), argc, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce);
+ error = "RPC - " + error;
+ ERR_PRINT(error);
+ }
+}
+
+void RPCManager::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) {
+ Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
+ ERR_FAIL_COND_MSG(peer.is_null(), "Attempt to call RPC without active multiplayer peer.");
+
+ ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to call RPC while multiplayer peer is not connected yet.");
+
+ ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to call RPC while multiplayer peer is disconnected.");
+
+ ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments (>255).");
+
+ if (p_to != 0 && !multiplayer->get_connected_peers().has(ABS(p_to))) {
+ ERR_FAIL_COND_MSG(p_to == peer->get_unique_id(), "Attempt to call RPC on yourself! Peer unique ID: " + itos(peer->get_unique_id()) + ".");
+
+ ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + ".");
+ }
+
+ NodePath from_path = (multiplayer->get_root_node()->get_path()).rel_path_to(p_from->get_path());
+ ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!");
+
+ // See if all peers have cached path (if so, call can be fast).
+ int psc_id;
+ const bool has_all_peers = multiplayer->send_confirm_path(p_from, from_path, p_to, psc_id);
+
+ // Create base packet, lots of hardcode because it must be tight.
+
+ int ofs = 0;
+
+#define MAKE_ROOM(m_amount) \
+ if (packet_cache.size() < m_amount) \
+ packet_cache.resize(m_amount);
+
+ // Encode meta.
+ uint8_t command_type = MultiplayerAPI::NETWORK_COMMAND_REMOTE_CALL;
+ uint8_t node_id_compression = UINT8_MAX;
+ uint8_t name_id_compression = UINT8_MAX;
+ bool byte_only_or_no_args = false;
+
+ MAKE_ROOM(1);
+ // The meta is composed along the way, so just set 0 for now.
+ packet_cache.write[0] = 0;
+ ofs += 1;
+
+ // Encode Node ID.
+ if (has_all_peers) {
+ // Compress the node ID only if all the target peers already know it.
+ if (psc_id >= 0 && psc_id <= 255) {
+ // We can encode the id in 1 byte
+ node_id_compression = NETWORK_NODE_ID_COMPRESSION_8;
+ MAKE_ROOM(ofs + 1);
+ packet_cache.write[ofs] = static_cast<uint8_t>(psc_id);
+ ofs += 1;
+ } else if (psc_id >= 0 && psc_id <= 65535) {
+ // We can encode the id in 2 bytes
+ node_id_compression = NETWORK_NODE_ID_COMPRESSION_16;
+ MAKE_ROOM(ofs + 2);
+ encode_uint16(static_cast<uint16_t>(psc_id), &(packet_cache.write[ofs]));
+ ofs += 2;
+ } else {
+ // Too big, let's use 4 bytes.
+ node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
+ MAKE_ROOM(ofs + 4);
+ encode_uint32(psc_id, &(packet_cache.write[ofs]));
+ ofs += 4;
+ }
+ } else {
+ // The targets don't know the node yet, so we need to use 32 bits int.
+ node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
+ MAKE_ROOM(ofs + 4);
+ encode_uint32(psc_id, &(packet_cache.write[ofs]));
+ ofs += 4;
+ }
+
+ // Encode method ID
+ if (p_rpc_id <= UINT8_MAX) {
+ // The ID fits in 1 byte
+ name_id_compression = NETWORK_NAME_ID_COMPRESSION_8;
+ MAKE_ROOM(ofs + 1);
+ packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id);
+ ofs += 1;
+ } else {
+ // The ID is larger, let's use 2 bytes
+ name_id_compression = NETWORK_NAME_ID_COMPRESSION_16;
+ MAKE_ROOM(ofs + 2);
+ encode_uint16(p_rpc_id, &(packet_cache.write[ofs]));
+ ofs += 2;
+ }
+
+ if (p_argcount == 0) {
+ byte_only_or_no_args = true;
+ } else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) {
+ byte_only_or_no_args = true;
+ // Special optimization when only the byte vector is sent.
+ const Vector<uint8_t> data = *p_arg[0];
+ MAKE_ROOM(ofs + data.size());
+ memcpy(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size());
+ ofs += data.size();
+ } else {
+ // Arguments
+ MAKE_ROOM(ofs + 1);
+ packet_cache.write[ofs] = p_argcount;
+ ofs += 1;
+ for (int i = 0; i < p_argcount; i++) {
+ int len(0);
+ Error err = multiplayer->encode_and_compress_variant(*p_arg[i], nullptr, len);
+ ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
+ MAKE_ROOM(ofs + len);
+ multiplayer->encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
+ ofs += len;
+ }
+ }
+
+ ERR_FAIL_COND(command_type > 7);
+ ERR_FAIL_COND(node_id_compression > 3);
+ ERR_FAIL_COND(name_id_compression > 1);
+
+ // We can now set the meta
+ packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + (byte_only_or_no_args ? BYTE_ONLY_OR_NO_ARGS_FLAG : 0);
+
+#ifdef DEBUG_ENABLED
+ multiplayer->profile_bandwidth("out", ofs);
+#endif
+
+ // Take chance and set transfer mode, since all send methods will use it.
+ peer->set_transfer_channel(p_config.channel);
+ peer->set_transfer_mode(p_config.transfer_mode);
+
+ if (has_all_peers) {
+ // They all have verified paths, so send fast.
+ peer->set_target_peer(p_to); // To all of you.
+ peer->put_packet(packet_cache.ptr(), ofs); // A message with love.
+ } else {
+ // Unreachable because the node ID is never compressed if the peers doesn't know it.
+ CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32);
+
+ // Not all verified path, so send one by one.
+
+ // Append path at the end, since we will need it for some packets.
+ CharString pname = String(from_path).utf8();
+ int path_len = encode_cstring(pname.get_data(), nullptr);
+ MAKE_ROOM(ofs + path_len);
+ encode_cstring(pname.get_data(), &(packet_cache.write[ofs]));
+
+ for (const int &P : multiplayer->get_connected_peers()) {
+ if (p_to < 0 && P == -p_to) {
+ continue; // Continue, excluded.
+ }
+
+ if (p_to > 0 && P != p_to) {
+ continue; // Continue, not for this peer.
+ }
+
+ bool confirmed = multiplayer->is_cache_confirmed(from_path, P);
+
+ peer->set_target_peer(P); // To this one specifically.
+
+ if (confirmed) {
+ // This one confirmed path, so use id.
+ encode_uint32(psc_id, &(packet_cache.write[1]));
+ peer->put_packet(packet_cache.ptr(), ofs);
+ } else {
+ // This one did not confirm path yet, so use entire path (sorry!).
+ encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag.
+ peer->put_packet(packet_cache.ptr(), ofs + path_len);
+ }
+ }
+ }
+}
+
+void RPCManager::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+ Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
+ ERR_FAIL_COND_MSG(!peer.is_valid(), "Trying to call an RPC while no multiplayer peer is active.");
+ ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree.");
+ ERR_FAIL_COND_MSG(peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a multiplayer peer which is not connected.");
+
+ int node_id = peer->get_unique_id();
+ bool call_local_native = false;
+ bool call_local_script = false;
+ uint16_t rpc_id = UINT16_MAX;
+ const Multiplayer::RPCConfig config = _get_rpc_config(p_node, p_method, rpc_id);
+ ERR_FAIL_COND_MSG(config.name == StringName(),
+ vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is not marked for RPCs.", p_method, p_node->get_path()));
+ if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
+ if (rpc_id & (1 << 15)) {
+ call_local_native = config.sync;
+ } else {
+ call_local_script = config.sync;
+ }
+ }
+
+ if (p_peer_id != node_id) {
+#ifdef DEBUG_ENABLED
+ _profile_node_data("out_rpc", p_node->get_instance_id());
+#endif
+
+ _send_rpc(p_node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount);
+ }
+
+ if (call_local_native) {
+ Callable::CallError ce;
+
+ multiplayer->set_remote_sender_override(peer->get_unique_id());
+ p_node->call(p_method, p_arg, p_argcount, ce);
+ multiplayer->set_remote_sender_override(0);
+
+ if (ce.error != Callable::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
+ error = "rpc() aborted in local call: - " + error + ".";
+ ERR_PRINT(error);
+ return;
+ }
+ }
+
+ if (call_local_script) {
+ Callable::CallError ce;
+ ce.error = Callable::CallError::CALL_OK;
+
+ multiplayer->set_remote_sender_override(peer->get_unique_id());
+ p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce);
+ multiplayer->set_remote_sender_override(0);
+
+ if (ce.error != Callable::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
+ error = "rpc() aborted in script local call: - " + error + ".";
+ ERR_PRINT(error);
+ return;
+ }
+ }
+
+ ERR_FAIL_COND_MSG(p_peer_id == node_id && !config.sync, "RPC '" + p_method + "' on yourself is not allowed by selected mode.");
+}
diff --git a/core/multiplayer/rpc_manager.h b/core/multiplayer/rpc_manager.h
new file mode 100644
index 0000000000..7b99dee226
--- /dev/null
+++ b/core/multiplayer/rpc_manager.h
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* rpc_manager.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_RPC_H
+#define MULTIPLAYER_RPC_H
+
+#include "core/multiplayer/multiplayer.h"
+#include "core/multiplayer/multiplayer_api.h"
+#include "core/object/ref_counted.h"
+
+class RPCManager : public RefCounted {
+ GDCLASS(RPCManager, RefCounted);
+
+private:
+ enum NetworkNodeIdCompression {
+ NETWORK_NODE_ID_COMPRESSION_8 = 0,
+ NETWORK_NODE_ID_COMPRESSION_16,
+ NETWORK_NODE_ID_COMPRESSION_32,
+ };
+
+ enum NetworkNameIdCompression {
+ NETWORK_NAME_ID_COMPRESSION_8 = 0,
+ NETWORK_NAME_ID_COMPRESSION_16,
+ };
+
+ // The RPC meta is composed by a single byte that contains (starting from the least significant bit):
+ // - `NetworkCommands` in the first four bits.
+ // - `NetworkNodeIdCompression` in the next 2 bits.
+ // - `NetworkNameIdCompression` in the next 1 bit.
+ // - `byte_only_or_no_args` in the next 1 bit.
+ enum {
+ NODE_ID_COMPRESSION_SHIFT = MultiplayerAPI::CMD_FLAG_0_SHIFT, // 2 bits for this.
+ NAME_ID_COMPRESSION_SHIFT = MultiplayerAPI::CMD_FLAG_2_SHIFT,
+ BYTE_ONLY_OR_NO_ARGS_SHIFT = MultiplayerAPI::CMD_FLAG_3_SHIFT,
+ };
+
+ enum {
+ NODE_ID_COMPRESSION_FLAG = (1 << NODE_ID_COMPRESSION_SHIFT) | (1 << (NODE_ID_COMPRESSION_SHIFT + 1)), // 2 bits for this.
+ NAME_ID_COMPRESSION_FLAG = (1 << NAME_ID_COMPRESSION_SHIFT),
+ BYTE_ONLY_OR_NO_ARGS_FLAG = (1 << BYTE_ONLY_OR_NO_ARGS_SHIFT),
+ };
+
+ MultiplayerAPI *multiplayer = nullptr;
+ Vector<uint8_t> packet_cache;
+
+protected:
+ _FORCE_INLINE_ void _profile_node_data(const String &p_what, ObjectID p_id);
+ void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
+
+ void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount);
+ Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len);
+
+public:
+ // Called by Node.rpc
+ void rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
+ void process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len);
+
+ String get_rpc_md5(const Node *p_node);
+ RPCManager(MultiplayerAPI *p_multiplayer) { multiplayer = p_multiplayer; }
+};
+
+#endif // MULTIPLAYER_RPC_H
diff --git a/core/object/SCsub b/core/object/SCsub
index 5d429960e5..dc116aeb19 100644
--- a/core/object/SCsub
+++ b/core/object/SCsub
@@ -2,6 +2,11 @@
Import("env")
+import make_virtuals
+from platform_methods import run_in_subprocess
+
+env.CommandNoCache(["gdvirtual.gen.inc"], "make_virtuals.py", run_in_subprocess(make_virtuals.run))
+
env_object = env.Clone()
env_object.add_source_files(env.core_sources, "*.cpp")
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index fb7eb42738..8e92340c1e 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -359,9 +359,9 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
//must be alphabetically sorted for hash to compute
names.sort_custom<StringName::AlphCompare>();
- for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
- ClassInfo *t = classes.getptr(E->get());
- ERR_FAIL_COND_V_MSG(!t, 0, "Cannot get class '" + String(E->get()) + "'.");
+ for (const StringName &E : names) {
+ ClassInfo *t = classes.getptr(E);
+ ERR_FAIL_COND_V_MSG(!t, 0, "Cannot get class '" + String(E) + "'.");
if (t->api != p_api || !t->exposed) {
continue;
}
@@ -388,8 +388,8 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
snames.sort_custom<StringName::AlphCompare>();
- for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
- MethodBind *mb = t->method_map[F->get()];
+ for (const StringName &F : snames) {
+ MethodBind *mb = t->method_map[F];
hash = hash_djb2_one_64(mb->get_name().hash(), hash);
hash = hash_djb2_one_64(mb->get_argument_count(), hash);
hash = hash_djb2_one_64(mb->get_argument_type(-1), hash); //return
@@ -426,9 +426,9 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
snames.sort_custom<StringName::AlphCompare>();
- for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
- hash = hash_djb2_one_64(F->get().hash(), hash);
- hash = hash_djb2_one_64(t->constant_map[F->get()], hash);
+ for (const StringName &F : snames) {
+ hash = hash_djb2_one_64(F.hash(), hash);
+ hash = hash_djb2_one_64(t->constant_map[F], hash);
}
}
@@ -444,9 +444,9 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
snames.sort_custom<StringName::AlphCompare>();
- for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
- MethodInfo &mi = t->signal_map[F->get()];
- hash = hash_djb2_one_64(F->get().hash(), hash);
+ for (const StringName &F : snames) {
+ MethodInfo &mi = t->signal_map[F];
+ hash = hash_djb2_one_64(F.hash(), hash);
for (int i = 0; i < mi.arguments.size(); i++) {
hash = hash_djb2_one_64(mi.arguments[i].type, hash);
}
@@ -465,23 +465,23 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
snames.sort_custom<StringName::AlphCompare>();
- for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
- PropertySetGet *psg = t->property_setget.getptr(F->get());
+ for (const StringName &F : snames) {
+ PropertySetGet *psg = t->property_setget.getptr(F);
ERR_FAIL_COND_V(!psg, 0);
- hash = hash_djb2_one_64(F->get().hash(), hash);
+ hash = hash_djb2_one_64(F.hash(), hash);
hash = hash_djb2_one_64(psg->setter.hash(), hash);
hash = hash_djb2_one_64(psg->getter.hash(), hash);
}
}
//property list
- for (List<PropertyInfo>::Element *F = t->property_list.front(); F; F = F->next()) {
- hash = hash_djb2_one_64(F->get().name.hash(), hash);
- hash = hash_djb2_one_64(F->get().type, hash);
- hash = hash_djb2_one_64(F->get().hint, hash);
- hash = hash_djb2_one_64(F->get().hint_string.hash(), hash);
- hash = hash_djb2_one_64(F->get().usage, hash);
+ for (const PropertyInfo &F : t->property_list) {
+ hash = hash_djb2_one_64(F.name.hash(), hash);
+ hash = hash_djb2_one_64(F.type, hash);
+ hash = hash_djb2_one_64(F.hint, hash);
+ hash = hash_djb2_one_64(F.hint_string.hash(), hash);
+ hash = hash_djb2_one_64(F.usage, hash);
}
}
@@ -501,12 +501,28 @@ void ClassDB::add_compatibility_class(const StringName &p_class, const StringNam
compat_classes[p_class] = p_fallback;
}
-Object *ClassDB::instance(const StringName &p_class) {
+thread_local bool initializing_with_extension = false;
+thread_local ObjectNativeExtension *initializing_extension = nullptr;
+thread_local GDExtensionClassInstancePtr initializing_extension_instance = nullptr;
+
+void ClassDB::instance_get_native_extension_data(ObjectNativeExtension **r_extension, GDExtensionClassInstancePtr *r_extension_instance, Object *p_base) {
+ if (initializing_with_extension) {
+ *r_extension = initializing_extension;
+ *r_extension_instance = initializing_extension_instance;
+ initializing_with_extension = false;
+ initializing_extension->set_object_instance(*r_extension_instance, p_base);
+ } else {
+ *r_extension = nullptr;
+ *r_extension_instance = nullptr;
+ }
+}
+
+Object *ClassDB::instantiate(const StringName &p_class) {
ClassInfo *ti;
{
OBJTYPE_RLOCK;
ti = classes.getptr(p_class);
- if (!ti || ti->disabled || !ti->creation_func) {
+ if (!ti || ti->disabled || !ti->creation_func || (ti->native_extension && !ti->native_extension->create_instance)) {
if (compat_classes.has(p_class)) {
ti = classes.getptr(compat_classes[p_class]);
}
@@ -521,10 +537,15 @@ Object *ClassDB::instance(const StringName &p_class) {
return nullptr;
}
#endif
+ if (ti->native_extension) {
+ initializing_with_extension = true;
+ initializing_extension = ti->native_extension;
+ initializing_extension_instance = ti->native_extension->create_instance(ti->native_extension->class_userdata);
+ }
return ti->creation_func();
}
-bool ClassDB::can_instance(const StringName &p_class) {
+bool ClassDB::can_instantiate(const StringName &p_class) {
OBJTYPE_RLOCK;
ClassInfo *ti = classes.getptr(p_class);
@@ -534,7 +555,7 @@ bool ClassDB::can_instance(const StringName &p_class) {
return false;
}
#endif
- return (!ti->disabled && ti->creation_func != nullptr);
+ return (!ti->disabled && ti->creation_func != nullptr && !(ti->native_extension && !ti->native_extension->create_instance));
}
void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) {
@@ -582,7 +603,7 @@ static MethodInfo info_from_bind(MethodBind *p_method) {
}
#endif
-void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) {
+void ClassDB::get_method_list(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) {
OBJTYPE_RLOCK;
ClassInfo *type = classes.getptr(p_class);
@@ -599,16 +620,16 @@ void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, b
#ifdef DEBUG_METHODS_ENABLED
- for (List<MethodInfo>::Element *E = type->virtual_methods.front(); E; E = E->next()) {
- p_methods->push_back(E->get());
+ for (const MethodInfo &E : type->virtual_methods) {
+ p_methods->push_back(E);
}
- for (List<StringName>::Element *E = type->method_order.front(); E; E = E->next()) {
- if (p_exclude_from_properties && type->methods_in_properties.has(E->get())) {
+ for (const StringName &E : type->method_order) {
+ if (p_exclude_from_properties && type->methods_in_properties.has(E)) {
continue;
}
- MethodBind *method = type->method_map.get(E->get());
+ MethodBind *method = type->method_map.get(E);
MethodInfo minfo = info_from_bind(method);
p_methods->push_back(minfo);
@@ -635,7 +656,7 @@ void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, b
}
}
-bool ClassDB::get_method_info(StringName p_class, StringName p_method, MethodInfo *r_info, bool p_no_inheritance, bool p_exclude_from_properties) {
+bool ClassDB::get_method_info(const StringName &p_class, const StringName &p_method, MethodInfo *r_info, bool p_no_inheritance, bool p_exclude_from_properties) {
OBJTYPE_RLOCK;
ClassInfo *type = classes.getptr(p_class);
@@ -686,7 +707,7 @@ bool ClassDB::get_method_info(StringName p_class, StringName p_method, MethodInf
return false;
}
-MethodBind *ClassDB::get_method(StringName p_class, StringName p_name) {
+MethodBind *ClassDB::get_method(const StringName &p_class, const StringName &p_name) {
OBJTYPE_RLOCK;
ClassInfo *type = classes.getptr(p_class);
@@ -743,8 +764,8 @@ void ClassDB::get_integer_constant_list(const StringName &p_class, List<String>
while (type) {
#ifdef DEBUG_METHODS_ENABLED
- for (List<StringName>::Element *E = type->constant_order.front(); E; E = E->next()) {
- p_constants->push_back(E->get());
+ for (const StringName &E : type->constant_order) {
+ p_constants->push_back(E);
}
#else
const StringName *K = nullptr;
@@ -871,6 +892,32 @@ void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_
}
}
+void ClassDB::set_method_error_return_values(const StringName &p_class, const StringName &p_method, const Vector<Error> &p_values) {
+ OBJTYPE_RLOCK;
+#ifdef DEBUG_METHODS_ENABLED
+ ClassInfo *type = classes.getptr(p_class);
+
+ ERR_FAIL_COND(!type);
+
+ type->method_error_values[p_method] = p_values;
+#endif
+}
+
+Vector<Error> ClassDB::get_method_error_return_values(const StringName &p_class, const StringName &p_method) {
+#ifdef DEBUG_METHODS_ENABLED
+ ClassInfo *type = classes.getptr(p_class);
+
+ ERR_FAIL_COND_V(!type, Vector<Error>());
+
+ if (!type->method_error_values.has(p_method)) {
+ return Vector<Error>();
+ }
+ return type->method_error_values[p_method];
+#else
+ return Vector<Error>();
+#endif
+}
+
bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) {
OBJTYPE_RLOCK;
@@ -890,7 +937,7 @@ bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool
return false;
}
-void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) {
+void ClassDB::add_signal(const StringName &p_class, const MethodInfo &p_signal) {
OBJTYPE_WLOCK;
ClassInfo *type = classes.getptr(p_class);
@@ -909,7 +956,7 @@ void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) {
type->signal_map[sname] = p_signal;
}
-void ClassDB::get_signal_list(StringName p_class, List<MethodInfo> *p_signals, bool p_no_inheritance) {
+void ClassDB::get_signal_list(const StringName &p_class, List<MethodInfo> *p_signals, bool p_no_inheritance) {
OBJTYPE_RLOCK;
ClassInfo *type = classes.getptr(p_class);
@@ -931,7 +978,7 @@ void ClassDB::get_signal_list(StringName p_class, List<MethodInfo> *p_signals, b
}
}
-bool ClassDB::has_signal(StringName p_class, StringName p_signal, bool p_no_inheritance) {
+bool ClassDB::has_signal(const StringName &p_class, const StringName &p_signal, bool p_no_inheritance) {
OBJTYPE_RLOCK;
ClassInfo *type = classes.getptr(p_class);
ClassInfo *check = type;
@@ -948,7 +995,7 @@ bool ClassDB::has_signal(StringName p_class, StringName p_signal, bool p_no_inhe
return false;
}
-bool ClassDB::get_signal(StringName p_class, StringName p_signal, MethodInfo *r_signal) {
+bool ClassDB::get_signal(const StringName &p_class, const StringName &p_signal, MethodInfo *r_signal) {
OBJTYPE_RLOCK;
ClassInfo *type = classes.getptr(p_class);
ClassInfo *check = type;
@@ -965,7 +1012,7 @@ bool ClassDB::get_signal(StringName p_class, StringName p_signal, MethodInfo *r_
return false;
}
-void ClassDB::add_property_group(StringName p_class, const String &p_name, const String &p_prefix) {
+void ClassDB::add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix) {
OBJTYPE_WLOCK;
ClassInfo *type = classes.getptr(p_class);
ERR_FAIL_COND(!type);
@@ -973,7 +1020,7 @@ void ClassDB::add_property_group(StringName p_class, const String &p_name, const
type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_GROUP));
}
-void ClassDB::add_property_subgroup(StringName p_class, const String &p_name, const String &p_prefix) {
+void ClassDB::add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix) {
OBJTYPE_WLOCK;
ClassInfo *type = classes.getptr(p_class);
ERR_FAIL_COND(!type);
@@ -981,8 +1028,20 @@ void ClassDB::add_property_subgroup(StringName p_class, const String &p_name, co
type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_SUBGROUP));
}
+void ClassDB::add_property_array_count(const StringName &p_class, const String &p_label, const StringName &p_count_property, const StringName &p_count_setter, const StringName &p_count_getter, const String &p_array_element_prefix, uint32_t p_count_usage) {
+ add_property(p_class, PropertyInfo(Variant::INT, p_count_property, PROPERTY_HINT_NONE, "", p_count_usage | PROPERTY_USAGE_ARRAY, vformat("%s,%s", p_label, p_array_element_prefix)), p_count_setter, p_count_getter);
+}
+
+void ClassDB::add_property_array(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix) {
+ OBJTYPE_WLOCK;
+ ClassInfo *type = classes.getptr(p_class);
+ ERR_FAIL_COND(!type);
+
+ type->property_list.push_back(PropertyInfo(Variant::NIL, p_path, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, p_array_element_prefix));
+}
+
// NOTE: For implementation simplicity reasons, this method doesn't allow setters to have optional arguments at the end.
-void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index) {
+void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index) {
lock.read_lock();
ClassInfo *type = classes.getptr(p_class);
lock.read_unlock();
@@ -1040,26 +1099,41 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons
type->property_setget[p_pinfo.name] = psg;
}
-void ClassDB::set_property_default_value(StringName p_class, const StringName &p_name, const Variant &p_default) {
+void ClassDB::set_property_default_value(const StringName &p_class, const StringName &p_name, const Variant &p_default) {
if (!default_values.has(p_class)) {
default_values[p_class] = HashMap<StringName, Variant>();
}
default_values[p_class][p_name] = p_default;
}
-void ClassDB::get_property_list(StringName p_class, List<PropertyInfo> *p_list, bool p_no_inheritance, const Object *p_validator) {
+void ClassDB::add_linked_property(const StringName &p_class, const String &p_property, const String &p_linked_property) {
+#ifdef TOOLS_ENABLED
+ OBJTYPE_WLOCK;
+ ClassInfo *type = classes.getptr(p_class);
+ ERR_FAIL_COND(!type);
+
+ ERR_FAIL_COND(!type->property_map.has(p_property));
+ ERR_FAIL_COND(!type->property_map.has(p_linked_property));
+
+ PropertyInfo &pinfo = type->property_map[p_property];
+ pinfo.linked_properties.push_back(p_linked_property);
+#endif
+}
+
+void ClassDB::get_property_list(const StringName &p_class, List<PropertyInfo> *p_list, bool p_no_inheritance, const Object *p_validator) {
OBJTYPE_RLOCK;
ClassInfo *type = classes.getptr(p_class);
ClassInfo *check = type;
while (check) {
- for (List<PropertyInfo>::Element *E = check->property_list.front(); E; E = E->next()) {
+ for (const PropertyInfo &pi : check->property_list) {
if (p_validator) {
- PropertyInfo pi = E->get();
- p_validator->_validate_property(pi);
- p_list->push_back(pi);
+ // Making a copy as we may modify it.
+ PropertyInfo pi_mut = pi;
+ p_validator->_validate_property(pi_mut);
+ p_list->push_back(pi_mut);
} else {
- p_list->push_back(E->get());
+ p_list->push_back(pi);
}
}
@@ -1070,7 +1144,7 @@ void ClassDB::get_property_list(StringName p_class, List<PropertyInfo> *p_list,
}
}
-bool ClassDB::get_property_info(StringName p_class, StringName p_property, PropertyInfo *r_info, bool p_no_inheritance, const Object *p_validator) {
+bool ClassDB::get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance, const Object *p_validator) {
OBJTYPE_RLOCK;
ClassInfo *check = classes.getptr(p_class);
@@ -1238,7 +1312,7 @@ Variant::Type ClassDB::get_property_type(const StringName &p_class, const String
return Variant::NIL;
}
-StringName ClassDB::get_property_setter(StringName p_class, const StringName &p_property) {
+StringName ClassDB::get_property_setter(const StringName &p_class, const StringName &p_property) {
ClassInfo *type = classes.getptr(p_class);
ClassInfo *check = type;
while (check) {
@@ -1253,7 +1327,7 @@ StringName ClassDB::get_property_setter(StringName p_class, const StringName &p_
return StringName();
}
-StringName ClassDB::get_property_getter(StringName p_class, const StringName &p_property) {
+StringName ClassDB::get_property_getter(const StringName &p_class, const StringName &p_property) {
ClassInfo *type = classes.getptr(p_class);
ClassInfo *check = type;
while (check) {
@@ -1285,7 +1359,7 @@ bool ClassDB::has_property(const StringName &p_class, const StringName &p_proper
return false;
}
-void ClassDB::set_method_flags(StringName p_class, StringName p_method, int p_flags) {
+void ClassDB::set_method_flags(const StringName &p_class, const StringName &p_method, int p_flags) {
OBJTYPE_WLOCK;
ClassInfo *type = classes.getptr(p_class);
ClassInfo *check = type;
@@ -1294,7 +1368,7 @@ void ClassDB::set_method_flags(StringName p_class, StringName p_method, int p_fl
check->method_map[p_method]->set_hint_flags(p_flags);
}
-bool ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inheritance) {
+bool ClassDB::has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance) {
ClassInfo *type = classes.getptr(p_class);
ClassInfo *check = type;
while (check) {
@@ -1310,6 +1384,24 @@ bool ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inhe
return false;
}
+void ClassDB::bind_method_custom(const StringName &p_class, MethodBind *p_method) {
+ ClassInfo *type = classes.getptr(p_class);
+ if (!type) {
+ ERR_FAIL_MSG("Couldn't bind custom method '" + p_method->get_name() + "' for instance '" + p_class + "'.");
+ }
+
+ if (type->method_map.has(p_method->get_name())) {
+ // overloading not supported
+ ERR_FAIL_MSG("Method already bound '" + p_class + "::" + p_method->get_name() + "'.");
+ }
+
+#ifdef DEBUG_METHODS_ENABLED
+ type->method_order.push_back(p_method->get_name());
+#endif
+
+ type->method_map[p_method->get_name()] = p_method;
+}
+
#ifdef DEBUG_METHODS_ENABLED
MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount) {
StringName mdname = method_name.name;
@@ -1367,7 +1459,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c
return p_bind;
}
-void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual) {
+void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual, const Vector<String> &p_arg_names, bool p_object_core) {
ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'.");
OBJTYPE_WLOCK;
@@ -1377,6 +1469,19 @@ void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_
if (p_virtual) {
mi.flags |= METHOD_FLAG_VIRTUAL;
}
+ if (p_object_core) {
+ mi.flags |= METHOD_FLAG_OBJECT_CORE;
+ }
+ if (p_arg_names.size()) {
+ if (p_arg_names.size() != mi.arguments.size()) {
+ WARN_PRINT("Mismatch argument name count for virtual function: " + String(p_class) + "::" + p_method.name);
+ } else {
+ for (int i = 0; i < p_arg_names.size(); i++) {
+ mi.arguments[i].name = p_arg_names[i];
+ }
+ }
+ }
+
classes[p_class].virtual_methods.push_back(mi);
classes[p_class].virtual_methods_map[p_method.name] = mi;
@@ -1391,8 +1496,8 @@ void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p
ClassInfo *type = classes.getptr(p_class);
ClassInfo *check = type;
while (check) {
- for (List<MethodInfo>::Element *E = check->virtual_methods.front(); E; E = E->next()) {
- p_methods->push_back(E->get());
+ for (const MethodInfo &E : check->virtual_methods) {
+ p_methods->push_back(E);
}
if (p_no_inheritance) {
@@ -1404,14 +1509,14 @@ void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p
#endif
}
-void ClassDB::set_class_enabled(StringName p_class, bool p_enable) {
+void ClassDB::set_class_enabled(const StringName &p_class, bool p_enable) {
OBJTYPE_WLOCK;
ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'.");
classes[p_class].disabled = !p_enable;
}
-bool ClassDB::is_class_enabled(StringName p_class) {
+bool ClassDB::is_class_enabled(const StringName &p_class) {
OBJTYPE_RLOCK;
ClassInfo *ti = classes.getptr(p_class);
@@ -1425,7 +1530,7 @@ bool ClassDB::is_class_enabled(StringName p_class) {
return !ti->disabled;
}
-bool ClassDB::is_class_exposed(StringName p_class) {
+bool ClassDB::is_class_exposed(const StringName &p_class) {
OBJTYPE_RLOCK;
ClassInfo *ti = classes.getptr(p_class);
@@ -1458,6 +1563,10 @@ void ClassDB::get_resource_base_extensions(List<String> *p_extensions) {
}
}
+bool ClassDB::is_resource_extension(const StringName &p_extension) {
+ return resource_base_extensions.has(p_extension);
+}
+
void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p_extensions) {
const StringName *K = nullptr;
@@ -1484,19 +1593,19 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con
if (Engine::get_singleton()->has_singleton(p_class)) {
c = Engine::get_singleton()->get_singleton_object(p_class);
cleanup_c = false;
- } else if (ClassDB::can_instance(p_class)) {
- c = ClassDB::instance(p_class);
+ } else if (ClassDB::can_instantiate(p_class)) {
+ c = ClassDB::instantiate(p_class);
cleanup_c = true;
}
if (c) {
List<PropertyInfo> plist;
c->get_property_list(&plist);
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- if (E->get().usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR)) {
- if (!default_values[p_class].has(E->get().name)) {
- Variant v = c->get(E->get().name);
- default_values[p_class][E->get().name] = v;
+ for (const PropertyInfo &E : plist) {
+ if (E.usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR)) {
+ if (!default_values[p_class].has(E.name)) {
+ Variant v = c->get(E.name);
+ default_values[p_class][E.name] = v;
}
}
}
@@ -1545,6 +1654,32 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con
return var;
}
+void ClassDB::register_extension_class(ObjectNativeExtension *p_extension) {
+ GLOBAL_LOCK_FUNCTION;
+
+ ERR_FAIL_COND_MSG(classes.has(p_extension->class_name), "Class already registered: " + String(p_extension->class_name));
+ ERR_FAIL_COND_MSG(!classes.has(p_extension->parent_class_name), "Parent class name for extension class not found: " + String(p_extension->parent_class_name));
+
+ ClassInfo *parent = classes.getptr(p_extension->parent_class_name);
+
+ ClassInfo c;
+ c.api = p_extension->editor_class ? API_EDITOR_EXTENSION : API_EXTENSION;
+ c.native_extension = p_extension;
+ c.name = p_extension->class_name;
+ c.creation_func = parent->creation_func;
+ c.inherits = parent->name;
+ c.class_ptr = parent->class_ptr;
+ c.inherits_ptr = parent;
+ c.exposed = true;
+
+ classes[p_extension->class_name] = c;
+}
+
+void ClassDB::unregister_extension_class(const StringName &p_class) {
+ ERR_FAIL_COND(!classes.has(p_class));
+ classes.erase(p_class);
+}
+
RWLock ClassDB::lock;
void ClassDB::cleanup_defaults() {
diff --git a/core/object/class_db.h b/core/object/class_db.h
index 6fd5748dbb..e89c7fffd7 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -97,6 +97,8 @@ public:
enum APIType {
API_CORE,
API_EDITOR,
+ API_EXTENSION,
+ API_EDITOR_EXTENSION,
API_NONE
};
@@ -115,6 +117,8 @@ public:
ClassInfo *inherits_ptr = nullptr;
void *class_ptr = nullptr;
+ ObjectNativeExtension *native_extension = nullptr;
+
HashMap<StringName, MethodBind *> method_map;
HashMap<StringName, int> constant_map;
HashMap<StringName, List<StringName>> enum_map;
@@ -128,6 +132,7 @@ public:
List<MethodInfo> virtual_methods;
Map<StringName, MethodInfo> virtual_methods_map;
StringName category;
+ Map<StringName, Vector<Error>> method_error_values;
#endif
HashMap<StringName, PropertySetGet> property_setget;
@@ -199,6 +204,9 @@ public:
//nothing
}
+ static void register_extension_class(ObjectNativeExtension *p_extension);
+ static void unregister_extension_class(const StringName &p_class);
+
template <class T>
static Object *_create_ptr_func() {
return T::create();
@@ -224,8 +232,10 @@ public:
static StringName get_compatibility_remapped_class(const StringName &p_class);
static bool class_exists(const StringName &p_class);
static bool is_parent_class(const StringName &p_class, const StringName &p_inherits);
- static bool can_instance(const StringName &p_class);
- static Object *instance(const StringName &p_class);
+ static bool can_instantiate(const StringName &p_class);
+ static Object *instantiate(const StringName &p_class);
+ static void instance_get_native_extension_data(ObjectNativeExtension **r_extension, GDExtensionClassInstancePtr *r_extension_instance, Object *p_base);
+
static APIType get_api_type(const StringName &p_class);
static uint64_t get_api_hash(APIType p_api);
@@ -302,7 +312,7 @@ public:
}
template <class M>
- static MethodBind *bind_vararg_method(uint32_t p_flags, StringName p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const Vector<Variant> &p_default_args = Vector<Variant>(), bool p_return_nil_is_variant = true) {
+ static MethodBind *bind_vararg_method(uint32_t p_flags, const StringName &p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const Vector<Variant> &p_default_args = Vector<Variant>(), bool p_return_nil_is_variant = true) {
GLOBAL_LOCK_FUNCTION;
MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant);
@@ -321,7 +331,7 @@ public:
if (type->method_map.has(p_name)) {
memdelete(bind);
- // overloading not supported
+ // Overloading not supported
ERR_FAIL_V_MSG(nullptr, "Method already bound: " + instance_type + "::" + p_name + ".");
}
type->method_map[p_name] = bind;
@@ -334,33 +344,38 @@ public:
return bind;
}
- static void add_signal(StringName p_class, const MethodInfo &p_signal);
- static bool has_signal(StringName p_class, StringName p_signal, bool p_no_inheritance = false);
- static bool get_signal(StringName p_class, StringName p_signal, MethodInfo *r_signal);
- static void get_signal_list(StringName p_class, List<MethodInfo> *p_signals, bool p_no_inheritance = false);
-
- static void add_property_group(StringName p_class, const String &p_name, const String &p_prefix = "");
- static void add_property_subgroup(StringName p_class, const String &p_name, const String &p_prefix = "");
- static void add_property(StringName p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index = -1);
- static void set_property_default_value(StringName p_class, const StringName &p_name, const Variant &p_default);
- static void get_property_list(StringName p_class, List<PropertyInfo> *p_list, bool p_no_inheritance = false, const Object *p_validator = nullptr);
- static bool get_property_info(StringName p_class, StringName p_property, PropertyInfo *r_info, bool p_no_inheritance = false, const Object *p_validator = nullptr);
+ static void bind_method_custom(const StringName &p_class, MethodBind *p_method);
+
+ static void add_signal(const StringName &p_class, const MethodInfo &p_signal);
+ static bool has_signal(const StringName &p_class, const StringName &p_signal, bool p_no_inheritance = false);
+ static bool get_signal(const StringName &p_class, const StringName &p_signal, MethodInfo *r_signal);
+ static void get_signal_list(const StringName &p_class, List<MethodInfo> *p_signals, bool p_no_inheritance = false);
+
+ static void add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix = "");
+ static void add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix = "");
+ static void add_property_array_count(const StringName &p_class, const String &p_label, const StringName &p_count_property, const StringName &p_count_setter, const StringName &p_count_getter, const String &p_array_element_prefix, uint32_t p_count_usage = PROPERTY_USAGE_EDITOR);
+ static void add_property_array(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix);
+ static void add_property(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index = -1);
+ static void set_property_default_value(const StringName &p_class, const StringName &p_name, const Variant &p_default);
+ static void add_linked_property(const StringName &p_class, const String &p_property, const String &p_linked_property);
+ static void get_property_list(const StringName &p_class, List<PropertyInfo> *p_list, bool p_no_inheritance = false, const Object *p_validator = nullptr);
+ static bool get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance = false, const Object *p_validator = nullptr);
static bool set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid = nullptr);
static bool get_property(Object *p_object, const StringName &p_property, Variant &r_value);
static bool has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance = false);
static int get_property_index(const StringName &p_class, const StringName &p_property, bool *r_is_valid = nullptr);
static Variant::Type get_property_type(const StringName &p_class, const StringName &p_property, bool *r_is_valid = nullptr);
- static StringName get_property_setter(StringName p_class, const StringName &p_property);
- static StringName get_property_getter(StringName p_class, const StringName &p_property);
+ static StringName get_property_setter(const StringName &p_class, const StringName &p_property);
+ static StringName get_property_getter(const StringName &p_class, const StringName &p_property);
- static bool has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false);
- static void set_method_flags(StringName p_class, StringName p_method, int p_flags);
+ static bool has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false);
+ static void set_method_flags(const StringName &p_class, const StringName &p_method, int p_flags);
- static void get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
- static bool get_method_info(StringName p_class, StringName p_method, MethodInfo *r_info, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
- static MethodBind *get_method(StringName p_class, StringName p_name);
+ static void get_method_list(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
+ static bool get_method_info(const StringName &p_class, const StringName &p_method, MethodInfo *r_info, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
+ static MethodBind *get_method(const StringName &p_class, const StringName &p_name);
- static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true);
+ static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true, const Vector<String> &p_arg_names = Vector<String>(), bool p_object_core = false);
static void get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false);
static void bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int p_constant);
@@ -373,18 +388,21 @@ public:
static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance = false);
static bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
+ static void set_method_error_return_values(const StringName &p_class, const StringName &p_method, const Vector<Error> &p_values);
+ static Vector<Error> get_method_error_return_values(const StringName &p_class, const StringName &p_method);
static Variant class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid = nullptr);
static StringName get_category(const StringName &p_node);
- static void set_class_enabled(StringName p_class, bool p_enable);
- static bool is_class_enabled(StringName p_class);
+ static void set_class_enabled(const StringName &p_class, bool p_enable);
+ static bool is_class_enabled(const StringName &p_class);
- static bool is_class_exposed(StringName p_class);
+ static bool is_class_exposed(const StringName &p_class);
static void add_resource_base_extension(const StringName &p_extension, const StringName &p_class);
static void get_resource_base_extensions(List<String> *p_extensions);
static void get_extensions_for_type(const StringName &p_class, List<String> *p_extensions);
+ static bool is_resource_extension(const StringName &p_extension);
static void add_compatibility_class(const StringName &p_class, const StringName &p_fallback);
@@ -397,30 +415,55 @@ public:
#ifdef DEBUG_METHODS_ENABLED
#define BIND_CONSTANT(m_constant) \
- ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant);
+ ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant);
#define BIND_ENUM_CONSTANT(m_constant) \
- ClassDB::bind_integer_constant(get_class_static(), __constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant);
-
-#else
+ ::ClassDB::bind_integer_constant(get_class_static(), __constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant);
-#define BIND_CONSTANT(m_constant) \
- ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant);
+_FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr) {
+}
-#define BIND_ENUM_CONSTANT(m_constant) \
- ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant);
+_FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr, const Error &p_err) {
+ arr.push_back(p_err);
+}
-#endif
+template <class... P>
+_FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr, const Error &p_err, P... p_args) {
+ arr.push_back(p_err);
+ errarray_add_str(arr, p_args...);
+}
-#ifdef TOOLS_ENABLED
+template <class... P>
+_FORCE_INLINE_ Vector<Error> errarray(P... p_args) {
+ Vector<Error> arr;
+ errarray_add_str(arr, p_args...);
+ return arr;
+}
-#define BIND_VMETHOD(m_method) \
- ClassDB::add_virtual_method(get_class_static(), m_method);
+#define BIND_METHOD_ERR_RETURN_DOC(m_method, ...) \
+ ::ClassDB::set_method_error_return_values(get_class_static(), m_method, errarray(__VA_ARGS__));
#else
-#define BIND_VMETHOD(m_method)
+#define BIND_CONSTANT(m_constant) \
+ ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant);
+
+#define BIND_ENUM_CONSTANT(m_constant) \
+ ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant);
+
+#define BIND_METHOD_ERR_RETURN_DOC(m_method, ...)
#endif
+#define GDREGISTER_CLASS(m_class) \
+ if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
+ ::ClassDB::register_class<m_class>(); \
+ }
+#define GDREGISTER_VIRTUAL_CLASS(m_class) \
+ if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
+ ::ClassDB::register_virtual_class<m_class>(); \
+ }
+
+#include "core/disabled_classes.gen.h"
+
#endif // CLASS_DB_H
diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py
new file mode 100644
index 0000000000..86c2891e5d
--- /dev/null
+++ b/core/object/make_virtuals.py
@@ -0,0 +1,161 @@
+proto = """
+#define GDVIRTUAL$VER($RET m_name $ARG) \\
+StringName _gdvirtual_##m_name##_sn = #m_name;\\
+GDNativeExtensionClassCallVirtual _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, #m_name) : (GDNativeExtensionClassCallVirtual) nullptr;\\
+_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
+ ScriptInstance *script_instance = ((Object*)(this))->get_script_instance();\\
+ if (script_instance) {\\
+ Callable::CallError ce; \\
+ $CALLSIARGS\\
+ $CALLSIBEGINscript_instance->call(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\
+ if (ce.error == Callable::CallError::CALL_OK) {\\
+ $CALLSIRET\\
+ return true;\\
+ } \\
+ }\\
+ if (_gdvirtual_##m_name) {\\
+ $CALLPTRARGS\\
+ $CALLPTRRETDEF\\
+ _gdvirtual_##m_name(_get_extension_instance(),$CALLPTRARGPASS,$CALLPTRRETPASS);\\
+ $CALLPTRRET\\
+ return true;\\
+ }\\
+\\
+ return false;\\
+}\\
+_FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const { \\
+ ScriptInstance *script_instance = ((Object*)(this))->get_script_instance();\\
+ if (script_instance) {\\
+ return script_instance->has_method(_gdvirtual_##m_name##_sn);\\
+ }\\
+ if (_gdvirtual_##m_name) {\\
+ return true;\\
+ }\\
+ return false;\\
+}\\
+\\
+_FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() { \\
+ MethodInfo method_info;\\
+ method_info.name = #m_name;\\
+ method_info.flags = METHOD_FLAG_VIRTUAL;\\
+ $FILL_METHOD_INFO\\
+ return method_info;\\
+}
+
+"""
+
+
+def generate_version(argcount, const=False, returns=False):
+ s = proto
+ sproto = str(argcount)
+ method_info = ""
+ if returns:
+ sproto += "R"
+ s = s.replace("$RET", "m_ret, ")
+ s = s.replace("$CALLPTRRETDEF", "PtrToArg<m_ret>::EncodeT ret;")
+ method_info += "\tmethod_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\\n"
+ else:
+ s = s.replace("$RET", "")
+ s = s.replace("$CALLPTRRETDEF", "")
+
+ if const:
+ sproto += "C"
+ s = s.replace("$CONST", "const")
+ method_info += "\tmethod_info.flags|=METHOD_FLAG_CONST;\\\n"
+ else:
+ s = s.replace("$CONST", "")
+
+ s = s.replace("$VER", sproto)
+ argtext = ""
+ callargtext = ""
+ callsiargs = ""
+ callsiargptrs = ""
+ callptrargsptr = ""
+ if argcount > 0:
+ argtext += ", "
+ callsiargs = "Variant vargs[" + str(argcount) + "]={"
+ callsiargptrs = "\t\tconst Variant *vargptrs[" + str(argcount) + "]={"
+ callptrargsptr = "\t\tconst GDNativeTypePtr argptrs[" + str(argcount) + "]={"
+ callptrargs = ""
+ for i in range(argcount):
+ if i > 0:
+ argtext += ", "
+ callargtext += ", "
+ callsiargs += ", "
+ callsiargptrs += ", "
+ callptrargs += "\t\t"
+ callptrargsptr += ", "
+ argtext += "m_type" + str(i + 1)
+ callargtext += "m_type" + str(i + 1) + " arg" + str(i + 1)
+ callsiargs += "Variant(arg" + str(i + 1) + ")"
+ callsiargptrs += "&vargs[" + str(i) + "]"
+ callptrargs += (
+ "PtrToArg<m_type" + str(i + 1) + ">::EncodeT argval" + str(i + 1) + " = arg" + str(i + 1) + ";\\\n"
+ )
+ callptrargsptr += "&argval" + str(i + 1)
+ method_info += "\tmethod_info.arguments.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::get_class_info());\\\n"
+
+ if argcount:
+ callsiargs += "};\\\n"
+ callsiargptrs += "};\\\n"
+ s = s.replace("$CALLSIARGS", callsiargs + callsiargptrs)
+ s = s.replace("$CALLSIARGPASS", "(const Variant **)vargptrs," + str(argcount))
+ callptrargsptr += "};\\\n"
+ s = s.replace("$CALLPTRARGS", callptrargs + callptrargsptr)
+ s = s.replace("$CALLPTRARGPASS", "(const GDNativeTypePtr*)argptrs")
+ else:
+ s = s.replace("$CALLSIARGS", "")
+ s = s.replace("$CALLSIARGPASS", "nullptr, 0")
+ s = s.replace("$CALLPTRARGS", "")
+ s = s.replace("$CALLPTRARGPASS", "nullptr")
+
+ if returns:
+ if argcount > 0:
+ callargtext += ","
+ callargtext += " m_ret& r_ret"
+ s = s.replace("$CALLSIBEGIN", "Variant ret = ")
+ s = s.replace("$CALLSIRET", "r_ret = ret;")
+ s = s.replace("$CALLPTRRETPASS", "&ret")
+ s = s.replace("$CALLPTRRET", "r_ret = ret;")
+ else:
+ s = s.replace("$CALLSIBEGIN", "")
+ s = s.replace("$CALLSIRET", "")
+ s = s.replace("$CALLPTRRETPASS", "nullptr")
+ s = s.replace("$CALLPTRRET", "")
+
+ s = s.replace("$ARG", argtext)
+ s = s.replace("$CALLARGS", callargtext)
+ s = s.replace("$FILL_METHOD_INFO", method_info)
+
+ return s
+
+
+def run(target, source, env):
+
+ max_versions = 12
+
+ txt = """
+#ifndef GDVIRTUAL_GEN_H
+#define GDVIRTUAL_GEN_H
+
+
+"""
+
+ for i in range(max_versions + 1):
+
+ txt += "/* " + str(i) + " Arguments */\n\n"
+ txt += generate_version(i, False, False)
+ txt += generate_version(i, False, True)
+ txt += generate_version(i, True, False)
+ txt += generate_version(i, True, True)
+
+ txt += "#endif"
+
+ with open(target[0], "w") as f:
+ f.write(txt)
+
+
+if __name__ == "__main__":
+ from platform_methods import subprocess_main
+
+ subprocess_main(globals())
diff --git a/core/object/method_bind.cpp b/core/object/method_bind.cpp
index 9c5ed60708..c53104fe3f 100644
--- a/core/object/method_bind.cpp
+++ b/core/object/method_bind.cpp
@@ -34,6 +34,35 @@
#include "method_bind.h"
+uint32_t MethodBind::get_hash() const {
+ uint32_t hash = hash_djb2_one_32(has_return() ? 1 : 0);
+ hash = hash_djb2_one_32(get_argument_count(), hash);
+
+#ifndef _MSC_VER
+#warning This needs proper class name and argument type for hashing
+#endif
+#if 0
+
+ for (int i = (has_return() ? -1 : 0); i < get_argument_count(); i++) {
+ PropertyInfo pi = i == -1 ? get_return_info() : get_argument_info(i);
+ hash = hash_djb2_one_32(get_argument_type(i), hash);
+ if (pi.class_name != StringName()) {
+ hash = hash_djb2_one_32(pi.class_name.operator String().hash(), hash);
+ }
+ }
+#endif
+ hash = hash_djb2_one_32(get_default_argument_count(), hash);
+ for (int i = 0; i < get_default_argument_count(); i++) {
+ Variant v = get_default_argument(i);
+ hash = hash_djb2_one_32(v.hash(), hash);
+ }
+
+ hash = hash_djb2_one_32(is_const(), hash);
+ hash = hash_djb2_one_32(is_vararg(), hash);
+
+ return hash;
+}
+
#ifdef DEBUG_METHODS_ENABLED
PropertyInfo MethodBind::get_argument_info(int p_argument) const {
ERR_FAIL_INDEX_V(p_argument, get_argument_count(), PropertyInfo());
diff --git a/core/object/method_bind.h b/core/object/method_bind.h
index 7030ae201b..b0b379873e 100644
--- a/core/object/method_bind.h
+++ b/core/object/method_bind.h
@@ -43,6 +43,7 @@ enum MethodFlags {
METHOD_FLAG_FROM_SCRIPT = 64,
METHOD_FLAG_VARARG = 128,
METHOD_FLAG_STATIC = 256,
+ METHOD_FLAG_OBJECT_CORE = 512,
METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL,
};
@@ -135,6 +136,8 @@ public:
void set_default_arguments(const Vector<Variant> &p_defargs);
+ uint32_t get_hash() const;
+
MethodBind();
virtual ~MethodBind();
};
diff --git a/core/object/object.cpp b/core/object/object.cpp
index a8b2c4a939..3335942fb3 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -385,6 +385,23 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid
}
}
+ if (_extension && _extension->set) {
+// C style pointer casts should never trigger a compiler warning because the risk is assumed by the user, so GCC should keep quiet about it.
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wignored-qualifiers"
+#endif
+ if (_extension->set(_extension_instance, (const GDNativeStringNamePtr)&p_name, (const GDNativeVariantPtr)&p_value)) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return;
+ }
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+ }
+
//try built-in setgetter
{
if (ClassDB::set_property(this, p_name, p_value, r_valid)) {
@@ -450,6 +467,23 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const {
return ret;
}
}
+ if (_extension && _extension->get) {
+// C style pointer casts should never trigger a compiler warning because the risk is assumed by the user, so GCC should keep quiet about it.
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wignored-qualifiers"
+#endif
+
+ if (_extension->get(_extension_instance, (const GDNativeStringNamePtr)&p_name, (GDNativeVariantPtr)&ret)) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return ret;
+ }
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+ }
//try built-in setgetter
{
@@ -596,6 +630,17 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
_get_property_listv(p_list, p_reversed);
+ if (_extension && _extension->get_property_list) {
+ uint32_t pcount;
+ const GDNativePropertyInfo *pinfo = _extension->get_property_list(_extension_instance, &pcount);
+ for (uint32_t i = 0; i < pcount; i++) {
+ p_list->push_back(PropertyInfo(Variant::Type(pinfo[i].type), pinfo[i].class_name, PropertyHint(pinfo[i].hint), pinfo[i].hint_string, pinfo[i].usage, pinfo[i].class_name));
+ }
+ if (_extension->free_property_list) {
+ _extension->free_property_list(_extension_instance, pinfo);
+ }
+ }
+
if (!is_class("Script")) { // can still be set, but this is for user-friendliness
p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT));
}
@@ -740,7 +785,7 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
return Variant();
}
- if (Object::cast_to<Reference>(this)) {
+ if (Object::cast_to<RefCounted>(this)) {
r_error.argument = 0;
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
ERR_FAIL_V_MSG(Variant(), "Can't 'free' a reference.");
@@ -761,6 +806,7 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
Variant ret;
OBJ_DEBUG_LOCK
+
if (script_instance) {
ret = script_instance->call(p_method, p_args, p_argcount, r_error);
//force jumptable
@@ -778,6 +824,8 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
}
}
+ //extension does not need this, because all methods are registered in MethodBind
+
MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
if (method) {
@@ -795,6 +843,10 @@ void Object::notification(int p_notification, bool p_reversed) {
if (script_instance) {
script_instance->notification(p_notification);
}
+
+ if (_extension && _extension->notification) {
+ _extension->notification(_extension_instance, p_notification);
+ }
}
String Object::to_string() {
@@ -805,6 +857,9 @@ String Object::to_string() {
return ret;
}
}
+ if (_extension && _extension->to_string) {
+ return _extension->to_string(_extension_instance);
+ }
return "[" + get_class() + ":" + itos(get_instance_id()) + "]";
}
@@ -832,7 +887,7 @@ void Object::set_script(const Variant &p_script) {
Ref<Script> s = script;
if (!s.is_null()) {
- if (s->can_instance()) {
+ if (s->can_instantiate()) {
OBJ_DEBUG_LOCK
script_instance = s->instance_create(this);
} else if (Engine::get_singleton()->is_editor_hint()) {
@@ -867,11 +922,11 @@ Variant Object::get_script() const {
return script;
}
-bool Object::has_meta(const String &p_name) const {
+bool Object::has_meta(const StringName &p_name) const {
return metadata.has(p_name);
}
-void Object::set_meta(const String &p_name, const Variant &p_value) {
+void Object::set_meta(const StringName &p_name, const Variant &p_value) {
if (p_value.get_type() == Variant::NIL) {
metadata.erase(p_name);
return;
@@ -880,12 +935,12 @@ void Object::set_meta(const String &p_name, const Variant &p_value) {
metadata[p_name] = p_value;
}
-Variant Object::get_meta(const String &p_name) const {
- ERR_FAIL_COND_V(!metadata.has(p_name), Variant());
+Variant Object::get_meta(const StringName &p_name) const {
+ ERR_FAIL_COND_V_MSG(!metadata.has(p_name), Variant(), "The object does not have any 'meta' values with the key '" + p_name + "'.");
return metadata[p_name];
}
-void Object::remove_meta(const String &p_name) {
+void Object::remove_meta(const StringName &p_name) {
metadata.erase(p_name);
}
@@ -909,23 +964,23 @@ Array Object::_get_method_list_bind() const {
return ret;
}
-Vector<String> Object::_get_meta_list_bind() const {
- Vector<String> _metaret;
+Vector<StringName> Object::_get_meta_list_bind() const {
+ Vector<StringName> _metaret;
List<Variant> keys;
metadata.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- _metaret.push_back(E->get());
+ for (const Variant &E : keys) {
+ _metaret.push_back(E);
}
return _metaret;
}
-void Object::get_meta_list(List<String> *p_list) const {
+void Object::get_meta_list(List<StringName> *p_list) const {
List<Variant> keys;
metadata.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- p_list->push_back(E->get());
+ for (const Variant &E : keys) {
+ p_list->push_back(E);
}
}
@@ -1129,8 +1184,8 @@ Array Object::_get_signal_list() const {
get_signal_list(&signal_list);
Array ret;
- for (List<MethodInfo>::Element *E = signal_list.front(); E; E = E->next()) {
- ret.push_back(Dictionary(E->get()));
+ for (const MethodInfo &E : signal_list) {
+ ret.push_back(Dictionary(E));
}
return ret;
@@ -1142,8 +1197,7 @@ Array Object::_get_signal_connection_list(const String &p_signal) const {
Array ret;
- for (List<Connection>::Element *E = conns.front(); E; E = E->next()) {
- Connection &c = E->get();
+ for (const Connection &c : conns) {
if (c.signal.get_name() == p_signal) {
ret.push_back(c);
}
@@ -1242,16 +1296,16 @@ int Object::get_persistent_signal_connection_count() const {
}
void Object::get_signals_connected_to_this(List<Connection> *p_connections) const {
- for (const List<Connection>::Element *E = connections.front(); E; E = E->next()) {
- p_connections->push_back(E->get());
+ for (const Connection &E : connections) {
+ p_connections->push_back(E);
}
}
Error Object::connect(const StringName &p_signal, const Callable &p_callable, const Vector<Variant> &p_binds, uint32_t p_flags) {
- ERR_FAIL_COND_V(p_callable.is_null(), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V_MSG(p_callable.is_null(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is null.");
Object *target_object = p_callable.get_object();
- ERR_FAIL_COND_V(!target_object, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V_MSG(!target_object, ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "' to callable '" + p_callable + "': the callable object is null.");
SignalData *s = signal_map.getptr(p_signal);
if (!s) {
@@ -1309,7 +1363,7 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, co
}
bool Object::is_connected(const StringName &p_signal, const Callable &p_callable) const {
- ERR_FAIL_COND_V(p_callable.is_null(), false);
+ ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, "Cannot determine if connected to '" + p_signal + "': the provided callable is null.");
const SignalData *s = signal_map.getptr(p_signal);
if (!s) {
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal);
@@ -1336,10 +1390,10 @@ void Object::disconnect(const StringName &p_signal, const Callable &p_callable)
}
void Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) {
- ERR_FAIL_COND(p_callable.is_null());
+ ERR_FAIL_COND_MSG(p_callable.is_null(), "Cannot disconnect from '" + p_signal + "': the provided callable is null.");
Object *target_object = p_callable.get_object();
- ERR_FAIL_COND(!target_object);
+ ERR_FAIL_COND_MSG(!target_object, "Cannot disconnect '" + p_signal + "' from callable '" + p_callable + "': the callable object is null.");
SignalData *s = signal_map.getptr(p_signal);
if (!s) {
@@ -1445,9 +1499,9 @@ void Object::_clear_internal_resource_paths(const Variant &p_var) {
List<Variant> keys;
d.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- _clear_internal_resource_paths(E->get());
- _clear_internal_resource_paths(d[E->get()]);
+ for (const Variant &E : keys) {
+ _clear_internal_resource_paths(E);
+ _clear_internal_resource_paths(d[E]);
}
} break;
default: {
@@ -1476,8 +1530,8 @@ void Object::clear_internal_resource_paths() {
get_property_list(&pinfo);
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- _clear_internal_resource_paths(get(E->get().name));
+ for (const PropertyInfo &E : pinfo) {
+ _clear_internal_resource_paths(get(E.name));
}
}
@@ -1565,22 +1619,25 @@ void Object::_bind_methods() {
ADD_SIGNAL(MethodInfo("script_changed"));
ADD_SIGNAL(MethodInfo("property_list_changed"));
- BIND_VMETHOD(MethodInfo("_notification", PropertyInfo(Variant::INT, "what")));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_set", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value")));
+#define BIND_OBJ_CORE_METHOD(m_method) \
+ ::ClassDB::add_virtual_method(get_class_static(), m_method, true, Vector<String>(), true);
+
+ BIND_OBJ_CORE_METHOD(MethodInfo("_notification", PropertyInfo(Variant::INT, "what")));
+ BIND_OBJ_CORE_METHOD(MethodInfo(Variant::BOOL, "_set", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value")));
#ifdef TOOLS_ENABLED
MethodInfo miget("_get", PropertyInfo(Variant::STRING_NAME, "property"));
miget.return_val.name = "Variant";
miget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- BIND_VMETHOD(miget);
+ BIND_OBJ_CORE_METHOD(miget);
MethodInfo plget("_get_property_list");
plget.return_val.type = Variant::ARRAY;
- BIND_VMETHOD(plget);
+ BIND_OBJ_CORE_METHOD(plget);
#endif
- BIND_VMETHOD(MethodInfo("_init"));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_to_string"));
+ BIND_OBJ_CORE_METHOD(MethodInfo("_init"));
+ BIND_OBJ_CORE_METHOD(MethodInfo(Variant::STRING, "_to_string"));
BIND_CONSTANT(NOTIFICATION_POSTINITIALIZE);
BIND_CONSTANT(NOTIFICATION_PREDELETE);
@@ -1611,12 +1668,12 @@ void Object::get_translatable_strings(List<String> *p_strings) const {
List<PropertyInfo> plist;
get_property_list(&plist);
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_INTERNATIONALIZED)) {
+ for (const PropertyInfo &E : plist) {
+ if (!(E.usage & PROPERTY_USAGE_INTERNATIONALIZED)) {
continue;
}
- String text = get(E->get().name);
+ String text = get(E.name);
if (text == "") {
continue;
@@ -1714,42 +1771,69 @@ uint32_t Object::get_edited_version() const {
}
#endif
-void *Object::get_script_instance_binding(int p_script_language_index) {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(p_script_language_index, MAX_SCRIPT_INSTANCE_BINDINGS, nullptr);
-#endif
-
- //it's up to the script language to make this thread safe, if the function is called twice due to threads being out of sync
- //just return the same pointer.
- //if you want to put a big lock in the entire function and keep allocated pointers in a map or something, feel free to do it
- //as it should not really affect performance much (won't be called too often), as in far most cases the condition below will be false afterwards
+void Object::set_instance_binding(void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks) {
+ // This is only meant to be used on creation by the binder.
+ ERR_FAIL_COND(_instance_bindings != nullptr);
+ _instance_bindings = (InstanceBinding *)memalloc(sizeof(InstanceBinding));
+ _instance_bindings[0].binding = p_binding;
+ _instance_bindings[0].free_callback = p_callbacks->free_callback;
+ _instance_bindings[0].reference_callback = p_callbacks->reference_callback;
+ _instance_bindings[0].token = p_token;
+ _instance_binding_count = 1;
+}
+
+void *Object::get_instance_binding(void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks) {
+ void *binding = nullptr;
+ _instance_binding_mutex.lock();
+ for (uint32_t i = 0; i < _instance_binding_count; i++) {
+ if (_instance_bindings[i].token == p_token) {
+ binding = _instance_bindings[i].binding;
+ break;
+ }
+ }
+ if (unlikely(!binding)) {
+ uint32_t current_size = next_power_of_2(_instance_binding_count);
+ uint32_t new_size = next_power_of_2(_instance_binding_count + 1);
- if (!_script_instance_bindings[p_script_language_index]) {
- void *script_data = ScriptServer::get_language(p_script_language_index)->alloc_instance_binding_data(this);
- if (script_data) {
- instance_binding_count.increment();
- _script_instance_bindings[p_script_language_index] = script_data;
+ if (current_size == 0 || new_size > current_size) {
+ _instance_bindings = (InstanceBinding *)memrealloc(_instance_bindings, new_size * sizeof(InstanceBinding));
}
+
+ _instance_bindings[_instance_binding_count].free_callback = p_callbacks->free_callback;
+ _instance_bindings[_instance_binding_count].reference_callback = p_callbacks->reference_callback;
+ _instance_bindings[_instance_binding_count].token = p_token;
+
+ binding = p_callbacks->create_callback(p_token, this);
+ _instance_bindings[_instance_binding_count].binding = binding;
+
+ _instance_binding_count++;
}
- return _script_instance_bindings[p_script_language_index];
-}
+ _instance_binding_mutex.unlock();
-bool Object::has_script_instance_binding(int p_script_language_index) {
- return _script_instance_bindings[p_script_language_index] != nullptr;
+ return binding;
}
-void Object::set_script_instance_binding(int p_script_language_index, void *p_data) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(_script_instance_bindings[p_script_language_index] != nullptr);
-#endif
- _script_instance_bindings[p_script_language_index] = p_data;
+bool Object::has_instance_binding(void *p_token) {
+ bool found = false;
+ _instance_binding_mutex.lock();
+ for (uint32_t i = 0; i < _instance_binding_count; i++) {
+ if (_instance_bindings[i].token == p_token) {
+ found = true;
+ break;
+ }
+ }
+
+ _instance_binding_mutex.unlock();
+
+ return found;
}
void Object::_construct_object(bool p_reference) {
type_is_reference = p_reference;
_instance_id = ObjectDB::add_instance(this);
- memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS);
+
+ ClassDB::instance_get_native_extension_data(&_extension, &_extension_instance, this);
#ifdef DEBUG_ENABLED
_lock_index.init(1);
@@ -1770,6 +1854,12 @@ Object::~Object() {
}
script_instance = nullptr;
+ if (_extension && _extension->free_instance) {
+ _extension->free_instance(_extension->class_userdata, _extension_instance);
+ _extension = nullptr;
+ _extension_instance = nullptr;
+ }
+
const StringName *S = nullptr;
if (_emitting) {
@@ -1801,12 +1891,13 @@ Object::~Object() {
_instance_id = ObjectID();
_predelete_ok = 2;
- if (!ScriptServer::are_languages_finished()) {
- for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
- if (_script_instance_bindings[i]) {
- ScriptServer::get_language(i)->free_instance_binding_data(_script_instance_bindings[i]);
+ if (_instance_bindings != nullptr) {
+ for (uint32_t i = 0; i < _instance_binding_count; i++) {
+ if (_instance_bindings[i].free_callback) {
+ _instance_bindings[i].free_callback(_instance_bindings[i].token, this, _instance_bindings[i].binding);
}
}
+ memfree(_instance_bindings);
}
}
@@ -1824,7 +1915,6 @@ void ObjectDB::debug_objects(DebugFunc p_func) {
for (uint32_t i = 0, count = slot_count; i < slot_max && count != 0; i++) {
if (object_slots[i].validator) {
p_func(object_slots[i].object);
-
count--;
}
}
@@ -1853,7 +1943,7 @@ ObjectID ObjectDB::add_instance(Object *p_object) {
object_slots = (ObjectSlot *)memrealloc(object_slots, sizeof(ObjectSlot) * new_slot_max);
for (uint32_t i = slot_max; i < new_slot_max; i++) {
object_slots[i].object = nullptr;
- object_slots[i].is_reference = false;
+ object_slots[i].is_ref_counted = false;
object_slots[i].next_free = i;
object_slots[i].validator = 0;
}
@@ -1866,7 +1956,7 @@ ObjectID ObjectDB::add_instance(Object *p_object) {
ERR_FAIL_COND_V(object_slots[slot].object != nullptr, ObjectID());
}
object_slots[slot].object = p_object;
- object_slots[slot].is_reference = p_object->is_reference();
+ object_slots[slot].is_ref_counted = p_object->is_ref_counted();
validator_counter = (validator_counter + 1) & OBJECTDB_VALIDATOR_MASK;
if (unlikely(validator_counter == 0)) {
validator_counter = 1;
@@ -1877,7 +1967,7 @@ ObjectID ObjectDB::add_instance(Object *p_object) {
id <<= OBJECTDB_SLOT_MAX_COUNT_BITS;
id |= uint64_t(slot);
- if (p_object->is_reference()) {
+ if (p_object->is_ref_counted()) {
id |= OBJECTDB_REFERENCE_BIT;
}
@@ -1915,7 +2005,7 @@ void ObjectDB::remove_instance(Object *p_object) {
object_slots[slot_count].next_free = slot;
//invalidate, so checks against it fail
object_slots[slot].validator = 0;
- object_slots[slot].is_reference = false;
+ object_slots[slot].is_ref_counted = false;
object_slots[slot].object = nullptr;
spin_lock.unlock();
@@ -1950,7 +2040,7 @@ void ObjectDB::cleanup() {
extra_info = " - Resource path: " + String(resource_get_path->call(obj, nullptr, 0, call_error));
}
- uint64_t id = uint64_t(i) | (uint64_t(object_slots[i].validator) << OBJECTDB_VALIDATOR_BITS) | (object_slots[i].is_reference ? OBJECTDB_REFERENCE_BIT : 0);
+ uint64_t id = uint64_t(i) | (uint64_t(object_slots[i].validator) << OBJECTDB_VALIDATOR_BITS) | (object_slots[i].is_ref_counted ? OBJECTDB_REFERENCE_BIT : 0);
print_line("Leaked instance: " + String(obj->get_class()) + ":" + itos(id) + extra_info);
count--;
diff --git a/core/object/object.h b/core/object/object.h
index 448a33d3bc..a44d921bff 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -31,6 +31,7 @@
#ifndef OBJECT_H
#define OBJECT_H
+#include "core/extension/gdnative_interface.h"
#include "core/object/object_id.h"
#include "core/os/rw_lock.h"
#include "core/os/spin_lock.h"
@@ -43,13 +44,13 @@
#include "core/variant/callable_bind.h"
#include "core/variant/variant.h"
-#define VARIANT_ARG_LIST const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant()
-#define VARIANT_ARG_PASS p_arg1, p_arg2, p_arg3, p_arg4, p_arg5
-#define VARIANT_ARG_DECLARE const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5
-#define VARIANT_ARG_MAX 5
-#define VARIANT_ARGPTRS const Variant *argptr[5] = { &p_arg1, &p_arg2, &p_arg3, &p_arg4, &p_arg5 };
-#define VARIANT_ARGPTRS_PASS *argptr[0], *argptr[1], *argptr[2], *argptr[3], *argptr[4]
-#define VARIANT_ARGS_FROM_ARRAY(m_arr) m_arr[0], m_arr[1], m_arr[2], m_arr[3], m_arr[4]
+#define VARIANT_ARG_LIST const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant(), const Variant &p_arg6 = Variant(), const Variant &p_arg7 = Variant(), const Variant &p_arg8 = Variant()
+#define VARIANT_ARG_PASS p_arg1, p_arg2, p_arg3, p_arg4, p_arg5, p_arg6, p_arg7, p_arg8
+#define VARIANT_ARG_DECLARE const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5, const Variant &p_arg6, const Variant &p_arg7, const Variant &p_arg8
+#define VARIANT_ARG_MAX 8
+#define VARIANT_ARGPTRS const Variant *argptr[8] = { &p_arg1, &p_arg2, &p_arg3, &p_arg4, &p_arg5, &p_arg6, &p_arg7, &p_arg8 };
+#define VARIANT_ARGPTRS_PASS *argptr[0], *argptr[1], *argptr[2], *argptr[3], *argptr[4], *argptr[5], *argptr[6]], *argptr[7]
+#define VARIANT_ARGS_FROM_ARRAY(m_arr) m_arr[0], m_arr[1], m_arr[2], m_arr[3], m_arr[4], m_arr[5], m_arr[6], m_arr[7]
/**
@author Juan Linietsky <reduzio@gmail.com>
@@ -57,9 +58,9 @@
enum PropertyHint {
PROPERTY_HINT_NONE, ///< no hint provided.
- PROPERTY_HINT_RANGE, ///< hint_text = "min,max,step,slider; //slider is optional"
- PROPERTY_HINT_EXP_RANGE, ///< hint_text = "min,max,step", exponential edit
+ PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_lesser][,noslider][,radians][,degrees][,exp][,suffix:<keyword>] range.
PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc"
+ PROPERTY_HINT_ENUM_SUGGESTION, ///< hint_text= "val1,val2,val3,etc"
PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout")
PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer)
PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer)
@@ -96,11 +97,13 @@ enum PropertyHint {
PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog
PROPERTY_HINT_INT_IS_OBJECTID,
PROPERTY_HINT_ARRAY_TYPE,
+ PROPERTY_HINT_INT_IS_POINTER,
PROPERTY_HINT_MAX,
// When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit
};
enum PropertyUsageFlags {
+ PROPERTY_USAGE_NONE = 0,
PROPERTY_USAGE_STORAGE = 1,
PROPERTY_USAGE_EDITOR = 2,
PROPERTY_USAGE_NETWORK = 4,
@@ -129,28 +132,39 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 26, // when loading, the resource for this property can be set at the end of loading
PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 27, // For Object properties, instantiate them when creating in editor.
PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 28, //for project or editor settings, show when basic settings are selected
+ PROPERTY_USAGE_READ_ONLY = 1 << 29, // Mark a property as read-only in the inspector.
+ PROPERTY_USAGE_ARRAY = 1 << 30, // Used in the inspector to group properties as elements of an array.
PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK,
PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED,
PROPERTY_USAGE_NOEDITOR = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_NETWORK,
};
-#define ADD_SIGNAL(m_signal) ClassDB::add_signal(get_class_static(), m_signal)
-#define ADD_PROPERTY(m_property, m_setter, m_getter) ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter))
-#define ADD_PROPERTYI(m_property, m_setter, m_getter, m_index) ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter), m_index)
-#define ADD_PROPERTY_DEFAULT(m_property, m_default) ClassDB::set_property_default_value(get_class_static(), m_property, m_default)
-#define ADD_GROUP(m_name, m_prefix) ClassDB::add_property_group(get_class_static(), m_name, m_prefix)
-#define ADD_SUBGROUP(m_name, m_prefix) ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix)
+#define ADD_SIGNAL(m_signal) ::ClassDB::add_signal(get_class_static(), m_signal)
+#define ADD_PROPERTY(m_property, m_setter, m_getter) ::ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter))
+#define ADD_PROPERTYI(m_property, m_setter, m_getter, m_index) ::ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter), m_index)
+#define ADD_PROPERTY_DEFAULT(m_property, m_default) ::ClassDB::set_property_default_value(get_class_static(), m_property, m_default)
+#define ADD_GROUP(m_name, m_prefix) ::ClassDB::add_property_group(get_class_static(), m_name, m_prefix)
+#define ADD_SUBGROUP(m_name, m_prefix) ::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix)
+#define ADD_LINKED_PROPERTY(m_property, m_linked_property) ::ClassDB::add_linked_property(get_class_static(), m_property, m_linked_property)
+
+#define ADD_ARRAY_COUNT(m_label, m_count_property, m_count_property_setter, m_count_property_getter, m_prefix) ClassDB::add_property_array_count(get_class_static(), m_label, m_count_property, _scs_create(m_count_property_setter), _scs_create(m_count_property_getter), m_prefix)
+#define ADD_ARRAY_COUNT_WITH_USAGE_FLAGS(m_label, m_count_property, m_count_property_setter, m_count_property_getter, m_prefix, m_property_usage_flags) ClassDB::add_property_array_count(get_class_static(), m_label, m_count_property, _scs_create(m_count_property_setter), _scs_create(m_count_property_getter), m_prefix, m_property_usage_flags)
+#define ADD_ARRAY(m_array_path, m_prefix) ClassDB::add_property_array(get_class_static(), m_array_path, m_prefix)
struct PropertyInfo {
Variant::Type type = Variant::NIL;
String name;
- StringName class_name; //for classes
+ StringName class_name; // For classes
PropertyHint hint = PROPERTY_HINT_NONE;
String hint_string;
uint32_t usage = PROPERTY_USAGE_DEFAULT;
- _FORCE_INLINE_ PropertyInfo added_usage(int p_fl) const {
+#ifdef TOOLS_ENABLED
+ Vector<String> linked_properties;
+#endif
+
+ _FORCE_INLINE_ PropertyInfo added_usage(uint32_t p_fl) const {
PropertyInfo pi = *this;
pi.usage |= p_fl;
return pi;
@@ -162,7 +176,7 @@ struct PropertyInfo {
PropertyInfo() {}
- PropertyInfo(Variant::Type p_type, const String p_name, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = "", uint32_t p_usage = PROPERTY_USAGE_DEFAULT, const StringName &p_class_name = StringName()) :
+ PropertyInfo(const Variant::Type p_type, const String p_name, const PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = "", const uint32_t p_usage = PROPERTY_USAGE_DEFAULT, const StringName &p_class_name = StringName()) :
type(p_type),
name(p_name),
hint(p_hint),
@@ -238,6 +252,52 @@ struct MethodInfo {
////else
//return nullptr;
+// API used to extend in GDNative and other C compatible compiled languages
+class MethodBind;
+
+struct ObjectNativeExtension {
+ ObjectNativeExtension *parent = nullptr;
+ List<ObjectNativeExtension *> children;
+ StringName parent_class_name;
+ StringName class_name;
+ bool editor_class = false;
+ GDNativeExtensionClassSet set;
+ GDNativeExtensionClassGet get;
+ GDNativeExtensionClassGetPropertyList get_property_list;
+ GDNativeExtensionClassFreePropertyList free_property_list;
+ GDNativeExtensionClassNotification notification;
+ GDNativeExtensionClassToString to_string;
+ GDNativeExtensionClassReference reference;
+ GDNativeExtensionClassReference unreference;
+
+ _FORCE_INLINE_ bool is_class(const String &p_class) const {
+ const ObjectNativeExtension *e = this;
+ while (e) {
+ if (p_class == e->class_name.operator String()) {
+ return true;
+ }
+ e = e->parent;
+ }
+ return false;
+ }
+ void *class_userdata = nullptr;
+
+ GDNativeExtensionClassCreateInstance create_instance;
+ GDNativeExtensionClassFreeInstance free_instance;
+ GDNativeExtensionClassObjectInstance set_object_instance;
+ GDNativeExtensionClassGetVirtual get_virtual;
+};
+
+#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call(__VA_ARGS__)
+#define GDVIRTUAL_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call(__VA_ARGS__)
+#ifdef DEBUG_METHODS_ENABLED
+#define GDVIRTUAL_BIND(m_name, ...) ::ClassDB::add_virtual_method(get_class_static(), _gdvirtual_##m_name##_get_method_info(), true, sarray(__VA_ARGS__));
+#else
+#define GDVIRTUAL_BIND(m_name, ...)
+#endif
+#define GDVIRTUAL_IS_OVERRIDDEN(m_name) _gdvirtual_##m_name##_overridden()
+#define GDVIRTUAL_IS_OVERRIDDEN_PTR(m_obj, m_name) m_obj->_gdvirtual_##m_name##_overridden()
+
/*
the following is an incomprehensible blob of hacks and workarounds to compensate for many of the fallencies in C++. As a plus, this macro pretty much alone defines the object model.
*/
@@ -258,13 +318,19 @@ private:
private: \
void operator=(const m_class &p_rval) {} \
mutable StringName _class_name; \
- friend class ClassDB; \
+ friend class ::ClassDB; \
\
public: \
virtual String get_class() const override { \
+ if (_get_extension()) { \
+ return _get_extension()->class_name.operator String(); \
+ } \
return String(#m_class); \
} \
virtual const StringName *_get_class_namev() const override { \
+ if (_get_extension()) { \
+ return &_get_extension()->class_name; \
+ } \
if (!_class_name) { \
_class_name = get_class_static(); \
} \
@@ -297,7 +363,12 @@ public:
static String inherits_static() { \
return String(#m_inherits); \
} \
- virtual bool is_class(const String &p_class) const override { return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); } \
+ virtual bool is_class(const String &p_class) const override { \
+ if (_get_extension() && _get_extension()->is_class(p_class)) { \
+ return true; \
+ } \
+ return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); \
+ } \
virtual bool is_class_ptr(void *p_ptr) const override { return (p_ptr == get_class_ptr_static()) ? true : m_inherits::is_class_ptr(p_ptr); } \
\
static void get_valid_parents_static(List<String> *p_parents) { \
@@ -320,7 +391,7 @@ public:
return; \
} \
m_inherits::initialize_class(); \
- ClassDB::_add_class<m_class>(); \
+ ::ClassDB::_add_class<m_class>(); \
if (m_class::_get_bind_methods() != m_inherits::_get_bind_methods()) { \
_bind_methods(); \
} \
@@ -363,13 +434,13 @@ protected:
} \
p_list->push_back(PropertyInfo(Variant::NIL, get_class_static(), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); \
if (!_is_gpl_reversed()) { \
- ClassDB::get_property_list(#m_class, p_list, true, this); \
+ ::ClassDB::get_property_list(#m_class, p_list, true, this); \
} \
if (m_class::_get_get_property_list() != m_inherits::_get_get_property_list()) { \
_get_property_list(p_list); \
} \
if (_is_gpl_reversed()) { \
- ClassDB::get_property_list(#m_class, p_list, true, this); \
+ ::ClassDB::get_property_list(#m_class, p_list, true, this); \
} \
if (p_reversed) { \
m_inherits::_get_property_listv(p_list, p_reversed); \
@@ -430,16 +501,15 @@ public:
};
private:
- enum {
- MAX_SCRIPT_INSTANCE_BINDINGS = 8
- };
-
#ifdef DEBUG_ENABLED
friend struct _ObjectDebugLock;
#endif
friend bool predelete_handler(Object *);
friend void postinitialize_handler(Object *);
+ ObjectNativeExtension *_extension = nullptr;
+ GDExtensionClassInstancePtr _extension_instance = nullptr;
+
struct SignalData {
struct Slot {
int reference_count = 0;
@@ -487,14 +557,40 @@ private:
_FORCE_INLINE_ void _construct_object(bool p_reference);
- friend class Reference;
+ friend class RefCounted;
bool type_is_reference = false;
- SafeNumeric<uint32_t> instance_binding_count;
- void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS];
+
+ std::mutex _instance_binding_mutex;
+ struct InstanceBinding {
+ void *binding;
+ void *token;
+ GDNativeInstanceBindingFreeCallback free_callback = nullptr;
+ GDNativeInstanceBindingReferenceCallback reference_callback = nullptr;
+ };
+ InstanceBinding *_instance_bindings = nullptr;
+ uint32_t _instance_binding_count = 0;
Object(bool p_reference);
protected:
+ _FORCE_INLINE_ bool _instance_binding_reference(bool p_reference) {
+ bool can_die = true;
+ if (_instance_bindings) {
+ _instance_binding_mutex.lock();
+ for (uint32_t i = 0; i < _instance_binding_count; i++) {
+ if (_instance_bindings[i].reference_callback) {
+ if (!_instance_bindings[i].reference_callback(_instance_bindings[i].token, _instance_bindings[i].binding, p_reference)) {
+ can_die = false;
+ }
+ }
+ }
+ _instance_binding_mutex.unlock();
+ }
+ return can_die;
+ }
+ friend class NativeExtensionMethodBind;
+ _ALWAYS_INLINE_ const ObjectNativeExtension *_get_extension() const { return _extension; }
+ _ALWAYS_INLINE_ GDExtensionClassInstancePtr _get_extension_instance() const { return _extension_instance; }
virtual void _initialize_classv() { initialize_class(); }
virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; };
virtual bool _getv(const StringName &p_name, Variant &r_property) const { return false; };
@@ -539,7 +635,7 @@ protected:
return &_class_name;
}
- Vector<String> _get_meta_list_bind() const;
+ Vector<StringName> _get_meta_list_bind() const;
Array _get_property_list_bind() const;
Array _get_method_list_bind() const;
@@ -610,13 +706,25 @@ public:
static String get_parent_class_static() { return String(); }
static String get_category_static() { return String(); }
- virtual String get_class() const { return "Object"; }
+ virtual String get_class() const {
+ if (_extension)
+ return _extension->class_name.operator String();
+ return "Object";
+ }
virtual String get_save_class() const { return get_class(); } //class stored when saving
- virtual bool is_class(const String &p_class) const { return (p_class == "Object"); }
+ virtual bool is_class(const String &p_class) const {
+ if (_extension && _extension->is_class(p_class)) {
+ return true;
+ }
+ return (p_class == "Object");
+ }
virtual bool is_class_ptr(void *p_ptr) const { return get_class_ptr_static() == p_ptr; }
_FORCE_INLINE_ const StringName &get_class_name() const {
+ if (_extension) {
+ return _extension->class_name;
+ }
if (!_class_ptr) {
return *_get_class_namev();
} else {
@@ -655,11 +763,11 @@ public:
/* SCRIPT */
- bool has_meta(const String &p_name) const;
- void set_meta(const String &p_name, const Variant &p_value);
- void remove_meta(const String &p_name);
- Variant get_meta(const String &p_name) const;
- void get_meta_list(List<String> *p_list) const;
+ bool has_meta(const StringName &p_name) const;
+ void set_meta(const StringName &p_name, const Variant &p_value);
+ void remove_meta(const StringName &p_name);
+ Variant get_meta(const StringName &p_name) const;
+ void get_meta_list(List<StringName> *p_list) const;
#ifdef TOOLS_ENABLED
void set_edited(bool p_edited);
@@ -716,14 +824,15 @@ public:
#endif
- //used by script languages to store binding data
- void *get_script_instance_binding(int p_script_language_index);
- bool has_script_instance_binding(int p_script_language_index);
- void set_script_instance_binding(int p_script_language_index, void *p_data);
+ // Used by script languages to store binding data.
+ void *get_instance_binding(void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks);
+ // Used on creation by binding only.
+ void set_instance_binding(void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks);
+ bool has_instance_binding(void *p_token);
void clear_internal_resource_paths();
- _ALWAYS_INLINE_ bool is_reference() const { return type_is_reference; }
+ _ALWAYS_INLINE_ bool is_ref_counted() const { return type_is_reference; }
Object();
virtual ~Object();
@@ -743,7 +852,7 @@ class ObjectDB {
struct ObjectSlot { //128 bits per slot
uint64_t validator : OBJECTDB_VALIDATOR_BITS;
uint64_t next_free : OBJECTDB_SLOT_MAX_COUNT_BITS;
- uint64_t is_reference : 1;
+ uint64_t is_ref_counted : 1;
Object *object;
};
diff --git a/core/object/object_id.h b/core/object/object_id.h
index 7f2496ad48..0666ec0855 100644
--- a/core/object/object_id.h
+++ b/core/object/object_id.h
@@ -42,7 +42,7 @@ class ObjectID {
uint64_t id = 0;
public:
- _ALWAYS_INLINE_ bool is_reference() const { return (id & (uint64_t(1) << 63)) != 0; }
+ _ALWAYS_INLINE_ bool is_ref_counted() const { return (id & (uint64_t(1) << 63)) != 0; }
_ALWAYS_INLINE_ bool is_valid() const { return id != 0; }
_ALWAYS_INLINE_ bool is_null() const { return id == 0; }
_ALWAYS_INLINE_ operator uint64_t() const { return id; }
diff --git a/core/object/reference.cpp b/core/object/ref_counted.cpp
index 22e4e8a336..2833f774dc 100644
--- a/core/object/reference.cpp
+++ b/core/object/ref_counted.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* reference.cpp */
+/* ref_counted.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "reference.h"
+#include "ref_counted.h"
#include "core/object/script_language.h"
-bool Reference::init_ref() {
+bool RefCounted::init_ref() {
if (reference()) {
if (!is_referenced() && refcount_init.unref()) {
unreference(); // first referencing is already 1, so compensate for the ref above
@@ -44,17 +44,17 @@ bool Reference::init_ref() {
}
}
-void Reference::_bind_methods() {
- ClassDB::bind_method(D_METHOD("init_ref"), &Reference::init_ref);
- ClassDB::bind_method(D_METHOD("reference"), &Reference::reference);
- ClassDB::bind_method(D_METHOD("unreference"), &Reference::unreference);
+void RefCounted::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("init_ref"), &RefCounted::init_ref);
+ ClassDB::bind_method(D_METHOD("reference"), &RefCounted::reference);
+ ClassDB::bind_method(D_METHOD("unreference"), &RefCounted::unreference);
}
-int Reference::reference_get_count() const {
+int RefCounted::reference_get_count() const {
return refcount.get();
}
-bool Reference::reference() {
+bool RefCounted::reference() {
uint32_t rc_val = refcount.refval();
bool success = rc_val != 0;
@@ -62,19 +62,17 @@ bool Reference::reference() {
if (get_script_instance()) {
get_script_instance()->refcount_incremented();
}
- if (instance_binding_count.get() > 0 && !ScriptServer::are_languages_finished()) {
- for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
- if (_script_instance_bindings[i]) {
- ScriptServer::get_language(i)->refcount_incremented_instance_binding(this);
- }
- }
+ if (_get_extension() && _get_extension()->reference) {
+ _get_extension()->reference(_get_extension_instance());
}
+
+ _instance_binding_reference(true);
}
return success;
}
-bool Reference::unreference() {
+bool RefCounted::unreference() {
uint32_t rc_val = refcount.unrefval();
bool die = rc_val == 0;
@@ -83,20 +81,17 @@ bool Reference::unreference() {
bool script_ret = get_script_instance()->refcount_decremented();
die = die && script_ret;
}
- if (instance_binding_count.get() > 0 && !ScriptServer::are_languages_finished()) {
- for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
- if (_script_instance_bindings[i]) {
- bool script_ret = ScriptServer::get_language(i)->refcount_decremented_instance_binding(this);
- die = die && script_ret;
- }
- }
+ if (_get_extension() && _get_extension()->unreference) {
+ _get_extension()->unreference(_get_extension_instance());
}
+
+ die = die && _instance_binding_reference(false);
}
return die;
}
-Reference::Reference() :
+RefCounted::RefCounted() :
Object(true) {
refcount.init();
refcount_init.init();
@@ -111,7 +106,7 @@ Variant WeakRef::get_ref() const {
if (!obj) {
return Variant();
}
- Reference *r = cast_to<Reference>(obj);
+ RefCounted *r = cast_to<RefCounted>(obj);
if (r) {
return REF(r);
}
diff --git a/core/object/reference.h b/core/object/ref_counted.h
index d02cb12069..f2dd2aa324 100644
--- a/core/object/reference.h
+++ b/core/object/ref_counted.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* reference.h */
+/* ref_counted.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,14 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef REFERENCE_H
-#define REFERENCE_H
+#ifndef REF_COUNTED_H
+#define REF_COUNTED_H
#include "core/object/class_db.h"
#include "core/templates/safe_refcount.h"
-class Reference : public Object {
- GDCLASS(Reference, Object);
+class RefCounted : public Object {
+ GDCLASS(RefCounted, Object);
SafeRefCount refcount;
SafeRefCount refcount_init;
@@ -49,8 +49,8 @@ public:
bool unreference();
int reference_get_count() const;
- Reference();
- ~Reference() {}
+ RefCounted();
+ ~RefCounted() {}
};
template <class T>
@@ -78,7 +78,7 @@ class Ref {
}
}
- //virtual Reference * get_reference() const { return reference; }
+ //virtual RefCounted * get_reference() const { return reference; }
public:
_FORCE_INLINE_ bool operator==(const T *p_ptr) const {
return reference == p_ptr;
@@ -130,7 +130,7 @@ public:
template <class T_Other>
void operator=(const Ref<T_Other> &p_from) {
- Reference *refb = const_cast<Reference *>(static_cast<const Reference *>(p_from.ptr()));
+ RefCounted *refb = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_from.ptr()));
if (!refb) {
unref();
return;
@@ -179,7 +179,7 @@ public:
template <class T_Other>
Ref(const Ref<T_Other> &p_from) {
- Reference *refb = const_cast<Reference *>(static_cast<const Reference *>(p_from.ptr()));
+ RefCounted *refb = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_from.ptr()));
if (!refb) {
unref();
return;
@@ -213,7 +213,7 @@ public:
inline bool is_null() const { return reference == nullptr; }
void unref() {
- //TODO this should be moved to mutexes, since this engine does not really
+ // TODO: this should be moved to mutexes, since this engine does not really
// do a lot of referencing on references and stuff
// mutexes will avoid more crashes?
@@ -223,7 +223,7 @@ public:
reference = nullptr;
}
- void instance() {
+ void instantiate() {
ref(memnew(T));
}
@@ -234,10 +234,10 @@ public:
}
};
-typedef Ref<Reference> REF;
+typedef Ref<RefCounted> REF;
-class WeakRef : public Reference {
- GDCLASS(WeakRef, Reference);
+class WeakRef : public RefCounted {
+ GDCLASS(WeakRef, RefCounted);
ObjectID ref;
@@ -258,20 +258,22 @@ struct PtrToArg<Ref<T>> {
return Ref<T>(const_cast<T *>(reinterpret_cast<const T *>(p_ptr)));
}
+ typedef Ref<T> EncodeT;
+
_FORCE_INLINE_ static void encode(Ref<T> p_val, const void *p_ptr) {
- *(Ref<Reference> *)p_ptr = p_val;
+ *(Ref<RefCounted> *)p_ptr = p_val;
}
};
template <class T>
struct PtrToArg<const Ref<T> &> {
+ typedef Ref<T> EncodeT;
+
_FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) {
return Ref<T>((T *)p_ptr);
}
};
-#ifdef DEBUG_METHODS_ENABLED
-
template <class T>
struct GetTypeInfo<Ref<T>> {
static const Variant::Type VARIANT_TYPE = Variant::OBJECT;
@@ -292,6 +294,4 @@ struct GetTypeInfo<const Ref<T> &> {
}
};
-#endif // DEBUG_METHODS_ENABLED
-
-#endif // REFERENCE_H
+#endif // REF_COUNTED_H
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index 42fb0a0caf..0fb8c7350c 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -63,8 +63,8 @@ Array Script::_get_script_property_list() {
Array ret;
List<PropertyInfo> list;
get_script_property_list(&list);
- for (List<PropertyInfo>::Element *E = list.front(); E; E = E->next()) {
- ret.append(E->get().operator Dictionary());
+ for (const PropertyInfo &E : list) {
+ ret.append(E.operator Dictionary());
}
return ret;
}
@@ -73,8 +73,8 @@ Array Script::_get_script_method_list() {
Array ret;
List<MethodInfo> list;
get_script_method_list(&list);
- for (List<MethodInfo>::Element *E = list.front(); E; E = E->next()) {
- ret.append(E->get().operator Dictionary());
+ for (const MethodInfo &E : list) {
+ ret.append(E.operator Dictionary());
}
return ret;
}
@@ -83,8 +83,8 @@ Array Script::_get_script_signal_list() {
Array ret;
List<MethodInfo> list;
get_script_signal_list(&list);
- for (List<MethodInfo>::Element *E = list.front(); E; E = E->next()) {
- ret.append(E->get().operator Dictionary());
+ for (const MethodInfo &E : list) {
+ ret.append(E.operator Dictionary());
}
return ret;
}
@@ -100,7 +100,7 @@ Dictionary Script::_get_script_constant_map() {
}
void Script::_bind_methods() {
- ClassDB::bind_method(D_METHOD("can_instance"), &Script::can_instance);
+ ClassDB::bind_method(D_METHOD("can_instantiate"), &Script::can_instantiate);
//ClassDB::bind_method(D_METHOD("instance_create","base_object"),&Script::instance_create);
ClassDB::bind_method(D_METHOD("instance_has", "base_object"), &Script::instance_has);
ClassDB::bind_method(D_METHOD("has_source_code"), &Script::has_source_code);
@@ -120,7 +120,7 @@ void Script::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_tool"), &Script::is_tool);
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_code", PROPERTY_HINT_NONE, "", 0), "set_source_code", "get_source_code");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_source_code", "get_source_code");
}
void ScriptServer::set_scripting_enabled(bool p_enabled) {
@@ -257,8 +257,8 @@ void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) {
classes.push_back(*K);
}
classes.sort_custom<StringName::AlphCompare>();
- for (List<StringName>::Element *E = classes.front(); E; E = E->next()) {
- r_global_classes->push_back(E->get());
+ for (const StringName &E : classes) {
+ r_global_classes->push_back(E);
}
}
@@ -266,12 +266,12 @@ void ScriptServer::save_global_classes() {
List<StringName> gc;
get_global_class_list(&gc);
Array gcarr;
- for (List<StringName>::Element *E = gc.front(); E; E = E->next()) {
+ for (const StringName &E : gc) {
Dictionary d;
- d["class"] = E->get();
- d["language"] = global_classes[E->get()].language;
- d["path"] = global_classes[E->get()].path;
- d["base"] = global_classes[E->get()].base;
+ d["class"] = E;
+ d["language"] = global_classes[E].language;
+ d["path"] = global_classes[E].path;
+ d["base"] = global_classes[E].base;
gcarr.push_back(d);
}
@@ -297,10 +297,10 @@ void ScriptServer::save_global_classes() {
void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state) {
List<PropertyInfo> pinfo;
get_property_list(&pinfo);
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- if (E->get().usage & PROPERTY_USAGE_STORAGE) {
+ for (const PropertyInfo &E : pinfo) {
+ if (E.usage & PROPERTY_USAGE_STORAGE) {
Pair<StringName, Variant> p;
- p.first = E->get().name;
+ p.first = E.name;
if (get(p.first, p.second)) {
state.push_back(p);
}
@@ -353,10 +353,10 @@ void ScriptLanguage::get_core_type_words(List<String> *p_core_type_words) const
p_core_type_words->push_back("Vector3i");
p_core_type_words->push_back("Transform2D");
p_core_type_words->push_back("Plane");
- p_core_type_words->push_back("Quat");
+ p_core_type_words->push_back("Quaternion");
p_core_type_words->push_back("AABB");
p_core_type_words->push_back("Basis");
- p_core_type_words->push_back("Transform");
+ p_core_type_words->push_back("Transform3D");
p_core_type_words->push_back("Color");
p_core_type_words->push_back("StringName");
p_core_type_words->push_back("NodePath");
@@ -430,16 +430,16 @@ bool PlaceHolderScriptInstance::get(const StringName &p_name, Variant &r_ret) co
void PlaceHolderScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const {
if (script->is_placeholder_fallback_enabled()) {
- for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
- p_properties->push_back(E->get());
+ for (const PropertyInfo &E : properties) {
+ p_properties->push_back(E);
}
} else {
- for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
- PropertyInfo pinfo = E->get();
+ for (const PropertyInfo &E : properties) {
+ PropertyInfo pinfo = E;
if (!values.has(pinfo.name)) {
pinfo.usage |= PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE;
}
- p_properties->push_back(E->get());
+ p_properties->push_back(E);
}
}
}
@@ -489,11 +489,11 @@ bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const {
void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const Map<StringName, Variant> &p_values) {
Set<StringName> new_values;
- for (const List<PropertyInfo>::Element *E = p_properties.front(); E; E = E->next()) {
- StringName n = E->get().name;
+ for (const PropertyInfo &E : p_properties) {
+ StringName n = E.name;
new_values.insert(n);
- if (!values.has(n) || values[n].get_type() != E->get().type) {
+ if (!values.has(n) || values[n].get_type() != E.type) {
if (p_values.has(n)) {
values[n] = p_values[n];
}
@@ -511,7 +511,7 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c
Variant defval;
if (script->get_property_default_value(E->key(), defval)) {
//remove because it's the same as the default value
- if (defval == E->get()) {
+ if (defval == E) {
to_remove.push_back(E->key());
}
}
@@ -542,8 +542,8 @@ void PlaceHolderScriptInstance::property_set_fallback(const StringName &p_name,
}
bool found = false;
- for (const List<PropertyInfo>::Element *F = properties.front(); F; F = F->next()) {
- if (F->get().name == p_name) {
+ for (const PropertyInfo &F : properties) {
+ if (F.name == p_name) {
found = true;
break;
}
@@ -585,14 +585,6 @@ Variant PlaceHolderScriptInstance::property_get_fallback(const StringName &p_nam
return Variant();
}
-uint16_t PlaceHolderScriptInstance::get_rpc_method_id(const StringName &p_method) const {
- return UINT16_MAX;
-}
-
-uint16_t PlaceHolderScriptInstance::get_rset_property_id(const StringName &p_method) const {
- return UINT16_MAX;
-}
-
PlaceHolderScriptInstance::PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner) :
owner(p_owner),
language(p_language),
diff --git a/core/object/script_language.h b/core/object/script_language.h
index 9ed3c7e80f..8d76cbf479 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -32,8 +32,8 @@
#define SCRIPT_LANGUAGE_H
#include "core/doc_data.h"
-#include "core/io/multiplayer_api.h"
#include "core/io/resource.h"
+#include "core/multiplayer/multiplayer.h"
#include "core/templates/map.h"
#include "core/templates/pair.h"
@@ -41,21 +41,6 @@ class ScriptLanguage;
typedef void (*ScriptEditRequestFunction)(const String &p_path);
-struct ScriptNetData {
- StringName name;
- MultiplayerAPI::RPCMode mode;
- bool operator==(ScriptNetData const &p_other) const {
- return name == p_other.name;
- }
-};
-
-struct SortNetData {
- StringName::AlphCompare compare;
- bool operator()(const ScriptNetData &p_a, const ScriptNetData &p_b) const {
- return compare(p_a.name, p_b.name);
- }
-};
-
class ScriptServer {
enum {
MAX_LANGUAGES = 16
@@ -130,7 +115,7 @@ protected:
Dictionary _get_script_constant_map();
public:
- virtual bool can_instance() const = 0;
+ virtual bool can_instantiate() const = 0;
virtual Ref<Script> get_base_script() const = 0; //for script inheritance
@@ -174,17 +159,7 @@ public:
virtual bool is_placeholder_fallback_enabled() const { return false; }
- virtual Vector<ScriptNetData> get_rpc_methods() const = 0;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const = 0;
- virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const = 0;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const = 0;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0;
-
- virtual Vector<ScriptNetData> get_rset_properties() const = 0;
- virtual uint16_t get_rset_property_id(const StringName &p_property) const = 0;
- virtual StringName get_rset_property(const uint16_t p_rset_property_id) const = 0;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const = 0;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const = 0;
Script() {}
};
@@ -225,17 +200,7 @@ public:
virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid);
virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid);
- virtual Vector<ScriptNetData> get_rpc_methods() const = 0;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const = 0;
- virtual StringName get_rpc_method(uint16_t p_id) const = 0;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const = 0;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0;
-
- virtual Vector<ScriptNetData> get_rset_properties() const = 0;
- virtual uint16_t get_rset_property_id(const StringName &p_variable) const = 0;
- virtual StringName get_rset_property(uint16_t p_id) const = 0;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const = 0;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const = 0;
virtual ScriptLanguage *get_language() = 0;
virtual ~ScriptInstance();
@@ -303,6 +268,12 @@ public:
String message;
};
+ struct ScriptError {
+ int line = -1;
+ int column = -1;
+ String message;
+ };
+
void get_core_type_words(List<String> *p_core_type_words) const;
virtual void get_reserved_words(List<String> *p_words) const = 0;
virtual bool is_control_flow_keyword(String p_string) const = 0;
@@ -311,7 +282,7 @@ public:
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const = 0;
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {}
virtual bool is_using_templates() { return false; }
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = nullptr, List<Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const = 0;
+ virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const = 0;
virtual String validate_path(const String &p_path) const { return ""; }
virtual Script *create_script() const = 0;
virtual bool has_named_classes() const = 0;
@@ -339,6 +310,7 @@ public:
Ref<Script> script;
String class_name;
String class_member;
+ String class_path;
int location;
};
@@ -447,17 +419,7 @@ public:
virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr);
virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid = nullptr);
- virtual Vector<ScriptNetData> get_rpc_methods() const { return Vector<ScriptNetData>(); }
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
- virtual StringName get_rpc_method(uint16_t p_id) const { return StringName(); }
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
-
- virtual Vector<ScriptNetData> get_rset_properties() const { return Vector<ScriptNetData>(); }
- virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
- virtual StringName get_rset_property(uint16_t p_id) const { return StringName(); }
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const { return MultiplayerAPI::RPC_MODE_DISABLED; }
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const { return Vector<Multiplayer::RPCConfig>(); }
PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner);
~PlaceHolderScriptInstance();
diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp
index e8735e335c..b7d2bac96d 100644
--- a/core/object/undo_redo.cpp
+++ b/core/object/undo_redo.cpp
@@ -39,11 +39,15 @@ void UndoRedo::_discard_redo() {
}
for (int i = current_action + 1; i < actions.size(); i++) {
- for (List<Operation>::Element *E = actions.write[i].do_ops.front(); E; E = E->next()) {
- if (E->get().type == Operation::TYPE_REFERENCE) {
- Object *obj = ObjectDB::get_instance(E->get().object);
- if (obj) {
- memdelete(obj);
+ for (Operation &E : actions.write[i].do_ops) {
+ if (E.type == Operation::TYPE_REFERENCE) {
+ if (E.ref.is_valid()) {
+ E.ref.unref();
+ } else {
+ Object *obj = ObjectDB::get_instance(E.object);
+ if (obj) {
+ memdelete(obj);
+ }
}
}
}
@@ -65,7 +69,7 @@ bool UndoRedo::_redo(bool p_execute) {
_process_operation_list(actions.write[current_action].do_ops.front());
}
version++;
- emit_signal("version_changed");
+ emit_signal(SNAME("version_changed"));
return true;
}
@@ -122,8 +126,8 @@ void UndoRedo::add_do_method(Object *p_object, const StringName &p_method, VARIA
ERR_FAIL_COND((current_action + 1) >= actions.size());
Operation do_op;
do_op.object = p_object->get_instance_id();
- if (Object::cast_to<Reference>(p_object)) {
- do_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object));
+ if (Object::cast_to<RefCounted>(p_object)) {
+ do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object));
}
do_op.type = Operation::TYPE_METHOD;
@@ -148,8 +152,8 @@ void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VAR
Operation undo_op;
undo_op.object = p_object->get_instance_id();
- if (Object::cast_to<Reference>(p_object)) {
- undo_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object));
+ if (Object::cast_to<RefCounted>(p_object)) {
+ undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object));
}
undo_op.type = Operation::TYPE_METHOD;
@@ -167,8 +171,8 @@ void UndoRedo::add_do_property(Object *p_object, const StringName &p_property, c
ERR_FAIL_COND((current_action + 1) >= actions.size());
Operation do_op;
do_op.object = p_object->get_instance_id();
- if (Object::cast_to<Reference>(p_object)) {
- do_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object));
+ if (Object::cast_to<RefCounted>(p_object)) {
+ do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object));
}
do_op.type = Operation::TYPE_PROPERTY;
@@ -189,8 +193,8 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property,
Operation undo_op;
undo_op.object = p_object->get_instance_id();
- if (Object::cast_to<Reference>(p_object)) {
- undo_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object));
+ if (Object::cast_to<RefCounted>(p_object)) {
+ undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object));
}
undo_op.type = Operation::TYPE_PROPERTY;
@@ -205,8 +209,8 @@ void UndoRedo::add_do_reference(Object *p_object) {
ERR_FAIL_COND((current_action + 1) >= actions.size());
Operation do_op;
do_op.object = p_object->get_instance_id();
- if (Object::cast_to<Reference>(p_object)) {
- do_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object));
+ if (Object::cast_to<RefCounted>(p_object)) {
+ do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object));
}
do_op.type = Operation::TYPE_REFERENCE;
@@ -225,8 +229,8 @@ void UndoRedo::add_undo_reference(Object *p_object) {
Operation undo_op;
undo_op.object = p_object->get_instance_id();
- if (Object::cast_to<Reference>(p_object)) {
- undo_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object));
+ if (Object::cast_to<RefCounted>(p_object)) {
+ undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object));
}
undo_op.type = Operation::TYPE_REFERENCE;
@@ -240,11 +244,15 @@ void UndoRedo::_pop_history_tail() {
return;
}
- for (List<Operation>::Element *E = actions.write[0].undo_ops.front(); E; E = E->next()) {
- if (E->get().type == Operation::TYPE_REFERENCE) {
- Object *obj = ObjectDB::get_instance(E->get().object);
- if (obj) {
- memdelete(obj);
+ for (Operation &E : actions.write[0].undo_ops) {
+ if (E.type == Operation::TYPE_REFERENCE) {
+ if (E.ref.is_valid()) {
+ E.ref.unref();
+ } else {
+ Object *obj = ObjectDB::get_instance(E.object);
+ if (obj) {
+ memdelete(obj);
+ }
}
}
}
@@ -352,7 +360,7 @@ bool UndoRedo::undo() {
_process_operation_list(actions.write[current_action].undo_ops.front());
current_action--;
version--;
- emit_signal("version_changed");
+ emit_signal(SNAME("version_changed"));
return true;
}
@@ -385,7 +393,7 @@ void UndoRedo::clear_history(bool p_increase_version) {
if (p_increase_version) {
version++;
- emit_signal("version_changed");
+ emit_signal(SNAME("version_changed"));
}
}
@@ -397,11 +405,11 @@ String UndoRedo::get_current_action_name() const {
return actions[current_action].name;
}
-bool UndoRedo::has_undo() {
+bool UndoRedo::has_undo() const {
return current_action >= 0;
}
-bool UndoRedo::has_redo() {
+bool UndoRedo::has_redo() const {
return (current_action + 1) < actions.size();
}
@@ -460,8 +468,8 @@ Variant UndoRedo::_add_do_method(const Variant **p_args, int p_argcount, Callabl
v[i] = *p_args[i + 2];
}
- static_assert(VARIANT_ARG_MAX == 5, "This code needs to be updated if VARIANT_ARG_MAX != 5");
- add_do_method(object, method, v[0], v[1], v[2], v[3], v[4]);
+ static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8");
+ add_do_method(object, method, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
return Variant();
}
@@ -497,8 +505,8 @@ Variant UndoRedo::_add_undo_method(const Variant **p_args, int p_argcount, Calla
v[i] = *p_args[i + 2];
}
- static_assert(VARIANT_ARG_MAX == 5, "This code needs to be updated if VARIANT_ARG_MAX != 5");
- add_undo_method(object, method, v[0], v[1], v[2], v[3], v[4]);
+ static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8");
+ add_undo_method(object, method, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
return Variant();
}
diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h
index a08ca7792f..d1ce252d86 100644
--- a/core/object/undo_redo.h
+++ b/core/object/undo_redo.h
@@ -32,7 +32,7 @@
#define UNDO_REDO_H
#include "core/object/class_db.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
class UndoRedo : public Object {
GDCLASS(UndoRedo, Object);
@@ -61,7 +61,7 @@ private:
};
Type type;
- Ref<Reference> ref;
+ Ref<RefCounted> ref;
ObjectID object;
StringName name;
Variant args[VARIANT_ARG_MAX];
@@ -121,8 +121,8 @@ public:
String get_action_name(int p_id);
void clear_history(bool p_increase_version = true);
- bool has_undo();
- bool has_redo();
+ bool has_undo() const;
+ bool has_redo() const;
uint64_t get_version() const;
diff --git a/core/os/keyboard.h b/core/os/keyboard.h
index 33f9213c4e..52174432d9 100644
--- a/core/os/keyboard.h
+++ b/core/os/keyboard.h
@@ -46,6 +46,7 @@ enum {
};
enum Key {
+ KEY_NONE = 0,
/* CURSOR/FUNCTION/BROWSER/MULTIMEDIA/MISC KEYS */
KEY_ESCAPE = SPKEY | 0x01,
KEY_TAB = SPKEY | 0x02,
@@ -314,6 +315,52 @@ enum KeyModifierMask {
// bit 31 can't be used because variant uses regular 32 bits int as datatype
};
+// To avoid having unnecessary operators, only define the ones that are needed.
+
+inline Key operator-(uint32_t a, Key b) {
+ return (Key)(a - (uint32_t)b);
+}
+
+inline Key &operator-=(Key &a, int b) {
+ return (Key &)((int &)a -= b);
+}
+
+inline Key operator+(Key a, Key b) {
+ return (Key)((int)a - (int)b);
+}
+
+inline Key &operator|=(Key &a, Key b) {
+ return (Key &)((int &)a |= (int)b);
+}
+
+inline Key &operator|=(Key &a, KeyModifierMask b) {
+ return (Key &)((int &)a |= (int)b);
+}
+
+inline Key operator|(Key a, KeyModifierMask b) {
+ return (Key)((int)a | (int)b);
+}
+
+inline Key operator&(Key a, KeyModifierMask b) {
+ return (Key)((int)a & (int)b);
+}
+
+inline Key operator+(KeyModifierMask a, Key b) {
+ return (Key)((int)a + (int)b);
+}
+
+inline Key operator|(KeyModifierMask a, Key b) {
+ return (Key)((int)a | (int)b);
+}
+
+inline KeyModifierMask operator+(KeyModifierMask a, KeyModifierMask b) {
+ return (KeyModifierMask)((int)a + (int)b);
+}
+
+inline KeyModifierMask operator|(KeyModifierMask a, KeyModifierMask b) {
+ return (KeyModifierMask)((int)a | (int)b);
+}
+
String keycode_get_string(uint32_t p_code);
bool keycode_has_unicode(uint32_t p_keycode);
int find_keycode(const String &p_code);
diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp
index 016d9d0a09..0ba69a8d47 100644
--- a/core/os/main_loop.cpp
+++ b/core/os/main_loop.cpp
@@ -33,11 +33,6 @@
#include "core/object/script_language.h"
void MainLoop::_bind_methods() {
- BIND_VMETHOD(MethodInfo("_initialize"));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_physics_process", PropertyInfo(Variant::FLOAT, "delta")));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_process", PropertyInfo(Variant::FLOAT, "delta")));
- BIND_VMETHOD(MethodInfo("_finalize"));
-
BIND_CONSTANT(NOTIFICATION_OS_MEMORY_WARNING);
BIND_CONSTANT(NOTIFICATION_TRANSLATION_CHANGED);
BIND_CONSTANT(NOTIFICATION_WM_ABOUT);
@@ -50,7 +45,12 @@ void MainLoop::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_TEXT_SERVER_CHANGED);
ADD_SIGNAL(MethodInfo("on_request_permissions_result", PropertyInfo(Variant::STRING, "permission"), PropertyInfo(Variant::BOOL, "granted")));
-};
+
+ GDVIRTUAL_BIND(_initialize);
+ GDVIRTUAL_BIND(_physics_process, "delta");
+ GDVIRTUAL_BIND(_process, "delta");
+ GDVIRTUAL_BIND(_finalize);
+}
void MainLoop::set_initialize_script(const Ref<Script> &p_initialize_script) {
initialize_script = p_initialize_script;
@@ -61,30 +61,31 @@ void MainLoop::initialize() {
set_script(initialize_script);
}
- if (get_script_instance()) {
- get_script_instance()->call("_initialize");
- }
+ GDVIRTUAL_CALL(_initialize);
}
-bool MainLoop::physics_process(float p_time) {
- if (get_script_instance()) {
- return get_script_instance()->call("_physics_process", p_time);
+bool MainLoop::physics_process(double p_time) {
+ bool quit;
+ if (GDVIRTUAL_CALL(_physics_process, p_time, quit)) {
+ return quit;
}
return false;
}
-bool MainLoop::process(float p_time) {
- if (get_script_instance()) {
- return get_script_instance()->call("_process", p_time);
+bool MainLoop::process(double p_time) {
+ bool quit;
+ if (GDVIRTUAL_CALL(_process, p_time, quit)) {
+ return quit;
}
return false;
}
void MainLoop::finalize() {
+ GDVIRTUAL_CALL(_finalize);
+
if (get_script_instance()) {
- get_script_instance()->call("_finalize");
set_script(Variant()); //clear script
}
}
diff --git a/core/os/main_loop.h b/core/os/main_loop.h
index 25a09fe98f..4da01d767e 100644
--- a/core/os/main_loop.h
+++ b/core/os/main_loop.h
@@ -32,7 +32,8 @@
#define MAIN_LOOP_H
#include "core/input/input_event.h"
-#include "core/object/reference.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/ref_counted.h"
#include "core/object/script_language.h"
class MainLoop : public Object {
@@ -44,6 +45,11 @@ class MainLoop : public Object {
protected:
static void _bind_methods();
+ GDVIRTUAL0(_initialize)
+ GDVIRTUAL1R(bool, _physics_process, double)
+ GDVIRTUAL1R(bool, _process, double)
+ GDVIRTUAL0(_finalize)
+
public:
enum {
//make sure these are replicated in Node
@@ -60,8 +66,8 @@ public:
};
virtual void initialize();
- virtual bool physics_process(float p_time);
- virtual bool process(float p_time);
+ virtual bool physics_process(double p_time);
+ virtual bool process(double p_time);
virtual void finalize();
void set_initialize_script(const Ref<Script> &p_initialize_script);
diff --git a/core/os/memory.h b/core/os/memory.h
index 10e678103d..f67384a17e 100644
--- a/core/os/memory.h
+++ b/core/os/memory.h
@@ -35,6 +35,7 @@
#include "core/templates/safe_refcount.h"
#include <stddef.h>
+#include <new>
#ifndef PAD_ALIGN
#define PAD_ALIGN 16 //must always be greater than this at much
@@ -80,7 +81,7 @@ void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_d
#define memalloc(m_size) Memory::alloc_static(m_size)
#define memrealloc(m_mem, m_size) Memory::realloc_static(m_mem, m_size)
-#define memfree(m_size) Memory::free_static(m_size)
+#define memfree(m_mem) Memory::free_static(m_mem)
_ALWAYS_INLINE_ void postinitialize_handler(void *) {}
@@ -92,15 +93,8 @@ _ALWAYS_INLINE_ T *_post_initialize(T *p_obj) {
#define memnew(m_class) _post_initialize(new ("") m_class)
-_ALWAYS_INLINE_ void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description) {
- //void *failptr=0;
- //ERR_FAIL_COND_V( check < p_size , failptr); /** bug, or strange compiler, most likely */
-
- return p_pointer;
-}
-
#define memnew_allocator(m_class, m_allocator) _post_initialize(new (m_allocator::alloc) m_class)
-#define memnew_placement(m_placement, m_class) _post_initialize(new (m_placement, sizeof(m_class), "") m_class)
+#define memnew_placement(m_placement, m_class) _post_initialize(new (m_placement) m_class)
_ALWAYS_INLINE_ bool predelete_handler(void *) {
return true;
@@ -140,7 +134,7 @@ void memdelete_allocator(T *p_class) {
#define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count)
template <typename T>
-T *memnew_arr_template(size_t p_elements, const char *p_descr = "") {
+T *memnew_arr_template(size_t p_elements) {
if (p_elements == 0) {
return nullptr;
}
@@ -158,7 +152,7 @@ T *memnew_arr_template(size_t p_elements, const char *p_descr = "") {
/* call operator new */
for (size_t i = 0; i < p_elements; i++) {
- new (&elems[i], sizeof(T), p_descr) T;
+ new (&elems[i]) T;
}
}
diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp
index a8be84c56c..ee87346dfc 100644
--- a/core/os/midi_driver.cpp
+++ b/core/os/midi_driver.cpp
@@ -45,23 +45,23 @@ void MIDIDriver::set_singleton() {
void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length) {
Ref<InputEventMIDI> event;
- event.instance();
+ event.instantiate();
uint32_t param_position = 1;
if (length >= 1) {
if (data[0] >= 0xF0) {
// channel does not apply to system common messages
event->set_channel(0);
- event->set_message(data[0]);
+ event->set_message(MIDIMessage(data[0]));
last_received_message = data[0];
} else if ((data[0] & 0x80) == 0x00) {
// running status
event->set_channel(last_received_message & 0xF);
- event->set_message(last_received_message >> 4);
+ event->set_message(MIDIMessage(last_received_message >> 4));
param_position = 0;
} else {
event->set_channel(data[0] & 0xF);
- event->set_message(data[0] >> 4);
+ event->set_message(MIDIMessage(data[0] >> 4));
param_position = 1;
last_received_message = data[0];
}
@@ -112,6 +112,8 @@ void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_
event->set_pressure(data[param_position]);
}
break;
+ default:
+ break;
}
Input *id = Input::get_singleton();
diff --git a/core/os/os.cpp b/core/os/os.cpp
index ca1b798e11..dc3fe29dca 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -32,8 +32,8 @@
#include "core/config/project_settings.h"
#include "core/input/input.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/os/midi_driver.h"
#include "core/version_generated.gen.h"
#include "servers/audio_server.h"
@@ -47,37 +47,8 @@ OS *OS::get_singleton() {
return singleton;
}
-uint32_t OS::get_ticks_msec() const {
- return get_ticks_usec() / 1000;
-}
-
-String OS::get_iso_date_time(bool local) const {
- OS::Date date = get_date(local);
- OS::Time time = get_time(local);
-
- String timezone;
- if (!local) {
- TimeZoneInfo zone = get_time_zone_info();
- if (zone.bias >= 0) {
- timezone = "+";
- }
- timezone = timezone + itos(zone.bias / 60).pad_zeros(2) + itos(zone.bias % 60).pad_zeros(2);
- } else {
- timezone = "Z";
- }
-
- return itos(date.year).pad_zeros(2) +
- "-" +
- itos(date.month).pad_zeros(2) +
- "-" +
- itos(date.day).pad_zeros(2) +
- "T" +
- itos(time.hour).pad_zeros(2) +
- ":" +
- itos(time.min).pad_zeros(2) +
- ":" +
- itos(time.sec).pad_zeros(2) +
- timezone;
+uint64_t OS::get_ticks_msec() const {
+ return get_ticks_usec() / 1000ULL;
}
double OS::get_unix_time() const {
@@ -139,6 +110,10 @@ void OS::printerr(const char *p_format, ...) {
va_end(argp);
}
+void OS::alert(const String &p_alert, const String &p_title) {
+ fprintf(stderr, "%s: %s\n", p_title.utf8().get_data(), p_alert.utf8().get_data());
+}
+
void OS::set_low_processor_usage_mode(bool p_enabled) {
low_processor_usage_mode = p_enabled;
}
@@ -171,6 +146,10 @@ bool OS::is_stdout_verbose() const {
return _verbose_stdout;
}
+bool OS::is_single_window() const {
+ return _single_window;
+}
+
bool OS::is_stdout_debug_enabled() const {
return _debug_stdout;
}
@@ -203,7 +182,7 @@ static void _OS_printres(Object *p_obj) {
return;
}
- String str = itos(res->get_instance_id()) + String(res->get_class()) + ":" + String(res->get_name()) + " - " + res->get_path();
+ String str = vformat("%s - %s - %s", res->to_string(), res->get_name(), res->get_path());
if (_OSPRF) {
_OSPRF->store_line(str);
} else {
@@ -240,14 +219,6 @@ void OS::dump_resources_to_file(const char *p_file) {
ResourceCache::dump(p_file);
}
-void OS::set_no_window_mode(bool p_enable) {
- _no_window = p_enable;
-}
-
-bool OS::is_no_window_mode_enabled() const {
- return _no_window;
-}
-
int OS::get_exit_code() const {
return _exit_code;
}
@@ -316,7 +287,7 @@ String OS::get_resource_dir() const {
}
// Access system-specific dirs like Documents, Downloads, etc.
-String OS::get_system_dir(SystemDir p_dir) const {
+String OS::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {
return ".";
}
@@ -390,9 +361,17 @@ void OS::set_has_server_feature_callback(HasServerFeatureCallback p_callback) {
}
bool OS::has_feature(const String &p_feature) {
- if (p_feature == get_name()) {
+ // Feature tags are always lowercase for consistency.
+ if (p_feature == get_name().to_lower()) {
return true;
}
+
+ // Catch-all `linuxbsd` feature tag that matches on both Linux and BSD.
+ // This is the one exposed in the project settings dialog.
+ if (p_feature == "linuxbsd" && (get_name() == "Linux" || get_name() == "FreeBSD" || get_name() == "NetBSD" || get_name() == "OpenBSD" || get_name() == "BSD")) {
+ return true;
+ }
+
#ifdef DEBUG_ENABLED
if (p_feature == "debug") {
return true;
diff --git a/core/os/os.h b/core/os/os.h
index 7198607237..f585483300 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -52,14 +52,12 @@ class OS {
int low_processor_usage_mode_sleep_usec = 10000;
bool _verbose_stdout = false;
bool _debug_stdout = false;
+ bool _single_window = false;
String _local_clipboard;
- bool _no_window = false;
int _exit_code = EXIT_FAILURE; // unexpected exit is marked as failure
int _orientation;
bool _allow_hidpi = false;
bool _allow_layered = false;
- bool _use_vsync;
- bool _vsync_via_compositor;
bool _stdout_enabled = true;
bool _stderr_enabled = true;
@@ -122,6 +120,8 @@ public:
virtual void open_midi_inputs();
virtual void close_midi_inputs();
+ virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
+
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) { return ERR_UNAVAILABLE; }
virtual Error close_dynamic_library(void *p_library_handle) { return ERR_UNAVAILABLE; }
virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false) { return ERR_UNAVAILABLE; }
@@ -158,17 +158,17 @@ public:
virtual void yield();
- enum Weekday {
- DAY_SUNDAY,
- DAY_MONDAY,
- DAY_TUESDAY,
- DAY_WEDNESDAY,
- DAY_THURSDAY,
- DAY_FRIDAY,
- DAY_SATURDAY
+ enum Weekday : uint8_t {
+ WEEKDAY_SUNDAY,
+ WEEKDAY_MONDAY,
+ WEEKDAY_TUESDAY,
+ WEEKDAY_WEDNESDAY,
+ WEEKDAY_THURSDAY,
+ WEEKDAY_FRIDAY,
+ WEEKDAY_SATURDAY,
};
- enum Month {
+ enum Month : uint8_t {
/// Start at 1 to follow Windows SYSTEMTIME structure
/// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx
MONTH_JANUARY = 1,
@@ -182,21 +182,21 @@ public:
MONTH_SEPTEMBER,
MONTH_OCTOBER,
MONTH_NOVEMBER,
- MONTH_DECEMBER
+ MONTH_DECEMBER,
};
struct Date {
- int year;
+ int64_t year;
Month month;
- int day;
+ uint8_t day;
Weekday weekday;
bool dst;
};
struct Time {
- int hour;
- int min;
- int sec;
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
};
struct TimeZoneInfo {
@@ -207,14 +207,13 @@ public:
virtual Date get_date(bool local = false) const = 0;
virtual Time get_time(bool local = false) const = 0;
virtual TimeZoneInfo get_time_zone_info() const = 0;
- virtual String get_iso_date_time(bool local = false) const;
virtual double get_unix_time() const;
virtual void delay_usec(uint32_t p_usec) const = 0;
virtual void add_frame_delay(bool p_can_draw);
virtual uint64_t get_ticks_usec() const = 0;
- uint32_t get_ticks_msec() const;
+ uint64_t get_ticks_msec() const;
virtual bool is_userfs_persistent() const { return true; }
@@ -226,6 +225,8 @@ public:
void set_stdout_enabled(bool p_enabled);
void set_stderr_enabled(bool p_enabled);
+ bool is_single_window() const;
+
virtual void disable_crash_handler() {}
virtual bool is_disable_crash_handler() const { return false; }
virtual void initialize_debugging() {}
@@ -265,13 +266,10 @@ public:
SYSTEM_DIR_RINGTONES,
};
- virtual String get_system_dir(SystemDir p_dir) const;
+ virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const;
virtual Error move_to_trash(const String &p_path) { return FAILED; }
- virtual void set_no_window_mode(bool p_enable);
- virtual bool is_no_window_mode_enabled() const;
-
virtual void debug_break();
virtual int get_exit_code() const;
diff --git a/core/os/pool_allocator.h b/core/os/pool_allocator.h
index 15e50dac90..49f433ba97 100644
--- a/core/os/pool_allocator.h
+++ b/core/os/pool_allocator.h
@@ -37,7 +37,7 @@
@author Juan Linietsky <reduzio@gmail.com>
* Generic Pool Allocator.
* This is a generic memory pool allocator, with locking, compacting and alignment. (@TODO alignment)
- * It used as a standard way to manage alloction in a specific region of memory, such as texture memory,
+ * It used as a standard way to manage allocation in a specific region of memory, such as texture memory,
* audio sample memory, or just any kind of memory overall.
* (@TODO) abstraction should be greater, because in many platforms, you need to manage a nonreachable memory.
*/
diff --git a/core/os/time.cpp b/core/os/time.cpp
new file mode 100644
index 0000000000..a185029969
--- /dev/null
+++ b/core/os/time.cpp
@@ -0,0 +1,433 @@
+/*************************************************************************/
+/* time.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "time.h"
+
+#include "core/os/os.h"
+
+#define UNIX_EPOCH_YEAR_AD 1970 // 1970
+#define SECONDS_PER_DAY (24 * 60 * 60) // 86400
+#define IS_LEAP_YEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400)))
+#define YEAR_SIZE(year) (IS_LEAP_YEAR(year) ? 366 : 365)
+
+#define YEAR_KEY "year"
+#define MONTH_KEY "month"
+#define DAY_KEY "day"
+#define WEEKDAY_KEY "weekday"
+#define HOUR_KEY "hour"
+#define MINUTE_KEY "minute"
+#define SECOND_KEY "second"
+#define DST_KEY "dst"
+
+// Table of number of days in each month (for regular year and leap year).
+static const uint8_t MONTH_DAYS_TABLE[2][12] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+VARIANT_ENUM_CAST(Time::Month);
+VARIANT_ENUM_CAST(Time::Weekday);
+
+#define UNIX_TIME_TO_HMS \
+ uint8_t hour, minute, second; \
+ { \
+ /* The time of the day (in seconds since start of day). */ \
+ uint32_t day_clock = Math::posmod(p_unix_time_val, SECONDS_PER_DAY); \
+ /* On x86 these 4 lines can be optimized to only 2 divisions. */ \
+ second = day_clock % 60; \
+ day_clock /= 60; \
+ minute = day_clock % 60; \
+ hour = day_clock / 60; \
+ }
+
+#define UNIX_TIME_TO_YMD \
+ int64_t year; \
+ Month month; \
+ uint8_t day; \
+ /* The day number since Unix epoch (0-index). Days before 1970 are negative. */ \
+ int64_t day_number = Math::floor(p_unix_time_val / (double)SECONDS_PER_DAY); \
+ { \
+ int64_t day_number_copy = day_number; \
+ year = UNIX_EPOCH_YEAR_AD; \
+ uint8_t month_zero_index = 0; \
+ while (day_number_copy >= YEAR_SIZE(year)) { \
+ day_number_copy -= YEAR_SIZE(year); \
+ year++; \
+ } \
+ while (day_number_copy < 0) { \
+ year--; \
+ day_number_copy += YEAR_SIZE(year); \
+ } \
+ /* After the above, day_number now represents the day of the year (0-index). */ \
+ while (day_number_copy >= MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][month_zero_index]) { \
+ day_number_copy -= MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][month_zero_index]; \
+ month_zero_index++; \
+ } \
+ /* After the above, day_number now represents the day of the month (0-index). */ \
+ month = (Month)(month_zero_index + 1); \
+ day = day_number_copy + 1; \
+ }
+
+#define VALIDATE_YMDHMS \
+ ERR_FAIL_COND_V_MSG(month == 0, 0, "Invalid month value of: " + itos(month) + ", months are 1-indexed and cannot be 0. See the Time.Month enum for valid values."); \
+ ERR_FAIL_COND_V_MSG(month > 12, 0, "Invalid month value of: " + itos(month) + ". See the Time.Month enum for valid values."); \
+ ERR_FAIL_COND_V_MSG(hour > 23, 0, "Invalid hour value of: " + itos(hour) + "."); \
+ ERR_FAIL_COND_V_MSG(minute > 59, 0, "Invalid minute value of: " + itos(minute) + "."); \
+ ERR_FAIL_COND_V_MSG(second > 59, 0, "Invalid second value of: " + itos(second) + " (leap seconds are not supported)."); \
+ /* Do this check after month is tested as valid. */ \
+ ERR_FAIL_COND_V_MSG(day == 0, 0, "Invalid day value of: " + itos(month) + ", days are 1-indexed and cannot be 0."); \
+ uint8_t days_in_this_month = MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][month - 1]; \
+ ERR_FAIL_COND_V_MSG(day > days_in_this_month, 0, "Invalid day value of: " + itos(day) + " which is larger than the maximum for this month, " + itos(days_in_this_month) + ".");
+
+#define YMD_TO_DAY_NUMBER \
+ /* The day number since Unix epoch (0-index). Days before 1970 are negative. */ \
+ int64_t day_number = day - 1; \
+ /* Add the days in the months to day_number. */ \
+ for (int i = 0; i < month - 1; i++) { \
+ day_number += MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][i]; \
+ } \
+ /* Add the days in the years to day_number. */ \
+ if (year >= UNIX_EPOCH_YEAR_AD) { \
+ for (int64_t iyear = UNIX_EPOCH_YEAR_AD; iyear < year; iyear++) { \
+ day_number += YEAR_SIZE(iyear); \
+ } \
+ } else { \
+ for (int64_t iyear = UNIX_EPOCH_YEAR_AD - 1; iyear >= year; iyear--) { \
+ day_number -= YEAR_SIZE(iyear); \
+ } \
+ }
+
+#define PARSE_ISO8601_STRING \
+ int64_t year = UNIX_EPOCH_YEAR_AD; \
+ Month month = MONTH_JANUARY; \
+ uint8_t day = 1; \
+ uint8_t hour = 0; \
+ uint8_t minute = 0; \
+ uint8_t second = 0; \
+ { \
+ bool has_date = false, has_time = false; \
+ String date, time; \
+ if (p_datetime.find_char('T') > 0) { \
+ has_date = has_time = true; \
+ PackedStringArray array = p_datetime.split("T"); \
+ date = array[0]; \
+ time = array[1]; \
+ } else if (p_datetime.find_char(' ') > 0) { \
+ has_date = has_time = true; \
+ PackedStringArray array = p_datetime.split(" "); \
+ date = array[0]; \
+ time = array[1]; \
+ } else if (p_datetime.find_char('-', 1) > 0) { \
+ has_date = true; \
+ date = p_datetime; \
+ } else if (p_datetime.find_char(':') > 0) { \
+ has_time = true; \
+ time = p_datetime; \
+ } \
+ /* Set the variables from the contents of the string. */ \
+ if (has_date) { \
+ PackedInt32Array array = date.split_ints("-", false); \
+ year = array[0]; \
+ month = (Month)array[1]; \
+ day = array[2]; \
+ /* Handle negative years. */ \
+ if (p_datetime.find_char('-') == 0) { \
+ year *= -1; \
+ } \
+ } \
+ if (has_time) { \
+ PackedInt32Array array = time.split_ints(":", false); \
+ hour = array[0]; \
+ minute = array[1]; \
+ second = array[2]; \
+ } \
+ }
+
+#define EXTRACT_FROM_DICTIONARY \
+ /* Get all time values from the dictionary. If it doesn't exist, set the */ \
+ /* values to the default values for Unix epoch (1970-01-01 00:00:00). */ \
+ int64_t year = p_datetime.has(YEAR_KEY) ? int64_t(p_datetime[YEAR_KEY]) : UNIX_EPOCH_YEAR_AD; \
+ Month month = Month((p_datetime.has(MONTH_KEY)) ? uint8_t(p_datetime[MONTH_KEY]) : 1); \
+ uint8_t day = p_datetime.has(DAY_KEY) ? uint8_t(p_datetime[DAY_KEY]) : 1; \
+ uint8_t hour = p_datetime.has(HOUR_KEY) ? uint8_t(p_datetime[HOUR_KEY]) : 0; \
+ uint8_t minute = p_datetime.has(MINUTE_KEY) ? uint8_t(p_datetime[MINUTE_KEY]) : 0; \
+ uint8_t second = p_datetime.has(SECOND_KEY) ? uint8_t(p_datetime[SECOND_KEY]) : 0;
+
+Time *Time::singleton = nullptr;
+
+Time *Time::get_singleton() {
+ if (!singleton) {
+ memnew(Time);
+ }
+ return singleton;
+}
+
+Dictionary Time::get_datetime_dict_from_unix_time(int64_t p_unix_time_val) const {
+ UNIX_TIME_TO_HMS
+ UNIX_TIME_TO_YMD
+ Dictionary datetime;
+ datetime[YEAR_KEY] = year;
+ datetime[MONTH_KEY] = (uint8_t)month;
+ datetime[DAY_KEY] = day;
+ // Unix epoch was a Thursday (day 0 aka 1970-01-01).
+ datetime[WEEKDAY_KEY] = Math::posmod(day_number + WEEKDAY_THURSDAY, 7);
+ datetime[HOUR_KEY] = hour;
+ datetime[MINUTE_KEY] = minute;
+ datetime[SECOND_KEY] = second;
+
+ return datetime;
+}
+
+Dictionary Time::get_date_dict_from_unix_time(int64_t p_unix_time_val) const {
+ UNIX_TIME_TO_YMD
+ Dictionary datetime;
+ datetime[YEAR_KEY] = year;
+ datetime[MONTH_KEY] = (uint8_t)month;
+ datetime[DAY_KEY] = day;
+ // Unix epoch was a Thursday (day 0 aka 1970-01-01).
+ datetime[WEEKDAY_KEY] = Math::posmod(day_number + WEEKDAY_THURSDAY, 7);
+
+ return datetime;
+}
+
+Dictionary Time::get_time_dict_from_unix_time(int64_t p_unix_time_val) const {
+ UNIX_TIME_TO_HMS
+ Dictionary datetime;
+ datetime[HOUR_KEY] = hour;
+ datetime[MINUTE_KEY] = minute;
+ datetime[SECOND_KEY] = second;
+
+ return datetime;
+}
+
+String Time::get_datetime_string_from_unix_time(int64_t p_unix_time_val, bool p_use_space) const {
+ UNIX_TIME_TO_HMS
+ UNIX_TIME_TO_YMD
+ // vformat only supports up to 6 arguments, so we need to split this up into 2 parts.
+ String timestamp = vformat("%04d-%02d-%02d", year, (uint8_t)month, day);
+ if (p_use_space) {
+ timestamp = vformat("%s %02d:%02d:%02d", timestamp, hour, minute, second);
+ } else {
+ timestamp = vformat("%sT%02d:%02d:%02d", timestamp, hour, minute, second);
+ }
+
+ return timestamp;
+}
+
+String Time::get_date_string_from_unix_time(int64_t p_unix_time_val) const {
+ UNIX_TIME_TO_YMD
+ // Android is picky about the types passed to make Variant, so we need a cast.
+ return vformat("%04d-%02d-%02d", year, (uint8_t)month, day);
+}
+
+String Time::get_time_string_from_unix_time(int64_t p_unix_time_val) const {
+ UNIX_TIME_TO_HMS
+ return vformat("%02d:%02d:%02d", hour, minute, second);
+}
+
+Dictionary Time::get_datetime_dict_from_string(String p_datetime, bool p_weekday) const {
+ PARSE_ISO8601_STRING
+ Dictionary dict;
+ dict[YEAR_KEY] = year;
+ dict[MONTH_KEY] = (uint8_t)month;
+ dict[DAY_KEY] = day;
+ if (p_weekday) {
+ YMD_TO_DAY_NUMBER
+ // Unix epoch was a Thursday (day 0 aka 1970-01-01).
+ dict[WEEKDAY_KEY] = Math::posmod(day_number + WEEKDAY_THURSDAY, 7);
+ }
+ dict[HOUR_KEY] = hour;
+ dict[MINUTE_KEY] = minute;
+ dict[SECOND_KEY] = second;
+
+ return dict;
+}
+
+String Time::get_datetime_string_from_dict(Dictionary p_datetime, bool p_use_space) const {
+ ERR_FAIL_COND_V_MSG(p_datetime.is_empty(), "", "Invalid datetime Dictionary: Dictionary is empty.");
+ EXTRACT_FROM_DICTIONARY
+ // vformat only supports up to 6 arguments, so we need to split this up into 2 parts.
+ String timestamp = vformat("%04d-%02d-%02d", year, (uint8_t)month, day);
+ if (p_use_space) {
+ timestamp = vformat("%s %02d:%02d:%02d", timestamp, hour, minute, second);
+ } else {
+ timestamp = vformat("%sT%02d:%02d:%02d", timestamp, hour, minute, second);
+ }
+ return timestamp;
+}
+
+int64_t Time::get_unix_time_from_datetime_dict(Dictionary p_datetime) const {
+ ERR_FAIL_COND_V_MSG(p_datetime.is_empty(), 0, "Invalid datetime Dictionary: Dictionary is empty");
+ EXTRACT_FROM_DICTIONARY
+ VALIDATE_YMDHMS
+ YMD_TO_DAY_NUMBER
+ return day_number * SECONDS_PER_DAY + hour * 3600 + minute * 60 + second;
+}
+
+int64_t Time::get_unix_time_from_datetime_string(String p_datetime) const {
+ PARSE_ISO8601_STRING
+ VALIDATE_YMDHMS
+ YMD_TO_DAY_NUMBER
+ return day_number * SECONDS_PER_DAY + hour * 3600 + minute * 60 + second;
+}
+
+Dictionary Time::get_datetime_dict_from_system(bool p_utc) const {
+ OS::Date date = OS::get_singleton()->get_date(p_utc);
+ OS::Time time = OS::get_singleton()->get_time(p_utc);
+ Dictionary datetime;
+ datetime[YEAR_KEY] = date.year;
+ datetime[MONTH_KEY] = (uint8_t)date.month;
+ datetime[DAY_KEY] = date.day;
+ datetime[WEEKDAY_KEY] = (uint8_t)date.weekday;
+ datetime[DST_KEY] = date.dst;
+ datetime[HOUR_KEY] = time.hour;
+ datetime[MINUTE_KEY] = time.minute;
+ datetime[SECOND_KEY] = time.second;
+ return datetime;
+}
+
+Dictionary Time::get_date_dict_from_system(bool p_utc) const {
+ OS::Date date = OS::get_singleton()->get_date(p_utc);
+ Dictionary date_dictionary;
+ date_dictionary[YEAR_KEY] = date.year;
+ date_dictionary[MONTH_KEY] = (uint8_t)date.month;
+ date_dictionary[DAY_KEY] = date.day;
+ date_dictionary[WEEKDAY_KEY] = (uint8_t)date.weekday;
+ date_dictionary[DST_KEY] = date.dst;
+ return date_dictionary;
+}
+
+Dictionary Time::get_time_dict_from_system(bool p_utc) const {
+ OS::Time time = OS::get_singleton()->get_time(p_utc);
+ Dictionary time_dictionary;
+ time_dictionary[HOUR_KEY] = time.hour;
+ time_dictionary[MINUTE_KEY] = time.minute;
+ time_dictionary[SECOND_KEY] = time.second;
+ return time_dictionary;
+}
+
+String Time::get_datetime_string_from_system(bool p_utc, bool p_use_space) const {
+ OS::Date date = OS::get_singleton()->get_date(p_utc);
+ OS::Time time = OS::get_singleton()->get_time(p_utc);
+ // vformat only supports up to 6 arguments, so we need to split this up into 2 parts.
+ String timestamp = vformat("%04d-%02d-%02d", date.year, (uint8_t)date.month, date.day);
+ if (p_use_space) {
+ timestamp = vformat("%s %02d:%02d:%02d", timestamp, time.hour, time.minute, time.second);
+ } else {
+ timestamp = vformat("%sT%02d:%02d:%02d", timestamp, time.hour, time.minute, time.second);
+ }
+
+ return timestamp;
+}
+
+String Time::get_date_string_from_system(bool p_utc) const {
+ OS::Date date = OS::get_singleton()->get_date(p_utc);
+ // Android is picky about the types passed to make Variant, so we need a cast.
+ return vformat("%04d-%02d-%02d", date.year, (uint8_t)date.month, date.day);
+}
+
+String Time::get_time_string_from_system(bool p_utc) const {
+ OS::Time time = OS::get_singleton()->get_time(p_utc);
+ return vformat("%02d:%02d:%02d", time.hour, time.minute, time.second);
+}
+
+Dictionary Time::get_time_zone_from_system() const {
+ OS::TimeZoneInfo info = OS::get_singleton()->get_time_zone_info();
+ Dictionary timezone;
+ timezone["bias"] = info.bias;
+ timezone["name"] = info.name;
+ return timezone;
+}
+
+double Time::get_unix_time_from_system() const {
+ return OS::get_singleton()->get_unix_time();
+}
+
+uint64_t Time::get_ticks_msec() const {
+ return OS::get_singleton()->get_ticks_msec();
+}
+
+uint64_t Time::get_ticks_usec() const {
+ return OS::get_singleton()->get_ticks_usec();
+}
+
+void Time::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_datetime_dict_from_unix_time", "unix_time_val"), &Time::get_datetime_dict_from_unix_time);
+ ClassDB::bind_method(D_METHOD("get_date_dict_from_unix_time", "unix_time_val"), &Time::get_date_dict_from_unix_time);
+ ClassDB::bind_method(D_METHOD("get_time_dict_from_unix_time", "unix_time_val"), &Time::get_time_dict_from_unix_time);
+ ClassDB::bind_method(D_METHOD("get_datetime_string_from_unix_time", "unix_time_val", "use_space"), &Time::get_datetime_string_from_unix_time, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_date_string_from_unix_time", "unix_time_val"), &Time::get_date_string_from_unix_time);
+ ClassDB::bind_method(D_METHOD("get_time_string_from_unix_time", "unix_time_val"), &Time::get_time_string_from_unix_time);
+ ClassDB::bind_method(D_METHOD("get_datetime_dict_from_string", "datetime", "weekday"), &Time::get_datetime_dict_from_string);
+ ClassDB::bind_method(D_METHOD("get_datetime_string_from_dict", "datetime", "use_space"), &Time::get_datetime_string_from_dict);
+ ClassDB::bind_method(D_METHOD("get_unix_time_from_datetime_dict", "datetime"), &Time::get_unix_time_from_datetime_dict);
+ ClassDB::bind_method(D_METHOD("get_unix_time_from_datetime_string", "datetime"), &Time::get_unix_time_from_datetime_string);
+
+ ClassDB::bind_method(D_METHOD("get_datetime_dict_from_system", "utc"), &Time::get_datetime_dict_from_system, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_date_dict_from_system", "utc"), &Time::get_date_dict_from_system, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_time_dict_from_system", "utc"), &Time::get_time_dict_from_system, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_datetime_string_from_system", "utc", "use_space"), &Time::get_datetime_string_from_system, DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_date_string_from_system", "utc"), &Time::get_date_string_from_system, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_time_string_from_system", "utc"), &Time::get_time_string_from_system, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_time_zone_from_system"), &Time::get_time_zone_from_system);
+ ClassDB::bind_method(D_METHOD("get_unix_time_from_system"), &Time::get_unix_time_from_system);
+ ClassDB::bind_method(D_METHOD("get_ticks_msec"), &Time::get_ticks_msec);
+ ClassDB::bind_method(D_METHOD("get_ticks_usec"), &Time::get_ticks_usec);
+
+ BIND_ENUM_CONSTANT(MONTH_JANUARY);
+ BIND_ENUM_CONSTANT(MONTH_FEBRUARY);
+ BIND_ENUM_CONSTANT(MONTH_MARCH);
+ BIND_ENUM_CONSTANT(MONTH_APRIL);
+ BIND_ENUM_CONSTANT(MONTH_MAY);
+ BIND_ENUM_CONSTANT(MONTH_JUNE);
+ BIND_ENUM_CONSTANT(MONTH_JULY);
+ BIND_ENUM_CONSTANT(MONTH_AUGUST);
+ BIND_ENUM_CONSTANT(MONTH_SEPTEMBER);
+ BIND_ENUM_CONSTANT(MONTH_OCTOBER);
+ BIND_ENUM_CONSTANT(MONTH_NOVEMBER);
+ BIND_ENUM_CONSTANT(MONTH_DECEMBER);
+
+ BIND_ENUM_CONSTANT(WEEKDAY_SUNDAY);
+ BIND_ENUM_CONSTANT(WEEKDAY_MONDAY);
+ BIND_ENUM_CONSTANT(WEEKDAY_TUESDAY);
+ BIND_ENUM_CONSTANT(WEEKDAY_WEDNESDAY);
+ BIND_ENUM_CONSTANT(WEEKDAY_THURSDAY);
+ BIND_ENUM_CONSTANT(WEEKDAY_FRIDAY);
+ BIND_ENUM_CONSTANT(WEEKDAY_SATURDAY);
+}
+
+Time::Time() {
+ ERR_FAIL_COND_MSG(singleton, "Singleton for Time already exists.");
+ singleton = this;
+}
+
+Time::~Time() {
+ singleton = nullptr;
+}
diff --git a/core/os/time.h b/core/os/time.h
new file mode 100644
index 0000000000..4325f93d56
--- /dev/null
+++ b/core/os/time.h
@@ -0,0 +1,109 @@
+/*************************************************************************/
+/* time.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TIME_H
+#define TIME_H
+
+#include "core/object/class_db.h"
+
+// This Time class conforms with as many of the ISO 8601 standards as possible.
+// * As per ISO 8601:2004 4.3.2.1, all dates follow the Proleptic Gregorian
+// calendar. As such, the day before 1582-10-15 is 1582-10-14, not 1582-10-04.
+// See: https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
+// * As per ISO 8601:2004 3.4.2 and 4.1.2.4, the year before 1 AD (aka 1 BC)
+// is number "0", with the year before that (2 BC) being "-1", etc.
+// Conversion methods assume "the same timezone", and do not handle DST.
+// Leap seconds are not handled, they must be done manually if desired.
+// Suffixes such as "Z" are not handled, you need to strip them away manually.
+
+class Time : public Object {
+ GDCLASS(Time, Object);
+ static void _bind_methods();
+ static Time *singleton;
+
+public:
+ static Time *get_singleton();
+
+ enum Month : uint8_t {
+ /// Start at 1 to follow Windows SYSTEMTIME structure
+ /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx
+ MONTH_JANUARY = 1,
+ MONTH_FEBRUARY,
+ MONTH_MARCH,
+ MONTH_APRIL,
+ MONTH_MAY,
+ MONTH_JUNE,
+ MONTH_JULY,
+ MONTH_AUGUST,
+ MONTH_SEPTEMBER,
+ MONTH_OCTOBER,
+ MONTH_NOVEMBER,
+ MONTH_DECEMBER,
+ };
+
+ enum Weekday : uint8_t {
+ WEEKDAY_SUNDAY,
+ WEEKDAY_MONDAY,
+ WEEKDAY_TUESDAY,
+ WEEKDAY_WEDNESDAY,
+ WEEKDAY_THURSDAY,
+ WEEKDAY_FRIDAY,
+ WEEKDAY_SATURDAY,
+ };
+
+ // Methods that convert times.
+ Dictionary get_datetime_dict_from_unix_time(int64_t p_unix_time_val) const;
+ Dictionary get_date_dict_from_unix_time(int64_t p_unix_time_val) const;
+ Dictionary get_time_dict_from_unix_time(int64_t p_unix_time_val) const;
+ String get_datetime_string_from_unix_time(int64_t p_unix_time_val, bool p_use_space = false) const;
+ String get_date_string_from_unix_time(int64_t p_unix_time_val) const;
+ String get_time_string_from_unix_time(int64_t p_unix_time_val) const;
+ Dictionary get_datetime_dict_from_string(String p_datetime, bool p_weekday = true) const;
+ String get_datetime_string_from_dict(Dictionary p_datetime, bool p_use_space = false) const;
+ int64_t get_unix_time_from_datetime_dict(Dictionary p_datetime) const;
+ int64_t get_unix_time_from_datetime_string(String p_datetime) const;
+
+ // Methods that get information from OS.
+ Dictionary get_datetime_dict_from_system(bool p_utc = false) const;
+ Dictionary get_date_dict_from_system(bool p_utc = false) const;
+ Dictionary get_time_dict_from_system(bool p_utc = false) const;
+ String get_datetime_string_from_system(bool p_utc = false, bool p_use_space = false) const;
+ String get_date_string_from_system(bool p_utc = false) const;
+ String get_time_string_from_system(bool p_utc = false) const;
+ Dictionary get_time_zone_from_system() const;
+ double get_unix_time_from_system() const;
+ uint64_t get_ticks_msec() const;
+ uint64_t get_ticks_usec() const;
+
+ Time();
+ virtual ~Time();
+};
+
+#endif // TIME_H
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index f1b1b98bea..3a037f9dd1 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -37,16 +37,17 @@
#include "core/crypto/aes_context.h"
#include "core/crypto/crypto.h"
#include "core/crypto/hashing_context.h"
+#include "core/extension/native_extension.h"
+#include "core/extension/native_extension_manager.h"
#include "core/input/input.h"
#include "core/input/input_map.h"
+#include "core/input/shortcut.h"
#include "core/io/config_file.h"
#include "core/io/dtls_server.h"
#include "core/io/http_client.h"
#include "core/io/image_loader.h"
#include "core/io/json.h"
#include "core/io/marshalls.h"
-#include "core/io/multiplayer_api.h"
-#include "core/io/networked_multiplayer_peer.h"
#include "core/io/packed_data_container.h"
#include "core/io/packet_peer.h"
#include "core/io/packet_peer_dtls.h"
@@ -54,6 +55,7 @@
#include "core/io/pck_packer.h"
#include "core/io/resource_format_binary.h"
#include "core/io/resource_importer.h"
+#include "core/io/resource_uid.h"
#include "core/io/stream_peer_ssl.h"
#include "core/io/tcp_server.h"
#include "core/io/translation_loader_po.h"
@@ -65,9 +67,13 @@
#include "core/math/geometry_3d.h"
#include "core/math/random_number_generator.h"
#include "core/math/triangle_mesh.h"
+#include "core/multiplayer/multiplayer_api.h"
+#include "core/multiplayer/multiplayer_peer.h"
+#include "core/multiplayer/multiplayer_replicator.h"
#include "core/object/class_db.h"
#include "core/object/undo_redo.h"
#include "core/os/main_loop.h"
+#include "core/os/time.h"
#include "core/string/optimized_translation.h"
#include "core/string/translation.h"
@@ -78,26 +84,30 @@ static Ref<ResourceFormatLoaderImage> resource_format_image;
static Ref<TranslationLoaderPO> resource_format_po;
static Ref<ResourceFormatSaverCrypto> resource_format_saver_crypto;
static Ref<ResourceFormatLoaderCrypto> resource_format_loader_crypto;
+static Ref<NativeExtensionResourceLoader> resource_loader_native_extension;
-static _ResourceLoader *_resource_loader = nullptr;
-static _ResourceSaver *_resource_saver = nullptr;
-static _OS *_os = nullptr;
-static _Engine *_engine = nullptr;
-static _ClassDB *_classdb = nullptr;
-static _Marshalls *_marshalls = nullptr;
-static _JSON *_json = nullptr;
-static _EngineDebugger *_engine_debugger = nullptr;
+static core_bind::ResourceLoader *_resource_loader = nullptr;
+static core_bind::ResourceSaver *_resource_saver = nullptr;
+static core_bind::OS *_os = nullptr;
+static core_bind::Engine *_engine = nullptr;
+static core_bind::special::ClassDB *_classdb = nullptr;
+static core_bind::Marshalls *_marshalls = nullptr;
+static core_bind::EngineDebugger *_engine_debugger = nullptr;
static IP *ip = nullptr;
-static _Geometry2D *_geometry_2d = nullptr;
-static _Geometry3D *_geometry_3d = nullptr;
+static core_bind::Geometry2D *_geometry_2d = nullptr;
+static core_bind::Geometry3D *_geometry_3d = nullptr;
extern Mutex _global_mutex;
+static NativeExtensionManager *native_extension_manager = nullptr;
+
extern void register_global_constants();
extern void unregister_global_constants();
+static ResourceUID *resource_uid = nullptr;
+
void register_core_types() {
//consistency check
static_assert(sizeof(Callable) <= 16);
@@ -113,121 +123,138 @@ void register_core_types() {
CoreStringNames::create();
- resource_format_po.instance();
+ resource_format_po.instantiate();
ResourceLoader::add_resource_format_loader(resource_format_po);
- resource_saver_binary.instance();
+ resource_saver_binary.instantiate();
ResourceSaver::add_resource_format_saver(resource_saver_binary);
- resource_loader_binary.instance();
+ resource_loader_binary.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_binary);
- resource_format_importer.instance();
+ resource_format_importer.instantiate();
ResourceLoader::add_resource_format_loader(resource_format_importer);
- resource_format_image.instance();
+ resource_format_image.instantiate();
ResourceLoader::add_resource_format_loader(resource_format_image);
- ClassDB::register_class<Object>();
-
- ClassDB::register_virtual_class<Script>();
-
- ClassDB::register_class<Reference>();
- ClassDB::register_class<WeakRef>();
- ClassDB::register_class<Resource>();
- ClassDB::register_class<Image>();
-
- ClassDB::register_virtual_class<InputEvent>();
- ClassDB::register_virtual_class<InputEventWithModifiers>();
- ClassDB::register_virtual_class<InputEventFromWindow>();
- ClassDB::register_class<InputEventKey>();
- ClassDB::register_virtual_class<InputEventMouse>();
- ClassDB::register_class<InputEventMouseButton>();
- ClassDB::register_class<InputEventMouseMotion>();
- ClassDB::register_class<InputEventJoypadButton>();
- ClassDB::register_class<InputEventJoypadMotion>();
- ClassDB::register_class<InputEventScreenDrag>();
- ClassDB::register_class<InputEventScreenTouch>();
- ClassDB::register_class<InputEventAction>();
- ClassDB::register_virtual_class<InputEventGesture>();
- ClassDB::register_class<InputEventMagnifyGesture>();
- ClassDB::register_class<InputEventPanGesture>();
- ClassDB::register_class<InputEventMIDI>();
-
- ClassDB::register_virtual_class<StreamPeer>();
- ClassDB::register_class<StreamPeerBuffer>();
- ClassDB::register_class<StreamPeerTCP>();
- ClassDB::register_class<TCPServer>();
- ClassDB::register_class<PacketPeerUDP>();
- ClassDB::register_class<UDPServer>();
- ClassDB::register_custom_instance_class<PacketPeerDTLS>();
- ClassDB::register_custom_instance_class<DTLSServer>();
+ GDREGISTER_CLASS(Object);
+
+ GDREGISTER_VIRTUAL_CLASS(Script);
+
+ GDREGISTER_CLASS(RefCounted);
+ GDREGISTER_CLASS(WeakRef);
+ GDREGISTER_CLASS(Resource);
+ GDREGISTER_CLASS(Image);
+
+ GDREGISTER_CLASS(Shortcut);
+ GDREGISTER_VIRTUAL_CLASS(InputEvent);
+ GDREGISTER_VIRTUAL_CLASS(InputEventWithModifiers);
+ GDREGISTER_VIRTUAL_CLASS(InputEventFromWindow);
+ GDREGISTER_CLASS(InputEventKey);
+ GDREGISTER_CLASS(InputEventShortcut);
+ GDREGISTER_VIRTUAL_CLASS(InputEventMouse);
+ GDREGISTER_CLASS(InputEventMouseButton);
+ GDREGISTER_CLASS(InputEventMouseMotion);
+ GDREGISTER_CLASS(InputEventJoypadButton);
+ GDREGISTER_CLASS(InputEventJoypadMotion);
+ GDREGISTER_CLASS(InputEventScreenDrag);
+ GDREGISTER_CLASS(InputEventScreenTouch);
+ GDREGISTER_CLASS(InputEventAction);
+ GDREGISTER_VIRTUAL_CLASS(InputEventGesture);
+ GDREGISTER_CLASS(InputEventMagnifyGesture);
+ GDREGISTER_CLASS(InputEventPanGesture);
+ GDREGISTER_CLASS(InputEventMIDI);
+
+ // Network
+ GDREGISTER_VIRTUAL_CLASS(IP);
+
+ GDREGISTER_VIRTUAL_CLASS(StreamPeer);
+ GDREGISTER_CLASS(StreamPeerBuffer);
+ GDREGISTER_CLASS(StreamPeerTCP);
+ GDREGISTER_CLASS(TCPServer);
+
+ GDREGISTER_VIRTUAL_CLASS(PacketPeer);
+ GDREGISTER_CLASS(PacketPeerStream);
+ GDREGISTER_CLASS(PacketPeerUDP);
+ GDREGISTER_CLASS(UDPServer);
+
+ ClassDB::register_custom_instance_class<HTTPClient>();
// Crypto
- ClassDB::register_class<HashingContext>();
- ClassDB::register_class<AESContext>();
+ GDREGISTER_CLASS(HashingContext);
+ GDREGISTER_CLASS(AESContext);
ClassDB::register_custom_instance_class<X509Certificate>();
ClassDB::register_custom_instance_class<CryptoKey>();
ClassDB::register_custom_instance_class<HMACContext>();
ClassDB::register_custom_instance_class<Crypto>();
ClassDB::register_custom_instance_class<StreamPeerSSL>();
+ ClassDB::register_custom_instance_class<PacketPeerDTLS>();
+ ClassDB::register_custom_instance_class<DTLSServer>();
- resource_format_saver_crypto.instance();
+ resource_format_saver_crypto.instantiate();
ResourceSaver::add_resource_format_saver(resource_format_saver_crypto);
- resource_format_loader_crypto.instance();
+ resource_format_loader_crypto.instantiate();
ResourceLoader::add_resource_format_loader(resource_format_loader_crypto);
- ClassDB::register_virtual_class<IP>();
- ClassDB::register_virtual_class<PacketPeer>();
- ClassDB::register_class<PacketPeerStream>();
- ClassDB::register_virtual_class<NetworkedMultiplayerPeer>();
- ClassDB::register_class<MultiplayerAPI>();
- ClassDB::register_class<MainLoop>();
- ClassDB::register_class<Translation>();
- ClassDB::register_class<OptimizedTranslation>();
- ClassDB::register_class<UndoRedo>();
- ClassDB::register_class<HTTPClient>();
- ClassDB::register_class<TriangleMesh>();
+ GDREGISTER_VIRTUAL_CLASS(MultiplayerPeer);
+ GDREGISTER_VIRTUAL_CLASS(MultiplayerReplicator);
+ GDREGISTER_CLASS(MultiplayerAPI);
+ GDREGISTER_CLASS(MainLoop);
+ GDREGISTER_CLASS(Translation);
+ GDREGISTER_CLASS(OptimizedTranslation);
+ GDREGISTER_CLASS(UndoRedo);
+ GDREGISTER_CLASS(TriangleMesh);
+
+ GDREGISTER_CLASS(ResourceFormatLoader);
+ GDREGISTER_CLASS(ResourceFormatSaver);
+
+ GDREGISTER_CLASS(core_bind::File);
+ GDREGISTER_CLASS(core_bind::Directory);
+ GDREGISTER_CLASS(core_bind::Thread);
+ GDREGISTER_CLASS(core_bind::Mutex);
+ GDREGISTER_CLASS(core_bind::Semaphore);
+
+ GDREGISTER_CLASS(XMLParser);
+ GDREGISTER_CLASS(JSON);
- ClassDB::register_class<ResourceFormatLoader>();
- ClassDB::register_class<ResourceFormatSaver>();
+ GDREGISTER_CLASS(ConfigFile);
- ClassDB::register_class<_File>();
- ClassDB::register_class<_Directory>();
- ClassDB::register_class<_Thread>();
- ClassDB::register_class<_Mutex>();
- ClassDB::register_class<_Semaphore>();
+ GDREGISTER_CLASS(PCKPacker);
- ClassDB::register_class<XMLParser>();
- ClassDB::register_class<JSONParser>();
+ GDREGISTER_CLASS(PackedDataContainer);
+ GDREGISTER_VIRTUAL_CLASS(PackedDataContainerRef);
+ GDREGISTER_CLASS(AStar);
+ GDREGISTER_CLASS(AStar2D);
+ GDREGISTER_CLASS(EncodedObjectAsID);
+ GDREGISTER_CLASS(RandomNumberGenerator);
- ClassDB::register_class<ConfigFile>();
+ GDREGISTER_VIRTUAL_CLASS(ResourceImporter);
- ClassDB::register_class<PCKPacker>();
+ GDREGISTER_CLASS(NativeExtension);
- ClassDB::register_class<PackedDataContainer>();
- ClassDB::register_virtual_class<PackedDataContainerRef>();
- ClassDB::register_class<AStar>();
- ClassDB::register_class<AStar2D>();
- ClassDB::register_class<EncodedObjectAsID>();
- ClassDB::register_class<RandomNumberGenerator>();
+ GDREGISTER_VIRTUAL_CLASS(NativeExtensionManager);
- ClassDB::register_class<JSONParseResult>();
+ GDREGISTER_VIRTUAL_CLASS(ResourceUID);
- ClassDB::register_virtual_class<ResourceImporter>();
+ resource_uid = memnew(ResourceUID);
+
+ native_extension_manager = memnew(NativeExtensionManager);
+
+ resource_loader_native_extension.instantiate();
+ ResourceLoader::add_resource_format_loader(resource_loader_native_extension);
ip = IP::create();
- _geometry_2d = memnew(_Geometry2D);
- _geometry_3d = memnew(_Geometry3D);
-
- _resource_loader = memnew(_ResourceLoader);
- _resource_saver = memnew(_ResourceSaver);
- _os = memnew(_OS);
- _engine = memnew(_Engine);
- _classdb = memnew(_ClassDB);
- _marshalls = memnew(_Marshalls);
- _json = memnew(_JSON);
- _engine_debugger = memnew(_EngineDebugger);
+ _geometry_2d = memnew(core_bind::Geometry2D);
+ _geometry_3d = memnew(core_bind::Geometry3D);
+
+ _resource_loader = memnew(core_bind::ResourceLoader);
+ _resource_saver = memnew(core_bind::ResourceSaver);
+ _os = memnew(core_bind::OS);
+ _engine = memnew(core_bind::Engine);
+ _classdb = memnew(core_bind::special::ClassDB);
+ _marshalls = memnew(core_bind::Marshalls);
+ _engine_debugger = memnew(core_bind::EngineDebugger);
}
void register_core_settings() {
@@ -242,48 +269,61 @@ void register_core_settings() {
}
void register_core_singletons() {
- ClassDB::register_class<ProjectSettings>();
- ClassDB::register_virtual_class<IP>();
- ClassDB::register_class<_Geometry2D>();
- ClassDB::register_class<_Geometry3D>();
- ClassDB::register_class<_ResourceLoader>();
- ClassDB::register_class<_ResourceSaver>();
- ClassDB::register_class<_OS>();
- ClassDB::register_class<_Engine>();
- ClassDB::register_class<_ClassDB>();
- ClassDB::register_class<_Marshalls>();
- ClassDB::register_class<TranslationServer>();
- ClassDB::register_virtual_class<Input>();
- ClassDB::register_class<InputMap>();
- ClassDB::register_class<_JSON>();
- ClassDB::register_class<Expression>();
- ClassDB::register_class<_EngineDebugger>();
+ GDREGISTER_CLASS(ProjectSettings);
+ GDREGISTER_VIRTUAL_CLASS(IP);
+ GDREGISTER_CLASS(core_bind::Geometry2D);
+ GDREGISTER_CLASS(core_bind::Geometry3D);
+ GDREGISTER_CLASS(core_bind::ResourceLoader);
+ GDREGISTER_CLASS(core_bind::ResourceSaver);
+ GDREGISTER_CLASS(core_bind::OS);
+ GDREGISTER_CLASS(core_bind::Engine);
+ GDREGISTER_CLASS(core_bind::special::ClassDB);
+ GDREGISTER_CLASS(core_bind::Marshalls);
+ GDREGISTER_CLASS(TranslationServer);
+ GDREGISTER_VIRTUAL_CLASS(Input);
+ GDREGISTER_CLASS(InputMap);
+ GDREGISTER_CLASS(Expression);
+ GDREGISTER_CLASS(core_bind::EngineDebugger);
+ GDREGISTER_CLASS(Time);
Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton()));
- Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton()));
- Engine::get_singleton()->add_singleton(Engine::Singleton("Geometry2D", _Geometry2D::get_singleton()));
- Engine::get_singleton()->add_singleton(Engine::Singleton("Geometry3D", _Geometry3D::get_singleton()));
- Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceLoader", _ResourceLoader::get_singleton()));
- Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceSaver", _ResourceSaver::get_singleton()));
- Engine::get_singleton()->add_singleton(Engine::Singleton("OS", _OS::get_singleton()));
- Engine::get_singleton()->add_singleton(Engine::Singleton("Engine", _Engine::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton(), "IP"));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("Geometry2D", core_bind::Geometry2D::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("Geometry3D", core_bind::Geometry3D::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceLoader", core_bind::ResourceLoader::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceSaver", core_bind::ResourceSaver::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("OS", core_bind::OS::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("Engine", core_bind::Engine::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("ClassDB", _classdb));
- Engine::get_singleton()->add_singleton(Engine::Singleton("Marshalls", _Marshalls::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("Marshalls", core_bind::Marshalls::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("TranslationServer", TranslationServer::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("Input", Input::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("InputMap", InputMap::get_singleton()));
- Engine::get_singleton()->add_singleton(Engine::Singleton("JSON", _JSON::get_singleton()));
- Engine::get_singleton()->add_singleton(Engine::Singleton("EngineDebugger", _EngineDebugger::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("EngineDebugger", core_bind::EngineDebugger::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("Time", Time::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("NativeExtensionManager", NativeExtensionManager::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceUID", ResourceUID::get_singleton()));
+}
+
+void register_core_extensions() {
+ // Hardcoded for now.
+ NativeExtension::initialize_native_extensions();
+ native_extension_manager->load_extensions();
+ native_extension_manager->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_CORE);
}
void unregister_core_types() {
+ native_extension_manager->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_CORE);
+
+ memdelete(native_extension_manager);
+
+ memdelete(resource_uid);
memdelete(_resource_loader);
memdelete(_resource_saver);
memdelete(_os);
memdelete(_engine);
memdelete(_classdb);
memdelete(_marshalls);
- memdelete(_json);
memdelete(_engine_debugger);
memdelete(_geometry_2d);
@@ -313,6 +353,9 @@ void unregister_core_types() {
memdelete(ip);
}
+ ResourceLoader::remove_resource_format_loader(resource_loader_native_extension);
+ resource_loader_native_extension.unref();
+
ResourceLoader::finalize();
ClassDB::cleanup_defaults();
diff --git a/core/register_core_types.h b/core/register_core_types.h
index baf7ddbe65..830f05607d 100644
--- a/core/register_core_types.h
+++ b/core/register_core_types.h
@@ -33,6 +33,7 @@
void register_core_types();
void register_core_settings();
+void register_core_extensions();
void register_core_singletons();
void unregister_core_types();
diff --git a/core/string/node_path.cpp b/core/string/node_path.cpp
index d3afa7b4dd..5fae13779e 100644
--- a/core/string/node_path.cpp
+++ b/core/string/node_path.cpp
@@ -240,19 +240,26 @@ NodePath NodePath::rel_path_to(const NodePath &p_np) const {
common_parent--;
Vector<StringName> relpath;
+ relpath.resize(src_dirs.size() + dst_dirs.size() + 1);
- for (int i = src_dirs.size() - 1; i > common_parent; i--) {
- relpath.push_back("..");
+ StringName *relpath_ptr = relpath.ptrw();
+
+ int path_size = 0;
+ StringName back_str("..");
+ for (int i = common_parent + 1; i < src_dirs.size(); i++) {
+ relpath_ptr[path_size++] = back_str;
}
for (int i = common_parent + 1; i < dst_dirs.size(); i++) {
- relpath.push_back(dst_dirs[i]);
+ relpath_ptr[path_size++] = dst_dirs[i];
}
- if (relpath.size() == 0) {
- relpath.push_back(".");
+ if (path_size == 0) {
+ relpath_ptr[path_size++] = ".";
}
+ relpath.resize(path_size);
+
return NodePath(relpath, p_np.get_subnames(), false);
}
diff --git a/core/string/optimized_translation.cpp b/core/string/optimized_translation.cpp
index 268562d971..5863bd1c46 100644
--- a/core/string/optimized_translation.cpp
+++ b/core/string/optimized_translation.cpp
@@ -66,9 +66,9 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) {
int total_compression_size = 0;
int total_string_size = 0;
- for (List<StringName>::Element *E = keys.front(); E; E = E->next()) {
+ for (const StringName &E : keys) {
//hash string
- CharString cs = E->get().operator String().utf8();
+ CharString cs = E.operator String().utf8();
uint32_t h = hash(0, cs.get_data());
Pair<int, CharString> p;
p.first = idx;
@@ -76,7 +76,7 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) {
buckets.write[h % size].push_back(p);
//compress string
- CharString src_s = p_from->get_message(E->get()).operator String().utf8();
+ CharString src_s = p_from->get_message(E).operator String().utf8();
CompressedString ps;
ps.orig_len = src_s.size();
ps.offset = total_compression_size;
diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp
index 14b87072bb..9024f60dae 100644
--- a/core/string/string_name.cpp
+++ b/core/string/string_name.cpp
@@ -41,13 +41,17 @@ StaticCString StaticCString::create(const char *p_ptr) {
StringName::_Data *StringName::_table[STRING_TABLE_LEN];
-StringName _scs_create(const char *p_chr) {
- return (p_chr[0] ? StringName(StaticCString::create(p_chr)) : StringName());
+StringName _scs_create(const char *p_chr, bool p_static) {
+ return (p_chr[0] ? StringName(StaticCString::create(p_chr), p_static) : StringName());
}
bool StringName::configured = false;
Mutex StringName::mutex;
+#ifdef DEBUG_ENABLED
+bool StringName::debug_stringname = false;
+#endif
+
void StringName::setup() {
ERR_FAIL_COND(configured);
for (int i = 0; i < STRING_TABLE_LEN; i++) {
@@ -59,12 +63,29 @@ void StringName::setup() {
void StringName::cleanup() {
MutexLock lock(mutex);
+#ifdef DEBUG_ENABLED
+ if (unlikely(debug_stringname)) {
+ Vector<_Data *> data;
+ for (int i = 0; i < STRING_TABLE_LEN; i++) {
+ _Data *d = _table[i];
+ while (d) {
+ data.push_back(d);
+ d = d->next;
+ }
+ }
+ print_line("\nStringName Reference Ranking:\n");
+ data.sort_custom<DebugSortReferences>();
+ for (int i = 0; i < MIN(100, data.size()); i++) {
+ print_line(itos(i + 1) + ": " + data[i]->get_name() + " - " + itos(data[i]->debug_references));
+ }
+ }
+#endif
int lost_strings = 0;
for (int i = 0; i < STRING_TABLE_LEN; i++) {
while (_table[i]) {
_Data *d = _table[i];
lost_strings++;
- if (OS::get_singleton()->is_stdout_verbose()) {
+ if (d->static_count.get() != d->refcount.get() && OS::get_singleton()->is_stdout_verbose()) {
if (d->cname) {
print_line("Orphan StringName: " + String(d->cname));
} else {
@@ -79,6 +100,7 @@ void StringName::cleanup() {
if (lost_strings) {
print_verbose("StringName: " + itos(lost_strings) + " unclaimed string names at exit.");
}
+ configured = false;
}
void StringName::unref() {
@@ -87,6 +109,13 @@ void StringName::unref() {
if (_data && _data->refcount.unref()) {
MutexLock lock(mutex);
+ if (_data->static_count.get() > 0) {
+ if (_data->cname) {
+ ERR_PRINT("BUG: Unreferenced static string to 0: " + String(_data->cname));
+ } else {
+ ERR_PRINT("BUG: Unreferenced static string to 0: " + String(_data->name));
+ }
+ }
if (_data->prev) {
_data->prev->next = _data->next;
} else {
@@ -153,7 +182,7 @@ StringName::StringName(const StringName &p_name) {
}
}
-StringName::StringName(const char *p_name) {
+StringName::StringName(const char *p_name, bool p_static) {
_data = nullptr;
ERR_FAIL_COND(!configured);
@@ -181,25 +210,42 @@ StringName::StringName(const char *p_name) {
if (_data) {
if (_data->refcount.ref()) {
// exists
- return;
+ if (p_static) {
+ _data->static_count.increment();
+ }
+#ifdef DEBUG_ENABLED
+ if (unlikely(debug_stringname)) {
+ _data->debug_references++;
+ }
+#endif
}
+
+ return;
}
_data = memnew(_Data);
_data->name = p_name;
_data->refcount.init();
+ _data->static_count.set(p_static ? 1 : 0);
_data->hash = hash;
_data->idx = idx;
_data->cname = nullptr;
_data->next = _table[idx];
_data->prev = nullptr;
+#ifdef DEBUG_ENABLED
+ if (unlikely(debug_stringname)) {
+ // Keep in memory, force static.
+ _data->refcount.ref();
+ _data->static_count.increment();
+ }
+#endif
if (_table[idx]) {
_table[idx]->prev = _data;
}
_table[idx] = _data;
}
-StringName::StringName(const StaticCString &p_static_string) {
+StringName::StringName(const StaticCString &p_static_string, bool p_static) {
_data = nullptr;
ERR_FAIL_COND(!configured);
@@ -225,6 +271,14 @@ StringName::StringName(const StaticCString &p_static_string) {
if (_data) {
if (_data->refcount.ref()) {
// exists
+ if (p_static) {
+ _data->static_count.increment();
+ }
+#ifdef DEBUG_ENABLED
+ if (unlikely(debug_stringname)) {
+ _data->debug_references++;
+ }
+#endif
return;
}
}
@@ -232,18 +286,26 @@ StringName::StringName(const StaticCString &p_static_string) {
_data = memnew(_Data);
_data->refcount.init();
+ _data->static_count.set(p_static ? 1 : 0);
_data->hash = hash;
_data->idx = idx;
_data->cname = p_static_string.ptr;
_data->next = _table[idx];
_data->prev = nullptr;
+#ifdef DEBUG_ENABLED
+ if (unlikely(debug_stringname)) {
+ // Keep in memory, force static.
+ _data->refcount.ref();
+ _data->static_count.increment();
+ }
+#endif
if (_table[idx]) {
_table[idx]->prev = _data;
}
_table[idx] = _data;
}
-StringName::StringName(const String &p_name) {
+StringName::StringName(const String &p_name, bool p_static) {
_data = nullptr;
ERR_FAIL_COND(!configured);
@@ -269,6 +331,14 @@ StringName::StringName(const String &p_name) {
if (_data) {
if (_data->refcount.ref()) {
// exists
+ if (p_static) {
+ _data->static_count.increment();
+ }
+#ifdef DEBUG_ENABLED
+ if (unlikely(debug_stringname)) {
+ _data->debug_references++;
+ }
+#endif
return;
}
}
@@ -276,11 +346,20 @@ StringName::StringName(const String &p_name) {
_data = memnew(_Data);
_data->name = p_name;
_data->refcount.init();
+ _data->static_count.set(p_static ? 1 : 0);
_data->hash = hash;
_data->idx = idx;
_data->cname = nullptr;
_data->next = _table[idx];
_data->prev = nullptr;
+#ifdef DEBUG_ENABLED
+ if (unlikely(debug_stringname)) {
+ // Keep in memory, force static.
+ _data->refcount.ref();
+ _data->static_count.increment();
+ }
+#endif
+
if (_table[idx]) {
_table[idx]->prev = _data;
}
@@ -311,6 +390,12 @@ StringName StringName::search(const char *p_name) {
}
if (_data && _data->refcount.ref()) {
+#ifdef DEBUG_ENABLED
+ if (unlikely(debug_stringname)) {
+ _data->debug_references++;
+ }
+#endif
+
return StringName(_data);
}
@@ -368,16 +453,17 @@ StringName StringName::search(const String &p_name) {
}
if (_data && _data->refcount.ref()) {
+#ifdef DEBUG_ENABLED
+ if (unlikely(debug_stringname)) {
+ _data->debug_references++;
+ }
+#endif
return StringName(_data);
}
return StringName(); //does not exist
}
-StringName::~StringName() {
- unref();
-}
-
bool operator==(const String &p_name, const StringName &p_string_name) {
return p_name == p_string_name.operator String();
}
diff --git a/core/string/string_name.h b/core/string/string_name.h
index 44d0ea14fa..ce7988744b 100644
--- a/core/string/string_name.h
+++ b/core/string/string_name.h
@@ -44,16 +44,19 @@ struct StaticCString {
class StringName {
enum {
- STRING_TABLE_BITS = 12,
+ STRING_TABLE_BITS = 16,
STRING_TABLE_LEN = 1 << STRING_TABLE_BITS,
STRING_TABLE_MASK = STRING_TABLE_LEN - 1
};
struct _Data {
SafeRefCount refcount;
+ SafeNumeric<uint32_t> static_count;
const char *cname = nullptr;
String name;
-
+#ifdef DEBUG_ENABLED
+ uint32_t debug_references = 0;
+#endif
String get_name() const { return cname ? String(cname) : name; }
int idx = 0;
uint32_t hash = 0;
@@ -79,6 +82,15 @@ class StringName {
static void setup();
static void cleanup();
static bool configured;
+#ifdef DEBUG_ENABLED
+ struct DebugSortReferences {
+ bool operator()(const _Data *p_left, const _Data *p_right) const {
+ return p_left->debug_references > p_right->debug_references;
+ }
+ };
+
+ static bool debug_stringname;
+#endif
StringName(_Data *p_data) { _data = p_data; }
@@ -146,12 +158,20 @@ public:
};
void operator=(const StringName &p_name);
- StringName(const char *p_name);
+ StringName(const char *p_name, bool p_static = false);
StringName(const StringName &p_name);
- StringName(const String &p_name);
- StringName(const StaticCString &p_static_string);
+ StringName(const String &p_name, bool p_static = false);
+ StringName(const StaticCString &p_static_string, bool p_static = false);
StringName() {}
- ~StringName();
+ _FORCE_INLINE_ ~StringName() {
+ if (likely(configured) && _data) { //only free if configured
+ unref();
+ }
+ }
+
+#ifdef DEBUG_ENABLED
+ static void set_debug_stringnames(bool p_enable) { debug_stringname = p_enable; }
+#endif
};
bool operator==(const String &p_name, const StringName &p_string_name);
@@ -159,6 +179,8 @@ bool operator!=(const String &p_name, const StringName &p_string_name);
bool operator==(const char *p_name, const StringName &p_string_name);
bool operator!=(const char *p_name, const StringName &p_string_name);
-StringName _scs_create(const char *p_chr);
+StringName _scs_create(const char *p_chr, bool p_static = false);
+
+#define SNAME(m_arg) ([]() -> const StringName & { static StringName sname = _scs_create(m_arg, true); return sname; })()
#endif // STRING_NAME_H
diff --git a/core/string/translation.cpp b/core/string/translation.cpp
index 153f0190fd..cb7d924556 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -84,6 +84,7 @@ static const char *locale_list[] = {
"ast_ES", // Asturian (Spain)
"ayc_PE", // Southern Aymara (Peru)
"ay_PE", // Aymara (Peru)
+ "az", // Azerbaijani
"az_AZ", // Azerbaijani (Azerbaijan)
"be", // Belarusian
"be_BY", // Belarusian (Belarus)
@@ -240,6 +241,7 @@ static const char *locale_list[] = {
"ka_GE", // Georgian (Georgia)
"kk_KZ", // Kazakh (Kazakhstan)
"kl_GL", // Kalaallisut (Greenland)
+ "km", // Central Khmer
"km_KH", // Central Khmer (Cambodia)
"kn_IN", // Kannada (India)
"kok_IN", // Konkani (India)
@@ -390,6 +392,7 @@ static const char *locale_list[] = {
"tr_CY", // Turkish (Cyprus)
"tr_TR", // Turkish (Turkey)
"ts_ZA", // Tsonga (South Africa)
+ "tt", // Tatar
"tt_RU", // Tatar (Russia)
"tzm", // Central Atlas Tamazight
"tzm_MA", // Central Atlas Tamazight (Marrocos)
@@ -458,6 +461,7 @@ static const char *locale_names[] = {
"Asturian (Spain)",
"Southern Aymara (Peru)",
"Aymara (Peru)",
+ "Azerbaijani",
"Azerbaijani (Azerbaijan)",
"Belarusian",
"Belarusian (Belarus)",
@@ -614,6 +618,7 @@ static const char *locale_names[] = {
"Georgian (Georgia)",
"Kazakh (Kazakhstan)",
"Kalaallisut (Greenland)",
+ "Central Khmer",
"Central Khmer (Cambodia)",
"Kannada (India)",
"Konkani (India)",
@@ -764,6 +769,7 @@ static const char *locale_names[] = {
"Turkish (Cyprus)",
"Turkish (Turkey)",
"Tsonga (South Africa)",
+ "Tatar",
"Tatar (Russia)",
"Central Atlas Tamazight",
"Central Atlas Tamazight (Marrocos)",
@@ -835,8 +841,8 @@ Vector<String> Translation::_get_message_list() const {
void Translation::_set_messages(const Dictionary &p_messages) {
List<Variant> keys;
p_messages.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- translation_map[E->get()] = p_messages[E->get()];
+ for (const Variant &E : keys) {
+ translation_map[E] = p_messages[E];
}
}
@@ -923,6 +929,66 @@ void Translation::_bind_methods() {
///////////////////////////////////////////////
+struct _character_accent_pair {
+ const char32_t character;
+ const char32_t *accented_character;
+};
+
+static _character_accent_pair _character_to_accented[] = {
+ { 'A', U"Å" },
+ { 'B', U"ß" },
+ { 'C', U"Ç" },
+ { 'D', U"Ð" },
+ { 'E', U"É" },
+ { 'F', U"F́" },
+ { 'G', U"Ĝ" },
+ { 'H', U"Ĥ" },
+ { 'I', U"Ĩ" },
+ { 'J', U"Ĵ" },
+ { 'K', U"ĸ" },
+ { 'L', U"Ł" },
+ { 'M', U"Ḿ" },
+ { 'N', U"й" },
+ { 'O', U"Ö" },
+ { 'P', U"Ṕ" },
+ { 'Q', U"Q́" },
+ { 'R', U"Ř" },
+ { 'S', U"Ŝ" },
+ { 'T', U"Ŧ" },
+ { 'U', U"Ũ" },
+ { 'V', U"Ṽ" },
+ { 'W', U"Ŵ" },
+ { 'X', U"X́" },
+ { 'Y', U"Ÿ" },
+ { 'Z', U"Ž" },
+ { 'a', U"á" },
+ { 'b', U"ḅ" },
+ { 'c', U"ć" },
+ { 'd', U"d́" },
+ { 'e', U"é" },
+ { 'f', U"f́" },
+ { 'g', U"ǵ" },
+ { 'h', U"h̀" },
+ { 'i', U"í" },
+ { 'j', U"ǰ" },
+ { 'k', U"ḱ" },
+ { 'l', U"ł" },
+ { 'm', U"m̀" },
+ { 'n', U"ή" },
+ { 'o', U"ô" },
+ { 'p', U"ṕ" },
+ { 'q', U"q́" },
+ { 'r', U"ŕ" },
+ { 's', U"š" },
+ { 't', U"ŧ" },
+ { 'u', U"ü" },
+ { 'v', U"ṽ" },
+ { 'w', U"ŵ" },
+ { 'x', U"x́" },
+ { 'y', U"ý" },
+ { 'z', U"ź" },
+};
+
bool TranslationServer::is_locale_valid(const String &p_locale) {
const char **ptr = locale_list;
@@ -1095,10 +1161,10 @@ StringName TranslationServer::translate(const StringName &p_message, const Strin
}
if (!res) {
- return p_message;
+ return pseudolocalization_enabled ? pseudolocalize(p_message) : p_message;
}
- return res;
+ return pseudolocalization_enabled ? pseudolocalize(res) : res;
}
StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
@@ -1211,7 +1277,18 @@ void TranslationServer::setup() {
} else {
set_locale(OS::get_singleton()->get_locale());
}
+
fallback = GLOBAL_DEF("internationalization/locale/fallback", "en");
+ pseudolocalization_enabled = GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false);
+ pseudolocalization_accents_enabled = GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true);
+ pseudolocalization_double_vowels_enabled = GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false);
+ pseudolocalization_fake_bidi_enabled = GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false);
+ pseudolocalization_override_enabled = GLOBAL_DEF("internationalization/pseudolocalization/override", false);
+ expansion_ratio = GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0);
+ pseudolocalization_prefix = GLOBAL_DEF("internationalization/pseudolocalization/prefix", "[");
+ pseudolocalization_suffix = GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]");
+ pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true);
+
#ifdef TOOLS_ENABLED
{
String options = "";
@@ -1252,10 +1329,10 @@ StringName TranslationServer::tool_translate(const StringName &p_message, const
if (tool_translation.is_valid()) {
StringName r = tool_translation->get_message(p_message, p_context);
if (r) {
- return r;
+ return editor_pseudolocalization ? tool_pseudolocalize(r) : r;
}
}
- return p_message;
+ return editor_pseudolocalization ? tool_pseudolocalize(p_message) : p_message;
}
StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
@@ -1300,6 +1377,181 @@ StringName TranslationServer::doc_translate_plural(const StringName &p_message,
return p_message_plural;
}
+bool TranslationServer::is_pseudolocalization_enabled() const {
+ return pseudolocalization_enabled;
+}
+
+void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) {
+ pseudolocalization_enabled = p_enabled;
+
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
+ }
+ ResourceLoader::reload_translation_remaps();
+}
+
+void TranslationServer::set_editor_pseudolocalization(bool p_enabled) {
+ editor_pseudolocalization = p_enabled;
+}
+
+void TranslationServer::reload_pseudolocalization() {
+ pseudolocalization_accents_enabled = GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents");
+ pseudolocalization_double_vowels_enabled = GLOBAL_GET("internationalization/pseudolocalization/double_vowels");
+ pseudolocalization_fake_bidi_enabled = GLOBAL_GET("internationalization/pseudolocalization/fake_bidi");
+ pseudolocalization_override_enabled = GLOBAL_GET("internationalization/pseudolocalization/override");
+ expansion_ratio = GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio");
+ pseudolocalization_prefix = GLOBAL_GET("internationalization/pseudolocalization/prefix");
+ pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix");
+ pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders");
+
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
+ }
+ ResourceLoader::reload_translation_remaps();
+}
+
+StringName TranslationServer::pseudolocalize(const StringName &p_message) const {
+ String message = p_message;
+ int length = message.length();
+ if (pseudolocalization_override_enabled) {
+ message = get_override_string(message);
+ }
+
+ if (pseudolocalization_double_vowels_enabled) {
+ message = double_vowels(message);
+ }
+
+ if (pseudolocalization_accents_enabled) {
+ message = replace_with_accented_string(message);
+ }
+
+ if (pseudolocalization_fake_bidi_enabled) {
+ message = wrap_with_fakebidi_characters(message);
+ }
+
+ StringName res = add_padding(message, length);
+ return res;
+}
+
+StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) const {
+ String message = p_message;
+ message = double_vowels(message);
+ message = replace_with_accented_string(message);
+ StringName res = "[!!! " + message + " !!!]";
+ return res;
+}
+
+String TranslationServer::get_override_string(String &p_message) const {
+ String res;
+ for (int i = 0; i < p_message.size(); i++) {
+ if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
+ res += p_message[i];
+ res += p_message[i + 1];
+ i++;
+ continue;
+ }
+ res += '*';
+ }
+ return res;
+}
+
+String TranslationServer::double_vowels(String &p_message) const {
+ String res;
+ for (int i = 0; i < p_message.size(); i++) {
+ if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
+ res += p_message[i];
+ res += p_message[i + 1];
+ i++;
+ continue;
+ }
+ res += p_message[i];
+ if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' ||
+ p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') {
+ res += p_message[i];
+ }
+ }
+ return res;
+};
+
+String TranslationServer::replace_with_accented_string(String &p_message) const {
+ String res;
+ for (int i = 0; i < p_message.size(); i++) {
+ if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
+ res += p_message[i];
+ res += p_message[i + 1];
+ i++;
+ continue;
+ }
+ const char32_t *accented = get_accented_version(p_message[i]);
+ if (accented) {
+ res += accented;
+ } else {
+ res += p_message[i];
+ }
+ }
+ return res;
+}
+
+String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const {
+ String res;
+ char32_t fakebidiprefix = U'\u202e';
+ char32_t fakebidisuffix = U'\u202c';
+ res += fakebidiprefix;
+ // The fake bidi unicode gets popped at every newline so pushing it back at every newline.
+ for (int i = 0; i < p_message.size(); i++) {
+ if (p_message[i] == '\n') {
+ res += fakebidisuffix;
+ res += p_message[i];
+ res += fakebidiprefix;
+ } else if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
+ res += fakebidisuffix;
+ res += p_message[i];
+ res += p_message[i + 1];
+ res += fakebidiprefix;
+ i++;
+ } else {
+ res += p_message[i];
+ }
+ }
+ res += fakebidisuffix;
+ return res;
+}
+
+String TranslationServer::add_padding(String &p_message, int p_length) const {
+ String res;
+ String prefix = pseudolocalization_prefix;
+ String suffix;
+ for (int i = 0; i < p_length * expansion_ratio / 2; i++) {
+ prefix += "_";
+ suffix += "_";
+ }
+ suffix += pseudolocalization_suffix;
+ res += prefix;
+ res += p_message;
+ res += suffix;
+ return res;
+}
+
+const char32_t *TranslationServer::get_accented_version(char32_t p_character) const {
+ if (!((p_character >= 'a' && p_character <= 'z') || (p_character >= 'A' && p_character <= 'Z'))) {
+ return nullptr;
+ }
+
+ for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) {
+ if (_character_to_accented[i].character == p_character) {
+ return _character_to_accented[i].accented_character;
+ }
+ }
+
+ return nullptr;
+}
+
+bool TranslationServer::is_placeholder(String &p_message, int p_index) const {
+ return p_message[p_index] == '%' && p_index < p_message.size() - 1 &&
+ (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' ||
+ p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f');
+}
+
void TranslationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale);
ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale);
@@ -1316,6 +1568,12 @@ void TranslationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear);
ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales);
+
+ ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationServer::is_pseudolocalization_enabled);
+ ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationServer::set_pseudolocalization_enabled);
+ ClassDB::bind_method(D_METHOD("reload_pseudolocalization"), &TranslationServer::reload_pseudolocalization);
+ ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationServer::pseudolocalize);
+ ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled");
}
void TranslationServer::load_translations() {
diff --git a/core/string/translation.h b/core/string/translation.h
index 72a828227e..4f179ac0fe 100644
--- a/core/string/translation.h
+++ b/core/string/translation.h
@@ -77,6 +77,26 @@ class TranslationServer : public Object {
bool enabled = true;
+ bool pseudolocalization_enabled = false;
+ bool pseudolocalization_accents_enabled = false;
+ bool pseudolocalization_double_vowels_enabled = false;
+ bool pseudolocalization_fake_bidi_enabled = false;
+ bool pseudolocalization_override_enabled = false;
+ bool pseudolocalization_skip_placeholders_enabled = false;
+ bool editor_pseudolocalization = false;
+ float expansion_ratio = 0.0;
+ String pseudolocalization_prefix;
+ String pseudolocalization_suffix;
+
+ StringName tool_pseudolocalize(const StringName &p_message) const;
+ String get_override_string(String &p_message) const;
+ String double_vowels(String &p_message) const;
+ String replace_with_accented_string(String &p_message) const;
+ String wrap_with_fakebidi_characters(String &p_message) const;
+ String add_padding(String &p_message, int p_length) const;
+ const char32_t *get_accented_version(char32_t p_character) const;
+ bool is_placeholder(String &p_message, int p_index) const;
+
static TranslationServer *singleton;
bool _load_translations(const String &p_from);
@@ -104,6 +124,13 @@ public:
StringName translate(const StringName &p_message, const StringName &p_context = "") const;
StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
+ StringName pseudolocalize(const StringName &p_message) const;
+
+ bool is_pseudolocalization_enabled() const;
+ void set_pseudolocalization_enabled(bool p_enabled);
+ void set_editor_pseudolocalization(bool p_enabled);
+ void reload_pseudolocalization();
+
static Vector<String> get_all_locales();
static Vector<String> get_all_locale_names();
static bool is_locale_valid(const String &p_locale);
diff --git a/core/string/translation_po.cpp b/core/string/translation_po.cpp
index ad768f7140..1da00aa54b 100644
--- a/core/string/translation_po.cpp
+++ b/core/string/translation_po.cpp
@@ -30,7 +30,7 @@
#include "translation_po.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#ifdef DEBUG_TRANSLATION_PO
void TranslationPO::print_translation_map() {
@@ -47,8 +47,7 @@ void TranslationPO::print_translation_map() {
List<StringName> context_l;
translation_map.get_key_list(&context_l);
- for (List<StringName>::Element *E = context_l.front(); E; E = E->next()) {
- StringName ctx = E->get();
+ for (const StringName &ctx : context_l) {
file->store_line(" ===== Context: " + String::utf8(String(ctx).utf8()) + " ===== ");
const HashMap<StringName, Vector<StringName>> &inner_map = translation_map[ctx];
@@ -74,8 +73,7 @@ Dictionary TranslationPO::_get_messages() const {
List<StringName> context_l;
translation_map.get_key_list(&context_l);
- for (List<StringName>::Element *E = context_l.front(); E; E = E->next()) {
- StringName ctx = E->get();
+ for (const StringName &ctx : context_l) {
const HashMap<StringName, Vector<StringName>> &id_str_map = translation_map[ctx];
Dictionary d2;
@@ -98,8 +96,7 @@ void TranslationPO::_set_messages(const Dictionary &p_messages) {
List<Variant> context_l;
p_messages.get_key_list(&context_l);
- for (List<Variant>::Element *E = context_l.front(); E; E = E->next()) {
- StringName ctx = E->get();
+ for (const Variant &ctx : context_l) {
const Dictionary &id_str_map = p_messages[ctx];
HashMap<StringName, Vector<StringName>> temp_map;
@@ -121,8 +118,8 @@ Vector<String> TranslationPO::_get_message_list() const {
get_message_list(&msgs);
Vector<String> v;
- for (List<StringName>::Element *E = msgs.front(); E; E = E->next()) {
- v.push_back(E->get());
+ for (const StringName &E : msgs) {
+ v.push_back(E);
}
return v;
@@ -188,7 +185,7 @@ void TranslationPO::set_plural_rule(const String &p_plural_rule) {
plural_rule = plural_rule.replacen("(", "");
plural_rule = plural_rule.replacen(")", "");
_cache_plural_tests(plural_rule);
- expr.instance();
+ expr.instantiate();
input_name.push_back("n");
}
@@ -281,13 +278,13 @@ void TranslationPO::get_message_list(List<StringName> *r_messages) const {
List<StringName> context_l;
translation_map.get_key_list(&context_l);
- for (List<StringName>::Element *E = context_l.front(); E; E = E->next()) {
- if (String(E->get()) != "") {
+ for (const StringName &E : context_l) {
+ if (String(E) != "") {
continue;
}
List<StringName> msgid_l;
- translation_map[E->get()].get_key_list(&msgid_l);
+ translation_map[E].get_key_list(&msgid_l);
for (List<StringName>::Element *E2 = msgid_l.front(); E2; E2 = E2->next()) {
r_messages->push_back(E2->get());
@@ -300,8 +297,8 @@ int TranslationPO::get_message_count() const {
translation_map.get_key_list(&context_l);
int count = 0;
- for (List<StringName>::Element *E = context_l.front(); E; E = E->next()) {
- count += translation_map[E->get()].size();
+ for (const StringName &E : context_l) {
+ count += translation_map[E].size();
}
return count;
}
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 49cf171f2b..daeb7fbd17 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -39,12 +39,9 @@
#include "core/string/ucaps.h"
#include "core/variant/variant.h"
-#include <cstdint>
-
-#ifndef NO_USE_STDLIB
#include <stdio.h>
#include <stdlib.h>
-#endif
+#include <cstdint>
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS // to disable build-time warning which suggested to use strcpy_s instead strcpy
@@ -54,11 +51,27 @@
#define snprintf _snprintf_s
#endif
-#define MAX_DIGITS 6
-#define UPPERCASE(m_c) (((m_c) >= 'a' && (m_c) <= 'z') ? ((m_c) - ('a' - 'A')) : (m_c))
-#define LOWERCASE(m_c) (((m_c) >= 'A' && (m_c) <= 'Z') ? ((m_c) + ('a' - 'A')) : (m_c))
-#define IS_DIGIT(m_d) ((m_d) >= '0' && (m_d) <= '9')
-#define IS_HEX_DIGIT(m_d) (((m_d) >= '0' && (m_d) <= '9') || ((m_d) >= 'a' && (m_d) <= 'f') || ((m_d) >= 'A' && (m_d) <= 'F'))
+static const int MAX_DECIMALS = 32;
+
+static _FORCE_INLINE_ bool is_digit(char32_t c) {
+ return (c >= '0' && c <= '9');
+}
+
+static _FORCE_INLINE_ bool is_hex_digit(char32_t c) {
+ return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+}
+
+static _FORCE_INLINE_ bool is_upper_case(char32_t c) {
+ return (c >= 'A' && c <= 'Z');
+}
+
+static _FORCE_INLINE_ bool is_lower_case(char32_t c) {
+ return (c >= 'a' && c <= 'z');
+}
+
+static _FORCE_INLINE_ char32_t lower_case(char32_t c) {
+ return (is_upper_case(c) ? (c + ('a' - 'A')) : c);
+}
const char CharString::_null = 0;
const char16_t Char16String::_null = 0;
@@ -275,7 +288,7 @@ Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r
base = base.substr(pos + 1, base.length() - pos - 1);
} else {
// Anything else
- if (base.get_slice_count(":") > 1) {
+ if (base.get_slice_count(":") > 2) {
return ERR_INVALID_PARAMETER;
}
pos = base.rfind(":");
@@ -294,7 +307,7 @@ Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r
// Port
if (base.begins_with(":")) {
base = base.substr(1, base.length() - 1);
- if (!base.is_valid_integer()) {
+ if (!base.is_valid_int()) {
return ERR_INVALID_PARAMETER;
}
r_port = base.to_int();
@@ -738,6 +751,7 @@ bool String::operator<=(const String &p_str) const {
bool String::operator>(const String &p_str) const {
return p_str < *this;
}
+
bool String::operator>=(const String &p_str) const {
return !(*this < p_str);
}
@@ -871,8 +885,8 @@ signed char String::naturalnocasecmp_to(const String &p_str) const {
while (*this_str) {
if (!*that_str) {
return 1;
- } else if (IS_DIGIT(*this_str)) {
- if (!IS_DIGIT(*that_str)) {
+ } else if (is_digit(*this_str)) {
+ if (!is_digit(*that_str)) {
return -1;
}
@@ -881,10 +895,10 @@ signed char String::naturalnocasecmp_to(const String &p_str) const {
const char32_t *that_substr = that_str;
// Compare lengths of both numerical sequences, ignoring leading zeros
- while (IS_DIGIT(*this_str)) {
+ while (is_digit(*this_str)) {
this_str++;
}
- while (IS_DIGIT(*that_str)) {
+ while (is_digit(*that_str)) {
that_str++;
}
while (*this_substr == '0') {
@@ -912,7 +926,7 @@ signed char String::naturalnocasecmp_to(const String &p_str) const {
this_substr++;
that_substr++;
}
- } else if (IS_DIGIT(*that_str)) {
+ } else if (is_digit(*that_str)) {
return 1;
} else {
if (_find_upper(*this_str) < _find_upper(*that_str)) { //more than
@@ -962,26 +976,25 @@ String String::capitalize() const {
String String::camelcase_to_underscore(bool lowercase) const {
const char32_t *cstr = get_data();
String new_string;
- const char A = 'A', Z = 'Z';
- const char a = 'a', z = 'z';
int start_index = 0;
for (int i = 1; i < this->size(); i++) {
- bool is_upper = cstr[i] >= A && cstr[i] <= Z;
- bool is_number = cstr[i] >= '0' && cstr[i] <= '9';
+ bool is_upper = is_upper_case(cstr[i]);
+ bool is_number = is_digit(cstr[i]);
+
bool are_next_2_lower = false;
bool is_next_lower = false;
bool is_next_number = false;
- bool was_precedent_upper = cstr[i - 1] >= A && cstr[i - 1] <= Z;
- bool was_precedent_number = cstr[i - 1] >= '0' && cstr[i - 1] <= '9';
+ bool was_precedent_upper = is_upper_case(cstr[i - 1]);
+ bool was_precedent_number = is_digit(cstr[i - 1]);
if (i + 2 < this->size()) {
- are_next_2_lower = cstr[i + 1] >= a && cstr[i + 1] <= z && cstr[i + 2] >= a && cstr[i + 2] <= z;
+ are_next_2_lower = is_lower_case(cstr[i + 1]) && is_lower_case(cstr[i + 2]);
}
if (i + 1 < this->size()) {
- is_next_lower = cstr[i + 1] >= a && cstr[i + 1] <= z;
- is_next_number = cstr[i + 1] >= '0' && cstr[i + 1] <= '9';
+ is_next_lower = is_lower_case(cstr[i + 1]);
+ is_next_number = is_digit(cstr[i + 1]);
}
const bool cond_a = is_upper && !was_precedent_upper && !was_precedent_number;
@@ -1377,10 +1390,18 @@ String String::num(double p_num, int p_decimals) {
return "inf";
}
}
-#ifndef NO_USE_STDLIB
- if (p_decimals > 16) {
- p_decimals = 16;
+ if (p_decimals < 0) {
+ p_decimals = 14;
+ const double abs_num = ABS(p_num);
+ if (abs_num > 10) {
+ // We want to align the digits to the above sane default, so we only
+ // need to subtract log10 for numbers with a positive power of ten.
+ p_decimals -= (int)floor(log10(abs_num));
+ }
+ }
+ if (p_decimals > MAX_DECIMALS) {
+ p_decimals = MAX_DECIMALS;
}
char fmt[7];
@@ -1391,7 +1412,6 @@ String String::num(double p_num, int p_decimals) {
fmt[1] = 'l';
fmt[2] = 'f';
fmt[3] = 0;
-
} else if (p_decimals < 10) {
fmt[2] = '0' + p_decimals;
fmt[3] = 'l';
@@ -1442,84 +1462,6 @@ String String::num(double p_num, int p_decimals) {
}
return buf;
-#else
-
- String s;
- String sd;
- /* integer part */
-
- bool neg = p_num < 0;
- p_num = ABS(p_num);
- int intn = (int)p_num;
-
- /* decimal part */
-
- if (p_decimals > 0 || (p_decimals == -1 && (int)p_num != p_num)) {
- double dec = p_num - (double)((int)p_num);
-
- int digit = 0;
- if (p_decimals > MAX_DIGITS)
- p_decimals = MAX_DIGITS;
-
- int dec_int = 0;
- int dec_max = 0;
-
- while (true) {
- dec *= 10.0;
- dec_int = dec_int * 10 + (int)dec % 10;
- dec_max = dec_max * 10 + 9;
- digit++;
-
- if (p_decimals == -1) {
- if (digit == MAX_DIGITS) //no point in going to infinite
- break;
-
- if (dec - (double)((int)dec) < 1e-6) {
- break;
- }
- }
-
- if (digit == p_decimals)
- break;
- }
- dec *= 10;
- int last = (int)dec % 10;
-
- if (last > 5) {
- if (dec_int == dec_max) {
- dec_int = 0;
- intn++;
- } else {
- dec_int++;
- }
- }
-
- String decimal;
- for (int i = 0; i < digit; i++) {
- char num[2] = { 0, 0 };
- num[0] = '0' + dec_int % 10;
- decimal = num + decimal;
- dec_int /= 10;
- }
- sd = '.' + decimal;
- }
-
- if (intn == 0)
-
- s = "0";
- else {
- while (intn) {
- char32_t num = '0' + (intn % 10);
- intn /= 10;
- s = num + s;
- }
- }
-
- s = s + sd;
- if (neg)
- s = "-" + s;
- return s;
-#endif
}
String String::num_int64(int64_t p_num, int base, bool capitalize_hex) {
@@ -1589,7 +1531,7 @@ String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) {
return s;
}
-String String::num_real(double p_num) {
+String String::num_real(double p_num, bool p_trailing) {
if (Math::is_nan(p_num)) {
return "nan";
}
@@ -1604,30 +1546,52 @@ String String::num_real(double p_num) {
String s;
String sd;
- /* integer part */
+
+ // Integer part.
bool neg = p_num < 0;
p_num = ABS(p_num);
- int intn = (int)p_num;
+ int64_t intn = (int64_t)p_num;
- /* decimal part */
+ // Decimal part.
- if ((int)p_num != p_num) {
- double dec = p_num - (double)((int)p_num);
+ if (intn != p_num) {
+ double dec = p_num - (double)intn;
int digit = 0;
- int decimals = MAX_DIGITS;
- int dec_int = 0;
- int dec_max = 0;
+#ifdef REAL_T_IS_DOUBLE
+ int decimals = 14;
+ double tolerance = 1e-14;
+#else
+ int decimals = 6;
+ double tolerance = 1e-6;
+#endif
+ // We want to align the digits to the above sane default, so we only
+ // need to subtract log10 for numbers with a positive power of ten.
+ if (p_num > 10) {
+ decimals -= (int)floor(log10(p_num));
+ }
+
+ if (decimals > MAX_DECIMALS) {
+ decimals = MAX_DECIMALS;
+ }
+
+ // In case the value ends up ending in "99999", we want to add a
+ // tiny bit to the value we're checking when deciding when to stop,
+ // so we multiply by slightly above 1 (1 + 1e-7 or 1e-15).
+ double check_multiplier = 1 + tolerance / 10;
+
+ int64_t dec_int = 0;
+ int64_t dec_max = 0;
while (true) {
dec *= 10.0;
- dec_int = dec_int * 10 + (int)dec % 10;
+ dec_int = dec_int * 10 + (int64_t)dec % 10;
dec_max = dec_max * 10 + 9;
digit++;
- if ((dec - (double)((int)dec)) < 1e-6) {
+ if ((dec - (double)(int64_t)(dec * check_multiplier)) < tolerance) {
break;
}
@@ -1637,7 +1601,7 @@ String String::num_real(double p_num) {
}
dec *= 10;
- int last = (int)dec % 10;
+ int last = (int64_t)dec % 10;
if (last > 5) {
if (dec_int == dec_max) {
@@ -1656,8 +1620,10 @@ String String::num_real(double p_num) {
dec_int /= 10;
}
sd = '.' + decimal;
- } else {
+ } else if (p_trailing) {
sd = ".0";
+ } else {
+ sd = "";
}
if (intn == 0) {
@@ -1689,19 +1655,18 @@ String String::num_scientific(double p_num) {
return "inf";
}
}
-#ifndef NO_USE_STDLIB
char buf[256];
#if defined(__GNUC__) || defined(_MSC_VER)
-#if (defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER < 1900)) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT)
- // MinGW and old MSC require _set_output_format() to conform to C99 output for printf
+#if defined(__MINGW32__) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT)
+ // MinGW requires _set_output_format() to conform to C99 output for printf
unsigned int old_exponent_format = _set_output_format(_TWO_DIGIT_EXPONENT);
#endif
snprintf(buf, 256, "%lg", p_num);
-#if (defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER < 1900)) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT)
+#if defined(__MINGW32__) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT)
_set_output_format(old_exponent_format);
#endif
@@ -1712,10 +1677,6 @@ String String::num_scientific(double p_num) {
buf[255] = 0;
return buf;
-#else
-
- return String::num(p_num);
-#endif
}
String String::md5(const uint8_t *p_md5) {
@@ -2183,9 +2144,9 @@ int64_t String::hex_to_int() const {
int64_t hex = 0;
while (*s) {
- char32_t c = LOWERCASE(*s);
+ char32_t c = lower_case(*s);
int64_t n;
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
n = c - '0';
} else if (c >= 'a' && c <= 'f') {
n = (c - 'a') + 10;
@@ -2224,7 +2185,7 @@ int64_t String::bin_to_int() const {
int64_t binary = 0;
while (*s) {
- char32_t c = LOWERCASE(*s);
+ char32_t c = lower_case(*s);
int64_t n;
if (c == '0' || c == '1') {
n = c - '0';
@@ -2254,7 +2215,7 @@ int64_t String::to_int() const {
for (int i = 0; i < to; i++) {
char32_t c = operator[](i);
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8')));
ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small."));
integer *= 10;
@@ -2283,7 +2244,7 @@ int64_t String::to_int(const char *p_str, int p_len) {
for (int i = 0; i < to; i++) {
char c = p_str[i];
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8')));
ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small."));
integer *= 10;
@@ -2314,7 +2275,7 @@ int64_t String::to_int(const wchar_t *p_str, int p_len) {
for (int i = 0; i < to; i++) {
wchar_t c = p_str[i];
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8')));
ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small."));
integer *= 10;
@@ -2434,7 +2395,7 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point
decPt = -1;
for (mantSize = 0;; mantSize += 1) {
c = *p;
- if (!IS_DIGIT(c)) {
+ if (!is_digit(c)) {
if ((c != '.') || (decPt >= 0)) {
break;
}
@@ -2509,11 +2470,11 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point
}
expSign = false;
}
- if (!IS_DIGIT(char32_t(*p))) {
+ if (!is_digit(char32_t(*p))) {
p = pExp;
goto done;
}
- while (IS_DIGIT(char32_t(*p))) {
+ while (is_digit(char32_t(*p))) {
exp = exp * 10 + (*p - '0');
p += 1;
}
@@ -2599,7 +2560,7 @@ int64_t String::to_int(const char32_t *p_str, int p_len, bool p_clamp) {
char32_t c = *(str++);
switch (reading) {
case READING_SIGN: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
reading = READING_INT;
// let it fallthrough
} else if (c == '-') {
@@ -2616,7 +2577,7 @@ int64_t String::to_int(const char32_t *p_str, int p_len, bool p_clamp) {
[[fallthrough]];
}
case READING_INT: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
if (integer > INT64_MAX / 10) {
String number("");
str = p_str;
@@ -3382,17 +3343,10 @@ String String::format(const Variant &values, String placeholder) const {
if (value_arr.size() == 2) {
Variant v_key = value_arr[0];
String key = v_key;
- if (key.left(1) == "\"" && key.right(1) == "\"") {
- key = key.substr(1, key.length() - 2);
- }
Variant v_val = value_arr[1];
String val = v_val;
- if (val.left(1) == "\"" && val.right(1) == "\"") {
- val = val.substr(1, val.length() - 2);
- }
-
new_string = new_string.replace(placeholder.replace("_", key), val);
} else {
ERR_PRINT(String("STRING.format Inner Array size != 2 ").ascii().get_data());
@@ -3401,10 +3355,6 @@ String String::format(const Variant &values, String placeholder) const {
Variant v_val = values_arr[i];
String val = v_val;
- if (val.left(1) == "\"" && val.right(1) == "\"") {
- val = val.substr(1, val.length() - 2);
- }
-
if (placeholder.find("_") > -1) {
new_string = new_string.replace(placeholder.replace("_", i_as_str), val);
} else {
@@ -3417,19 +3367,8 @@ String String::format(const Variant &values, String placeholder) const {
List<Variant> keys;
d.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- String key = E->get();
- String val = d[E->get()];
-
- if (key.left(1) == "\"" && key.right(1) == "\"") {
- key = key.substr(1, key.length() - 2);
- }
-
- if (val.left(1) == "\"" && val.right(1) == "\"") {
- val = val.substr(1, val.length() - 2);
- }
-
- new_string = new_string.replace(placeholder.replace("_", key), val);
+ for (const Variant &key : keys) {
+ new_string = new_string.replace(placeholder.replace("_", key), d[key]);
}
} else {
ERR_PRINT(String("Invalid type: use Array or Dictionary.").ascii().get_data());
@@ -3623,7 +3562,7 @@ String String::strip_edges(bool left, bool right) const {
}
if (right) {
- for (int i = (int)(len - 1); i >= 0; i--) {
+ for (int i = len - 1; i >= 0; i--) {
if (operator[](i) <= 32) {
end--;
} else {
@@ -3786,7 +3725,7 @@ String String::humanize_size(uint64_t p_size) {
return String::num(p_size / divisor).pad_decimals(digits) + " " + prefixes[prefix_idx];
}
-bool String::is_abs_path() const {
+bool String::is_absolute_path() const {
if (length() > 1) {
return (operator[](0) == '/' || operator[](0) == '\\' || find(":/") != -1 || find(":\\") != -1);
} else if ((length()) == 1) {
@@ -3807,12 +3746,12 @@ bool String::is_valid_identifier() const {
for (int i = 0; i < len; i++) {
if (i == 0) {
- if (str[0] >= '0' && str[0] <= '9') {
+ if (is_digit(str[0])) {
return false; // no start with number plz
}
}
- bool valid_char = (str[i] >= '0' && str[i] <= '9') || (str[i] >= 'a' && str[i] <= 'z') || (str[i] >= 'A' && str[i] <= 'Z') || str[i] == '_';
+ bool valid_char = is_digit(str[i]) || is_lower_case(str[i]) || is_upper_case(str[i]) || str[i] == '_';
if (!valid_char) {
return false;
@@ -3837,10 +3776,7 @@ String String::uri_encode() const {
String res;
for (int i = 0; i < temp.length(); ++i) {
char ord = temp[i];
- if (ord == '.' || ord == '-' || ord == '_' || ord == '~' ||
- (ord >= 'a' && ord <= 'z') ||
- (ord >= 'A' && ord <= 'Z') ||
- (ord >= '0' && ord <= '9')) {
+ if (ord == '.' || ord == '-' || ord == '_' || ord == '~' || is_lower_case(ord) || is_upper_case(ord) || is_digit(ord)) {
res += ord;
} else {
char h_Val[3];
@@ -3862,9 +3798,9 @@ String String::uri_decode() const {
for (int i = 0; i < src.length(); ++i) {
if (src[i] == '%' && i + 2 < src.length()) {
char ord1 = src[i + 1];
- if ((ord1 >= '0' && ord1 <= '9') || (ord1 >= 'A' && ord1 <= 'Z')) {
+ if (is_digit(ord1) || is_upper_case(ord1)) {
char ord2 = src[i + 2];
- if ((ord2 >= '0' && ord2 <= '9') || (ord2 >= 'A' && ord2 <= 'Z')) {
+ if (is_digit(ord2) || is_upper_case(ord2)) {
char bytes[3] = { (char)ord1, (char)ord2, 0 };
res += (char)strtol(bytes, nullptr, 16);
i += 2;
@@ -3970,7 +3906,7 @@ static _FORCE_INLINE_ int _xml_unescape(const char32_t *p_src, int p_src_len, ch
char32_t ct = p_src[i];
if (ct == ';') {
break;
- } else if (ct >= '0' && ct <= '9') {
+ } else if (is_digit(ct)) {
ct = ct - '0';
} else if (ct >= 'a' && ct <= 'f') {
ct = (ct - 'a') + 10;
@@ -4153,7 +4089,7 @@ String String::trim_suffix(const String &p_suffix) const {
return s;
}
-bool String::is_valid_integer() const {
+bool String::is_valid_int() const {
int len = length();
if (len == 0) {
@@ -4198,7 +4134,7 @@ bool String::is_valid_hex_number(bool p_with_prefix) const {
for (int i = from; i < len; i++) {
char32_t c = operator[](i);
- if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
+ if (is_hex_digit(c)) {
continue;
}
return false;
@@ -4226,7 +4162,7 @@ bool String::is_valid_float() const {
bool numbers_found = false;
for (int i = from; i < len; i++) {
- if (operator[](i) >= '0' && operator[](i) <= '9') {
+ if (is_digit(operator[](i))) {
if (exponent_found) {
exponent_values_found = true;
} else {
@@ -4378,7 +4314,7 @@ bool String::is_valid_ip_address() const {
}
for (int i = 0; i < ip.size(); i++) {
String n = ip[i];
- if (!n.is_valid_integer()) {
+ if (!n.is_valid_int()) {
return false;
}
int val = n.to_int();
@@ -4395,28 +4331,44 @@ bool String::is_resource_file() const {
return begins_with("res://") && find("::") == -1;
}
-bool String::is_rel_path() const {
- return !is_abs_path();
+bool String::is_relative_path() const {
+ return !is_absolute_path();
}
String String::get_base_dir() const {
- int basepos = find(":/");
- if (basepos == -1) {
- basepos = find(":\\");
+ int end = 0;
+
+ // url scheme style base
+ int basepos = find("://");
+ if (basepos != -1) {
+ end = basepos + 3;
}
+
+ // windows top level directory base
+ if (end == 0) {
+ basepos = find(":/");
+ if (basepos == -1) {
+ basepos = find(":\\");
+ }
+ if (basepos != -1) {
+ end = basepos + 2;
+ }
+ }
+
+ // unix root directory base
+ if (end == 0) {
+ if (begins_with("/")) {
+ end = 1;
+ }
+ }
+
String rs;
String base;
- if (basepos != -1) {
- int end = basepos + 3;
+ if (end != 0) {
rs = substr(end, length());
base = substr(0, end);
} else {
- if (begins_with("/")) {
- rs = substr(1, length());
- base = "/";
- } else {
- rs = *this;
- }
+ rs = *this;
}
int sep = MAX(rs.rfind("/"), rs.rfind("\\"));
diff --git a/core/string/ustring.h b/core/string/ustring.h
index a56845deff..24da6b82af 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -309,7 +309,7 @@ public:
String unquote() const;
static String num(double p_num, int p_decimals = -1);
static String num_scientific(double p_num);
- static String num_real(double p_num);
+ static String num_real(double p_num, bool p_trailing = true);
static String num_int64(int64_t p_num, int base = 10, bool capitalize_hex = false);
static String num_uint64(uint64_t p_num, int base = 10, bool capitalize_hex = false);
static String chr(char32_t p_char);
@@ -397,8 +397,8 @@ public:
_FORCE_INLINE_ bool is_empty() const { return length() == 0; }
// path functions
- bool is_abs_path() const;
- bool is_rel_path() const;
+ bool is_absolute_path() const;
+ bool is_relative_path() const;
bool is_resource_file() const;
String path_to(const String &p_path) const;
String path_to_file(const String &p_path) const;
@@ -425,7 +425,7 @@ public:
String validate_node_name() const;
bool is_valid_identifier() const;
- bool is_valid_integer() const;
+ bool is_valid_int() const;
bool is_valid_float() const;
bool is_valid_hex_number(bool p_with_prefix) const;
bool is_valid_html_color() const;
@@ -523,10 +523,10 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St
#define TTRGET(m_value) TTR(m_value)
#else
-#define TTR(m_value) (String())
-#define TTRN(m_value) (String())
-#define DTR(m_value) (String())
-#define DTRN(m_value) (String())
+#define TTR(m_value) String()
+#define TTRN(m_value) String()
+#define DTR(m_value) String()
+#define DTRN(m_value) String()
#define TTRC(m_value) (m_value)
#define TTRGET(m_value) (m_value)
#endif
diff --git a/core/templates/bin_sorted_array.h b/core/templates/bin_sorted_array.h
new file mode 100644
index 0000000000..be9d0b5475
--- /dev/null
+++ b/core/templates/bin_sorted_array.h
@@ -0,0 +1,181 @@
+/*************************************************************************/
+/* bin_sorted_array.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef BIN_SORTED_ARRAY_H
+#define BIN_SORTED_ARRAY_H
+
+#include "core/templates/local_vector.h"
+#include "core/templates/paged_array.h"
+
+template <class T>
+class BinSortedArray {
+ PagedArray<T> array;
+ LocalVector<uint64_t> bin_limits;
+
+ // Implement if elements need to keep track of their own index in the array.
+ _FORCE_INLINE_ virtual void _update_idx(T &r_element, uint64_t p_idx) {}
+
+ _FORCE_INLINE_ void _swap(uint64_t p_a, uint64_t p_b) {
+ SWAP(array[p_a], array[p_b]);
+ _update_idx(array[p_a], p_a);
+ _update_idx(array[p_b], p_b);
+ }
+
+public:
+ uint64_t insert(T &p_element, uint64_t p_bin) {
+ array.push_back(p_element);
+ uint64_t new_idx = array.size() - 1;
+ _update_idx(p_element, new_idx);
+ bin_limits[0] = new_idx;
+ if (p_bin != 0) {
+ new_idx = move(new_idx, p_bin);
+ }
+ return new_idx;
+ }
+
+ uint64_t move(uint64_t p_idx, uint64_t p_bin) {
+ ERR_FAIL_COND_V(p_idx >= array.size(), -1);
+
+ uint64_t current_bin = bin_limits.size() - 1;
+ while (p_idx > bin_limits[current_bin]) {
+ current_bin--;
+ }
+
+ if (p_bin == current_bin) {
+ return p_idx;
+ }
+
+ uint64_t current_idx = p_idx;
+ if (p_bin > current_bin) {
+ while (p_bin > current_bin) {
+ uint64_t swap_idx = 0;
+
+ if (current_bin == bin_limits.size() - 1) {
+ bin_limits.push_back(0);
+ } else {
+ bin_limits[current_bin + 1]++;
+ swap_idx = bin_limits[current_bin + 1];
+ }
+
+ if (current_idx != swap_idx) {
+ _swap(current_idx, swap_idx);
+ current_idx = swap_idx;
+ }
+
+ current_bin++;
+ }
+ } else {
+ while (p_bin < current_bin) {
+ uint64_t swap_idx = bin_limits[current_bin];
+
+ if (current_idx != swap_idx) {
+ _swap(current_idx, swap_idx);
+ }
+
+ if (current_bin == bin_limits.size() - 1 && bin_limits[current_bin] == 0) {
+ bin_limits.resize(bin_limits.size() - 1);
+ } else {
+ bin_limits[current_bin]--;
+ }
+ current_idx = swap_idx;
+ current_bin--;
+ }
+ }
+
+ return current_idx;
+ }
+
+ void remove(uint64_t p_idx) {
+ ERR_FAIL_COND(p_idx >= array.size());
+ uint64_t new_idx = move(p_idx, 0);
+ uint64_t swap_idx = array.size() - 1;
+
+ if (new_idx != swap_idx) {
+ _swap(new_idx, swap_idx);
+ }
+
+ if (bin_limits[0] > 0) {
+ bin_limits[0]--;
+ }
+
+ array.pop_back();
+ }
+
+ void set_page_pool(PagedArrayPool<T> *p_page_pool) {
+ array.set_page_pool(p_page_pool);
+ }
+
+ _FORCE_INLINE_ const T &operator[](uint64_t p_index) const {
+ return array[p_index];
+ }
+
+ _FORCE_INLINE_ T &operator[](uint64_t p_index) {
+ return array[p_index];
+ }
+
+ int get_bin_count() {
+ if (array.size() == 0) {
+ return 0;
+ }
+ return bin_limits.size();
+ }
+
+ int get_bin_start(int p_bin) {
+ ERR_FAIL_COND_V(p_bin >= get_bin_count(), ~0U);
+ if ((unsigned int)p_bin == bin_limits.size() - 1) {
+ return 0;
+ }
+ return bin_limits[p_bin + 1] + 1;
+ }
+
+ int get_bin_size(int p_bin) {
+ ERR_FAIL_COND_V(p_bin >= get_bin_count(), 0);
+ if ((unsigned int)p_bin == bin_limits.size() - 1) {
+ return bin_limits[p_bin] + 1;
+ }
+ return bin_limits[p_bin] - bin_limits[p_bin + 1];
+ }
+
+ void reset() {
+ array.reset();
+ bin_limits.clear();
+ bin_limits.push_back(0);
+ }
+
+ BinSortedArray() {
+ bin_limits.push_back(0);
+ }
+
+ virtual ~BinSortedArray() {
+ reset();
+ }
+};
+
+#endif //BIN_SORTED_ARRAY_H
diff --git a/core/templates/command_queue_mt.cpp b/core/templates/command_queue_mt.cpp
index 238bf3975c..04a8095f0b 100644
--- a/core/templates/command_queue_mt.cpp
+++ b/core/templates/command_queue_mt.cpp
@@ -70,35 +70,7 @@ CommandQueueMT::SyncSemaphore *CommandQueueMT::_alloc_sync_sem() {
return &sync_sems[idx];
}
-bool CommandQueueMT::dealloc_one() {
-tryagain:
- if (dealloc_ptr == (write_ptr_and_epoch >> 1)) {
- // The queue is empty
- return false;
- }
-
- uint32_t size = *(uint32_t *)&command_mem[dealloc_ptr];
-
- if (size == 0) {
- // End of command buffer wrap down
- dealloc_ptr = 0;
- goto tryagain;
- }
-
- if (size & 1) {
- // Still used, nothing can be deallocated
- return false;
- }
-
- dealloc_ptr += (size >> 1) + 8;
- return true;
-}
-
CommandQueueMT::CommandQueueMT(bool p_sync) {
- command_mem_size = GLOBAL_DEF_RST("memory/limits/command_queue/multithreading_queue_size_kb", DEFAULT_COMMAND_MEM_SIZE_KB);
- ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/command_queue/multithreading_queue_size_kb", PropertyInfo(Variant::INT, "memory/limits/command_queue/multithreading_queue_size_kb", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"));
- command_mem_size *= 1024;
- command_mem = (uint8_t *)memalloc(command_mem_size);
if (p_sync) {
sync = memnew(Semaphore);
}
@@ -108,5 +80,4 @@ CommandQueueMT::~CommandQueueMT() {
if (sync) {
memdelete(sync);
}
- memfree(command_mem);
}
diff --git a/core/templates/command_queue_mt.h b/core/templates/command_queue_mt.h
index 0012cea72d..519a896ffc 100644
--- a/core/templates/command_queue_mt.h
+++ b/core/templates/command_queue_mt.h
@@ -34,6 +34,8 @@
#include "core/os/memory.h"
#include "core/os/mutex.h"
#include "core/os/semaphore.h"
+#include "core/string/print_string.h"
+#include "core/templates/local_vector.h"
#include "core/templates/simple_type.h"
#include "core/typedefs.h"
@@ -319,7 +321,7 @@ class CommandQueueMT {
DECL_CMD(0)
SPACE_SEP_LIST(DECL_CMD, 15)
- /* comands that return */
+ // Commands that return.
DECL_CMD_RET(0)
SPACE_SEP_LIST(DECL_CMD_RET, 15)
@@ -334,11 +336,7 @@ class CommandQueueMT {
SYNC_SEMAPHORES = 8
};
- uint8_t *command_mem = nullptr;
- uint32_t read_ptr_and_epoch = 0;
- uint32_t write_ptr_and_epoch = 0;
- uint32_t dealloc_ptr = 0;
- uint32_t command_mem_size = 0;
+ LocalVector<uint8_t> command_mem;
SyncSemaphore sync_sems[SYNC_SEMAPHORES];
Mutex mutex;
Semaphore *sync = nullptr;
@@ -346,138 +344,47 @@ class CommandQueueMT {
template <class T>
T *allocate() {
// alloc size is size+T+safeguard
- uint32_t alloc_size = ((sizeof(T) + 8 - 1) & ~(8 - 1)) + 8;
-
- // Assert that the buffer is big enough to hold at least two messages.
- ERR_FAIL_COND_V(alloc_size * 2 + sizeof(uint32_t) > command_mem_size, nullptr);
-
- tryagain:
- uint32_t write_ptr = write_ptr_and_epoch >> 1;
-
- if (write_ptr < dealloc_ptr) {
- // behind dealloc_ptr, check that there is room
- if ((dealloc_ptr - write_ptr) <= alloc_size) {
- // There is no more room, try to deallocate something
- if (dealloc_one()) {
- goto tryagain;
- }
- return nullptr;
- }
- } else {
- // ahead of dealloc_ptr, check that there is room
-
- if ((command_mem_size - write_ptr) < alloc_size + sizeof(uint32_t)) {
- // no room at the end, wrap down;
-
- if (dealloc_ptr == 0) { // don't want write_ptr to become dealloc_ptr
-
- // There is no more room, try to deallocate something
- if (dealloc_one()) {
- goto tryagain;
- }
- return nullptr;
- }
-
- // if this happens, it's a bug
- ERR_FAIL_COND_V((command_mem_size - write_ptr) < 8, nullptr);
- // zero means, wrap to beginning
-
- uint32_t *p = (uint32_t *)&command_mem[write_ptr];
- *p = 1;
- write_ptr_and_epoch = 0 | (1 & ~write_ptr_and_epoch); // Invert epoch.
- // See if we can get the thread to run and clear up some more space while we wait.
- // This is required if alloc_size * 2 + 4 > COMMAND_MEM_SIZE
- if (sync) {
- sync->post();
- }
- goto tryagain;
- }
- }
- // Allocate the size and the 'in use' bit.
- // First bit used to mark if command is still in use (1)
- // or if it has been destroyed and can be deallocated (0).
- uint32_t size = (sizeof(T) + 8 - 1) & ~(8 - 1);
- uint32_t *p = (uint32_t *)&command_mem[write_ptr];
- *p = (size << 1) | 1;
- write_ptr += 8;
- // allocate the command
- T *cmd = memnew_placement(&command_mem[write_ptr], T);
- write_ptr += size;
- write_ptr_and_epoch = (write_ptr << 1) | (write_ptr_and_epoch & 1);
+ uint32_t alloc_size = ((sizeof(T) + 8 - 1) & ~(8 - 1));
+ uint64_t size = command_mem.size();
+ command_mem.resize(size + alloc_size + 8);
+ *(uint64_t *)&command_mem[size] = alloc_size;
+ T *cmd = memnew_placement(&command_mem[size + 8], T);
return cmd;
}
template <class T>
T *allocate_and_lock() {
lock();
- T *ret;
-
- while ((ret = allocate<T>()) == nullptr) {
- unlock();
- // sleep a little until fetch happened and some room is made
- wait_for_flush();
- lock();
- }
-
+ T *ret = allocate<T>();
return ret;
}
- bool flush_one(bool p_lock = true) {
- if (p_lock) {
- lock();
- }
- tryagain:
-
- // tried to read an empty queue
- if (read_ptr_and_epoch == write_ptr_and_epoch) {
- if (p_lock) {
- unlock();
- }
- return false;
- }
-
- uint32_t read_ptr = read_ptr_and_epoch >> 1;
- uint32_t size_ptr = read_ptr;
- uint32_t size = *(uint32_t *)&command_mem[read_ptr] >> 1;
-
- if (size == 0) {
- *(uint32_t *)&command_mem[read_ptr] = 0; // clear in-use bit.
- //end of ringbuffer, wrap
- read_ptr_and_epoch = 0 | (1 & ~read_ptr_and_epoch); // Invert epoch.
- goto tryagain;
- }
-
- read_ptr += 8;
+ void _flush() {
+ lock();
- CommandBase *cmd = reinterpret_cast<CommandBase *>(&command_mem[read_ptr]);
+ uint64_t read_ptr = 0;
+ uint64_t limit = command_mem.size();
- read_ptr += size;
+ while (read_ptr < limit) {
+ uint64_t size = *(uint64_t *)&command_mem[read_ptr];
+ read_ptr += 8;
+ CommandBase *cmd = reinterpret_cast<CommandBase *>(&command_mem[read_ptr]);
- read_ptr_and_epoch = (read_ptr << 1) | (read_ptr_and_epoch & 1);
+ cmd->call(); //execute the function
+ cmd->post(); //release in case it needs sync/ret
+ cmd->~CommandBase(); //should be done, so erase the command
- if (p_lock) {
- unlock();
- }
- cmd->call();
- if (p_lock) {
- lock();
+ read_ptr += size;
}
- cmd->post();
- cmd->~CommandBase();
- *(uint32_t *)&command_mem[size_ptr] &= ~1;
-
- if (p_lock) {
- unlock();
- }
- return true;
+ command_mem.clear();
+ unlock();
}
void lock();
void unlock();
void wait_for_flush();
SyncSemaphore *_alloc_sync_sem();
- bool dealloc_one();
public:
/* NORMAL PUSH COMMANDS */
@@ -492,23 +399,19 @@ public:
DECL_PUSH_AND_SYNC(0)
SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 15)
- void wait_and_flush_one() {
- ERR_FAIL_COND(!sync);
- sync->wait();
- flush_one();
- }
-
_FORCE_INLINE_ void flush_if_pending() {
- if (unlikely(read_ptr_and_epoch != write_ptr_and_epoch)) {
- flush_all();
+ if (unlikely(command_mem.size() > 0)) {
+ _flush();
}
}
void flush_all() {
- //ERR_FAIL_COND(sync);
- lock();
- while (flush_one(false)) {
- }
- unlock();
+ _flush();
+ }
+
+ void wait_and_flush() {
+ ERR_FAIL_COND(!sync);
+ sync->wait();
+ _flush();
}
CommandQueueMT(bool p_sync);
diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h
index c985593473..9b8c0eb528 100644
--- a/core/templates/cowdata.h
+++ b/core/templates/cowdata.h
@@ -49,6 +49,12 @@ class VMap;
SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t)
#endif
+// Silence a false positive warning (see GH-52119).
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wplacement-new"
+#endif
+
template <class T>
class CowData {
template <class TV>
@@ -232,7 +238,7 @@ uint32_t CowData<T>::_copy_on_write() {
uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true);
- new (mem_new - 2, sizeof(uint32_t), "") SafeNumeric<uint32_t>(1); //refcount
+ new (mem_new - 2) SafeNumeric<uint32_t>(1); //refcount
*(mem_new - 1) = current_size; //size
T *_data = (T *)(mem_new);
@@ -286,14 +292,14 @@ Error CowData<T>::resize(int p_size) {
uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true);
ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY);
*(ptr - 1) = 0; //size, currently none
- new (ptr - 2, sizeof(uint32_t), "") SafeNumeric<uint32_t>(1); //refcount
+ new (ptr - 2) SafeNumeric<uint32_t>(1); //refcount
_ptr = (T *)ptr;
} else {
uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true);
ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY);
- new (_ptrnew - 2, sizeof(uint32_t), "") SafeNumeric<uint32_t>(rc); //refcount
+ new (_ptrnew - 2) SafeNumeric<uint32_t>(rc); //refcount
_ptr = (T *)(_ptrnew);
}
@@ -323,7 +329,7 @@ Error CowData<T>::resize(int p_size) {
if (alloc_size != current_alloc_size) {
uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true);
ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY);
- new (_ptrnew - 2, sizeof(uint32_t), "") SafeNumeric<uint32_t>(rc); //refcount
+ new (_ptrnew - 2) SafeNumeric<uint32_t>(rc); //refcount
_ptr = (T *)(_ptrnew);
}
@@ -380,4 +386,8 @@ CowData<T>::~CowData() {
_unref(_ptr);
}
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
#endif // COWDATA_H
diff --git a/core/templates/hash_map.h b/core/templates/hash_map.h
index dc378aed69..1257b54449 100644
--- a/core/templates/hash_map.h
+++ b/core/templates/hash_map.h
@@ -291,7 +291,7 @@ public:
}
/**
- * Same as get, except it can return nullptr when item was not found.
+ * Same as get, except it can return nullptr when item was not found.
* This is mainly used for speed purposes.
*/
@@ -324,7 +324,7 @@ public:
}
/**
- * Same as get, except it can return nullptr when item was not found.
+ * Same as get, except it can return nullptr when item was not found.
* This version is custom, will take a hash and a custom key (that should support operator==()
*/
@@ -443,7 +443,7 @@ public:
/**
* Get the next key to p_key, and the first key if p_key is null.
- * Returns a pointer to the next key if found, nullptr otherwise.
+ * Returns a pointer to the next key if found, nullptr otherwise.
* Adding/Removing elements while iterating will, of course, have unexpected results, don't do it.
*
* Example:
diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h
index 4572b269cf..2e932f9f26 100644
--- a/core/templates/hashfuncs.h
+++ b/core/templates/hashfuncs.h
@@ -95,7 +95,7 @@ static inline uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev = 5381)
if (p_in == 0.0f) {
u.d = 0.0;
} else if (Math::is_nan(p_in)) {
- u.d = Math_NAN;
+ u.d = NAN;
} else {
u.d = p_in;
}
@@ -124,7 +124,7 @@ static inline uint64_t hash_djb2_one_float_64(double p_in, uint64_t p_prev = 538
if (p_in == 0.0f) {
u.d = 0.0;
} else if (Math::is_nan(p_in)) {
- u.d = Math_NAN;
+ u.d = NAN;
} else {
u.d = p_in;
}
diff --git a/core/templates/list.h b/core/templates/list.h
index 010e35eed8..c2e17a2f6f 100644
--- a/core/templates/list.h
+++ b/core/templates/list.h
@@ -135,6 +135,83 @@ public:
_FORCE_INLINE_ Element() {}
};
+ typedef T ValueType;
+
+ struct Iterator {
+ _FORCE_INLINE_ T &operator*() const {
+ return E->get();
+ }
+ _FORCE_INLINE_ T *operator->() const { return &E->get(); }
+ _FORCE_INLINE_ Iterator &operator++() {
+ E = E->next();
+ return *this;
+ }
+ _FORCE_INLINE_ Iterator &operator--() {
+ E = E->prev();
+ return *this;
+ }
+
+ _FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
+ _FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
+
+ Iterator(Element *p_E) { E = p_E; }
+ Iterator() {}
+ Iterator(const Iterator &p_it) { E = p_it.E; }
+
+ private:
+ Element *E = nullptr;
+ };
+
+ struct ConstIterator {
+ _FORCE_INLINE_ const T &operator*() const {
+ return E->get();
+ }
+ _FORCE_INLINE_ const T *operator->() const { return &E->get(); }
+ _FORCE_INLINE_ ConstIterator &operator++() {
+ E = E->next();
+ return *this;
+ }
+ _FORCE_INLINE_ ConstIterator &operator--() {
+ E = E->prev();
+ return *this;
+ }
+
+ _FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; }
+ _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; }
+
+ _FORCE_INLINE_ ConstIterator(const Element *p_E) { E = p_E; }
+ _FORCE_INLINE_ ConstIterator() {}
+ _FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
+
+ private:
+ const Element *E = nullptr;
+ };
+
+ _FORCE_INLINE_ Iterator begin() {
+ return Iterator(front());
+ }
+ _FORCE_INLINE_ Iterator end() {
+ return Iterator(nullptr);
+ }
+
+#if 0
+ //to use when replacing find()
+ _FORCE_INLINE_ Iterator find(const K &p_key) {
+ return Iterator(find(p_key));
+ }
+#endif
+ _FORCE_INLINE_ ConstIterator begin() const {
+ return ConstIterator(front());
+ }
+ _FORCE_INLINE_ ConstIterator end() const {
+ return ConstIterator(nullptr);
+ }
+#if 0
+ //to use when replacing find()
+ _FORCE_INLINE_ ConstIterator find(const K &p_key) const {
+ return ConstIterator(find(p_key));
+ }
+#endif
private:
struct _Data {
Element *first = nullptr;
diff --git a/core/templates/local_vector.h b/core/templates/local_vector.h
index 5f22e08eb8..5704b8f230 100644
--- a/core/templates/local_vector.h
+++ b/core/templates/local_vector.h
@@ -170,7 +170,7 @@ public:
push_back(p_val);
} else {
resize(count + 1);
- for (U i = count; i > p_pos; i--) {
+ for (U i = count - 1; i > p_pos; i--) {
data[i] = data[i - 1];
}
data[p_pos] = p_val;
@@ -178,7 +178,7 @@ public:
}
int64_t find(const T &p_val, U p_from = 0) const {
- for (U i = 0; i < count; i++) {
+ for (U i = p_from; i < count; i++) {
if (data[i] == p_val) {
return int64_t(i);
}
diff --git a/core/templates/map.h b/core/templates/map.h
index 7dfee13d2c..badb407e5d 100644
--- a/core/templates/map.h
+++ b/core/templates/map.h
@@ -33,9 +33,10 @@
#include "core/error/error_macros.h"
#include "core/os/memory.h"
+#include "core/templates/pair.h"
// based on the very nice implementation of rb-trees by:
-// https://web.archive.org/web/20120507164830/http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
+// https://web.archive.org/web/20120507164830/https://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
template <class K, class V, class C = Comparator<K>, class A = DefaultAllocator>
class Map {
@@ -55,11 +56,12 @@ public:
Element *parent = nullptr;
Element *_next = nullptr;
Element *_prev = nullptr;
- K _key;
- V _value;
- //_Data *data;
+ KeyValue<K, V> _data;
public:
+ KeyValue<K, V> &key_value() { return _data; }
+ const KeyValue<K, V> &key_value() const { return _data; }
+
const Element *next() const {
return _next;
}
@@ -73,23 +75,106 @@ public:
return _prev;
}
const K &key() const {
- return _key;
+ return _data.key;
}
V &value() {
- return _value;
+ return _data.value;
}
const V &value() const {
- return _value;
+ return _data.value;
}
V &get() {
- return _value;
+ return _data.value;
}
const V &get() const {
- return _value;
+ return _data.value;
}
- Element() {}
+ Element(const KeyValue<K, V> &p_data) :
+ _data(p_data) {}
};
+ typedef KeyValue<K, V> ValueType;
+
+ struct Iterator {
+ _FORCE_INLINE_ KeyValue<K, V> &operator*() const {
+ return E->key_value();
+ }
+ _FORCE_INLINE_ KeyValue<K, V> *operator->() const { return &E->key_value(); }
+ _FORCE_INLINE_ Iterator &operator++() {
+ E = E->next();
+ return *this;
+ }
+ _FORCE_INLINE_ Iterator &operator--() {
+ E = E->prev();
+ return *this;
+ }
+
+ _FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
+ _FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
+
+ Iterator(Element *p_E) { E = p_E; }
+ Iterator() {}
+ Iterator(const Iterator &p_it) { E = p_it.E; }
+
+ private:
+ Element *E = nullptr;
+ };
+
+ struct ConstIterator {
+ _FORCE_INLINE_ const KeyValue<K, V> &operator*() const {
+ return E->key_value();
+ }
+ _FORCE_INLINE_ const KeyValue<K, V> *operator->() const { return &E->key_value(); }
+ _FORCE_INLINE_ ConstIterator &operator++() {
+ E = E->next();
+ return *this;
+ }
+ _FORCE_INLINE_ ConstIterator &operator--() {
+ E = E->prev();
+ return *this;
+ }
+
+ _FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; }
+ _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; }
+
+ ConstIterator(const Element *p_E) { E = p_E; }
+ ConstIterator() {}
+ ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
+
+ private:
+ const Element *E = nullptr;
+ };
+
+ _FORCE_INLINE_ Iterator begin() {
+ return Iterator(front());
+ }
+ _FORCE_INLINE_ Iterator end() {
+ return Iterator(nullptr);
+ }
+
+#if 0
+ //to use when replacing find()
+ _FORCE_INLINE_ Iterator find(const K &p_key) {
+ return Iterator(find(p_key));
+ }
+#endif
+ _FORCE_INLINE_ void remove(const Iterator &p_iter) {
+ return erase(p_iter.E);
+ }
+
+ _FORCE_INLINE_ ConstIterator begin() const {
+ return ConstIterator(front());
+ }
+ _FORCE_INLINE_ ConstIterator end() const {
+ return ConstIterator(nullptr);
+ }
+
+#if 0
+ //to use when replacing find()
+ _FORCE_INLINE_ ConstIterator find(const K &p_key) const {
+ return ConstIterator(find(p_key));
+ }
+#endif
private:
struct _Data {
Element *_root = nullptr;
@@ -107,7 +192,7 @@ private:
}
void _create_root() {
- _root = memnew_allocator(Element, A);
+ _root = memnew_allocator(Element(KeyValue<K, V>(K(), V())), A);
_root->parent = _root->left = _root->right = _nil;
_root->color = BLACK;
}
@@ -216,9 +301,9 @@ private:
C less;
while (node != _data._nil) {
- if (less(p_key, node->_key)) {
+ if (less(p_key, node->_data.key)) {
node = node->left;
- } else if (less(node->_key, p_key)) {
+ } else if (less(node->_data.key, p_key)) {
node = node->right;
} else {
return node; // found
@@ -236,9 +321,9 @@ private:
while (node != _data._nil) {
prev = node;
- if (less(p_key, node->_key)) {
+ if (less(p_key, node->_data.key)) {
node = node->left;
- } else if (less(node->_key, p_key)) {
+ } else if (less(node->_data.key, p_key)) {
node = node->right;
} else {
return node; // found
@@ -249,7 +334,7 @@ private:
return nullptr; // tree empty
}
- if (less(p_key, prev->_key)) {
+ if (less(p_key, prev->_data.key)) {
prev = prev->_prev;
}
@@ -312,25 +397,25 @@ private:
while (node != _data._nil) {
new_parent = node;
- if (less(p_key, node->_key)) {
+ if (less(p_key, node->_data.key)) {
node = node->left;
- } else if (less(node->_key, p_key)) {
+ } else if (less(node->_data.key, p_key)) {
node = node->right;
} else {
- node->_value = p_value;
+ node->_data.value = p_value;
return node; // Return existing node with new value
}
}
- Element *new_node = memnew_allocator(Element, A);
+ typedef KeyValue<K, V> KV;
+ Element *new_node = memnew_allocator(Element(KV(p_key, p_value)), A);
new_node->parent = new_parent;
new_node->right = _data._nil;
new_node->left = _data._nil;
- new_node->_key = p_key;
- new_node->_value = p_value;
+
//new_node->data=_data;
- if (new_parent == _data._root || less(p_key, new_parent->_key)) {
+ if (new_parent == _data._root || less(p_key, new_parent->_data.key)) {
new_parent->left = new_node;
} else {
new_parent->right = new_node;
@@ -575,7 +660,7 @@ public:
CRASH_COND(!_data._root);
const Element *e = find(p_key);
CRASH_COND(!e);
- return e->_value;
+ return e->_data.value;
}
V &operator[](const K &p_key) {
@@ -588,7 +673,7 @@ public:
e = insert(p_key, V());
}
- return e->_value;
+ return e->_data.value;
}
Element *front() const {
diff --git a/core/templates/oa_hash_map.h b/core/templates/oa_hash_map.h
index 2c7c64cd78..025cc30db4 100644
--- a/core/templates/oa_hash_map.h
+++ b/core/templates/oa_hash_map.h
@@ -231,7 +231,7 @@ public:
/**
* returns true if the value was found, false otherwise.
*
- * if r_data is not nullptr then the value will be written to the object
+ * if r_data is not nullptr then the value will be written to the object
* it points to.
*/
bool lookup(const TKey &p_key, TValue &r_data) const {
@@ -249,7 +249,7 @@ public:
/**
* returns true if the value was found, false otherwise.
*
- * if r_data is not nullptr then the value will be written to the object
+ * if r_data is not nullptr then the value will be written to the object
* it points to.
*/
TValue *lookup_ptr(const TKey &p_key) const {
diff --git a/core/templates/paged_allocator.h b/core/templates/paged_allocator.h
index 481289309f..dfc885c6eb 100644
--- a/core/templates/paged_allocator.h
+++ b/core/templates/paged_allocator.h
@@ -50,7 +50,8 @@ class PagedAllocator {
SpinLock spin_lock;
public:
- T *alloc() {
+ template <class... Args>
+ T *alloc(const Args &&...p_args) {
if (thread_safe) {
spin_lock.lock();
}
@@ -75,7 +76,7 @@ public:
if (thread_safe) {
spin_lock.unlock();
}
- memnew_placement(alloc, T);
+ memnew_placement(alloc, T(p_args...));
return alloc;
}
diff --git a/core/templates/pair.h b/core/templates/pair.h
index bc1a764694..e30ee8bc56 100644
--- a/core/templates/pair.h
+++ b/core/templates/pair.h
@@ -31,6 +31,8 @@
#ifndef PAIR_H
#define PAIR_H
+#include "core/typedefs.h"
+
template <class F, class S>
struct Pair {
F first;
@@ -60,7 +62,43 @@ bool operator!=(const Pair<F, S> &pair, const Pair<F, S> &other) {
template <class F, class S>
struct PairSort {
bool operator()(const Pair<F, S> &A, const Pair<F, S> &B) const {
- return A.first < B.first;
+ if (A.first != B.first) {
+ return A.first < B.first;
+ }
+ return A.second < B.second;
+ }
+};
+
+template <class K, class V>
+struct KeyValue {
+ const K key;
+ V value;
+
+ void operator=(const KeyValue &p_kv) = delete;
+ _FORCE_INLINE_ KeyValue(const KeyValue &p_kv) :
+ key(p_kv.key),
+ value(p_kv.value) {
+ }
+ _FORCE_INLINE_ KeyValue(const K &p_key, const V &p_value) :
+ key(p_key),
+ value(p_value) {
+ }
+};
+
+template <class K, class V>
+bool operator==(const KeyValue<K, V> &pair, const KeyValue<K, V> &other) {
+ return (pair.key == other.key) && (pair.value == other.value);
+}
+
+template <class K, class V>
+bool operator!=(const KeyValue<K, V> &pair, const KeyValue<K, V> &other) {
+ return (pair.key != other.key) || (pair.value != other.value);
+}
+
+template <class K, class V>
+struct KeyValueSort {
+ bool operator()(const KeyValue<K, V> &A, const KeyValue<K, V> &B) const {
+ return A.key < B.key;
}
};
diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h
index c4aa93c394..e947d2211c 100644
--- a/core/templates/rid_owner.h
+++ b/core/templates/rid_owner.h
@@ -53,14 +53,16 @@ protected:
return rid;
}
- static uint64_t _gen_id() {
- return base_id.increment();
- }
-
static RID _gen_rid() {
return _make_from_id(_gen_id());
}
+ friend struct VariantUtilityFunctions;
+
+ static uint64_t _gen_id() {
+ return base_id.increment();
+ }
+
public:
virtual ~RID_AllocBase() {}
};
@@ -79,7 +81,7 @@ class RID_Alloc : public RID_AllocBase {
SpinLock spin_lock;
- _FORCE_INLINE_ RID _allocate_rid(const T *p_initializer) {
+ _FORCE_INLINE_ RID _allocate_rid() {
if (THREAD_SAFE) {
spin_lock.lock();
}
@@ -101,7 +103,7 @@ class RID_Alloc : public RID_AllocBase {
//initialize
for (uint32_t i = 0; i < elements_in_chunk; i++) {
- //dont initialize chunk
+ // Don't initialize chunk.
validator_chunks[chunk_count][i] = 0xFFFFFFFF;
free_list_chunks[chunk_count][i] = alloc_count + i;
}
@@ -114,11 +116,6 @@ class RID_Alloc : public RID_AllocBase {
uint32_t free_chunk = free_index / elements_in_chunk;
uint32_t free_element = free_index % elements_in_chunk;
- if (p_initializer) {
- T *ptr = &chunks[free_chunk][free_element];
- memnew_placement(ptr, T(*p_initializer));
- }
-
uint32_t validator = (uint32_t)(_gen_id() & 0x7FFFFFFF);
uint64_t id = validator;
id <<= 32;
@@ -126,9 +123,7 @@ class RID_Alloc : public RID_AllocBase {
validator_chunks[free_chunk][free_element] = validator;
- if (!p_initializer) {
- validator_chunks[free_chunk][free_element] |= 0x80000000; //mark uninitialized bit
- }
+ validator_chunks[free_chunk][free_element] |= 0x80000000; //mark uninitialized bit
alloc_count++;
@@ -140,13 +135,20 @@ class RID_Alloc : public RID_AllocBase {
}
public:
+ RID make_rid() {
+ RID rid = _allocate_rid();
+ initialize_rid(rid);
+ return rid;
+ }
RID make_rid(const T &p_value) {
- return _allocate_rid(&p_value);
+ RID rid = _allocate_rid();
+ initialize_rid(rid, p_value);
+ return rid;
}
//allocate but don't initialize, use initialize_rid afterwards
RID allocate_rid() {
- return _allocate_rid(nullptr);
+ return _allocate_rid();
}
_FORCE_INLINE_ T *getornull(const RID &p_rid, bool p_initialize = false) {
@@ -193,7 +195,7 @@ public:
if (THREAD_SAFE) {
spin_lock.unlock();
}
- if (validator_chunks[idx_chunk][idx_element] & 0x80000000) {
+ if ((validator_chunks[idx_chunk][idx_element] & 0x80000000) && validator_chunks[idx_chunk][idx_element] != 0xFFFFFFFF) {
ERR_FAIL_V_MSG(nullptr, "Attempting to use an uninitialized RID");
}
return nullptr;
@@ -207,6 +209,11 @@ public:
return ptr;
}
+ void initialize_rid(RID p_rid) {
+ T *mem = getornull(p_rid, true);
+ ERR_FAIL_COND(!mem);
+ memnew_placement(mem, T);
+ }
void initialize_rid(RID p_rid, const T &p_value) {
T *mem = getornull(p_rid, true);
ERR_FAIL_COND(!mem);
@@ -333,7 +340,7 @@ public:
description = p_descrption;
}
- RID_Alloc(uint32_t p_target_chunk_byte_size = 4096) {
+ RID_Alloc(uint32_t p_target_chunk_byte_size = 65536) {
elements_in_chunk = sizeof(T) > p_target_chunk_byte_size ? 1 : (p_target_chunk_byte_size / sizeof(T));
}
@@ -351,6 +358,9 @@ public:
for (size_t i = 0; i < max_alloc; i++) {
uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
+ if (validator & 0x80000000) {
+ continue; //uninitialized
+ }
if (validator != 0xFFFFFFFF) {
chunks[i / elements_in_chunk][i % elements_in_chunk].~T();
}
@@ -431,7 +441,7 @@ public:
alloc.set_description(p_descrption);
}
- RID_PtrOwner(uint32_t p_target_chunk_byte_size = 4096) :
+ RID_PtrOwner(uint32_t p_target_chunk_byte_size = 65536) :
alloc(p_target_chunk_byte_size) {}
};
@@ -440,6 +450,9 @@ class RID_Owner {
RID_Alloc<T, THREAD_SAFE> alloc;
public:
+ _FORCE_INLINE_ RID make_rid() {
+ return alloc.make_rid();
+ }
_FORCE_INLINE_ RID make_rid(const T &p_ptr) {
return alloc.make_rid(p_ptr);
}
@@ -448,6 +461,10 @@ public:
return alloc.allocate_rid();
}
+ _FORCE_INLINE_ void initialize_rid(RID p_rid) {
+ alloc.initialize_rid(p_rid);
+ }
+
_FORCE_INLINE_ void initialize_rid(RID p_rid, const T &p_ptr) {
alloc.initialize_rid(p_rid, p_ptr);
}
@@ -483,7 +500,7 @@ public:
void set_description(const char *p_descrption) {
alloc.set_description(p_descrption);
}
- RID_Owner(uint32_t p_target_chunk_byte_size = 4096) :
+ RID_Owner(uint32_t p_target_chunk_byte_size = 65536) :
alloc(p_target_chunk_byte_size) {}
};
diff --git a/core/templates/safe_list.h b/core/templates/safe_list.h
new file mode 100644
index 0000000000..d8f010663b
--- /dev/null
+++ b/core/templates/safe_list.h
@@ -0,0 +1,375 @@
+/*************************************************************************/
+/* safe_list.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SAFE_LIST_H
+#define SAFE_LIST_H
+
+#include "core/os/memory.h"
+#include "core/typedefs.h"
+#include <functional>
+
+#if !defined(NO_THREADS)
+
+#include <atomic>
+#include <type_traits>
+
+// Design goals for these classes:
+// - Accessing this list with an iterator will never result in a use-after free,
+// even if the element being accessed has been logically removed from the list on
+// another thread.
+// - Logical deletion from the list will not result in deallocation at that time,
+// instead the node will be deallocated at a later time when it is safe to do so.
+// - No blocking synchronization primitives will be used.
+
+// This is used in very specific areas of the engine where it's critical that these guarantees are held.
+
+template <class T, class A = DefaultAllocator>
+class SafeList {
+ struct SafeListNode {
+ std::atomic<SafeListNode *> next = nullptr;
+
+ // If the node is logically deleted, this pointer will typically point
+ // to the previous list item in time that was also logically deleted.
+ std::atomic<SafeListNode *> graveyard_next = nullptr;
+
+ std::function<void(T)> deletion_fn = [](T t) { return; };
+
+ T val;
+ };
+
+ static_assert(std::atomic<T>::is_always_lock_free);
+
+ std::atomic<SafeListNode *> head = nullptr;
+ std::atomic<SafeListNode *> graveyard_head = nullptr;
+
+ std::atomic_uint active_iterator_count = 0;
+
+public:
+ class Iterator {
+ friend class SafeList;
+
+ SafeListNode *cursor;
+ SafeList *list;
+
+ Iterator(SafeListNode *p_cursor, SafeList *p_list) :
+ cursor(p_cursor), list(p_list) {
+ list->active_iterator_count++;
+ }
+
+ public:
+ Iterator(const Iterator &p_other) :
+ cursor(p_other.cursor), list(p_other.list) {
+ list->active_iterator_count++;
+ }
+
+ ~Iterator() {
+ list->active_iterator_count--;
+ }
+
+ public:
+ T &operator*() {
+ return cursor->val;
+ }
+
+ Iterator &operator++() {
+ cursor = cursor->next;
+ return *this;
+ }
+
+ // These two operators are mostly useful for comparisons to nullptr.
+ bool operator==(const void *p_other) const {
+ return cursor == p_other;
+ }
+
+ bool operator!=(const void *p_other) const {
+ return cursor != p_other;
+ }
+
+ // These two allow easy range-based for loops.
+ bool operator==(const Iterator &p_other) const {
+ return cursor == p_other.cursor;
+ }
+
+ bool operator!=(const Iterator &p_other) const {
+ return cursor != p_other.cursor;
+ }
+ };
+
+public:
+ // Calling this will cause an allocation.
+ void insert(T p_value) {
+ SafeListNode *new_node = memnew_allocator(SafeListNode, A);
+ new_node->val = p_value;
+ SafeListNode *expected_head = nullptr;
+ do {
+ expected_head = head.load();
+ new_node->next.store(expected_head);
+ } while (!head.compare_exchange_strong(/* expected= */ expected_head, /* new= */ new_node));
+ }
+
+ Iterator find(T p_value) {
+ for (Iterator it = begin(); it != end(); ++it) {
+ if (*it == p_value) {
+ return it;
+ }
+ }
+ return end();
+ }
+
+ void erase(T p_value, std::function<void(T)> p_deletion_fn) {
+ Iterator tmp = find(p_value);
+ erase(tmp, p_deletion_fn);
+ }
+
+ void erase(T p_value) {
+ Iterator tmp = find(p_value);
+ erase(tmp, [](T t) { return; });
+ }
+
+ void erase(Iterator &p_iterator, std::function<void(T)> p_deletion_fn) {
+ p_iterator.cursor->deletion_fn = p_deletion_fn;
+ erase(p_iterator);
+ }
+
+ void erase(Iterator &p_iterator) {
+ if (find(p_iterator.cursor->val) == nullptr) {
+ // Not in the list, nothing to do.
+ return;
+ }
+ // First, remove the node from the list.
+ while (true) {
+ Iterator prev = begin();
+ SafeListNode *expected_head = prev.cursor;
+ for (; prev != end(); ++prev) {
+ if (prev.cursor && prev.cursor->next == p_iterator.cursor) {
+ break;
+ }
+ }
+ if (prev != end()) {
+ // There exists a node before this.
+ prev.cursor->next.store(p_iterator.cursor->next.load());
+ // Done.
+ break;
+ } else {
+ if (head.compare_exchange_strong(/* expected= */ expected_head, /* new= */ p_iterator.cursor->next.load())) {
+ // Successfully reassigned the head pointer before another thread changed it to something else.
+ break;
+ }
+ // Fall through upon failure, try again.
+ }
+ }
+ // Then queue it for deletion by putting it in the node graveyard.
+ // Don't touch `next` because an iterator might still be pointing at this node.
+ SafeListNode *expected_head = nullptr;
+ do {
+ expected_head = graveyard_head.load();
+ p_iterator.cursor->graveyard_next.store(expected_head);
+ } while (!graveyard_head.compare_exchange_strong(/* expected= */ expected_head, /* new= */ p_iterator.cursor));
+ }
+
+ Iterator begin() {
+ return Iterator(head.load(), this);
+ }
+
+ Iterator end() {
+ return Iterator(nullptr, this);
+ }
+
+ // Calling this will cause zero to many deallocations.
+ void maybe_cleanup() {
+ SafeListNode *cursor = nullptr;
+ SafeListNode *new_graveyard_head = nullptr;
+ do {
+ // The access order here is theoretically important.
+ cursor = graveyard_head.load();
+ if (active_iterator_count.load() != 0) {
+ // It's not safe to clean up with an active iterator, because that iterator
+ // could be pointing to an element that we want to delete.
+ return;
+ }
+ // Any iterator created after this point will never point to a deleted node.
+ // Swap it out with the current graveyard head.
+ } while (!graveyard_head.compare_exchange_strong(/* expected= */ cursor, /* new= */ new_graveyard_head));
+ // Our graveyard list is now unreachable by any active iterators,
+ // detached from the main graveyard head and ready for deletion.
+ while (cursor) {
+ SafeListNode *tmp = cursor;
+ cursor = cursor->graveyard_next;
+ tmp->deletion_fn(tmp->val);
+ memdelete_allocator<SafeListNode, A>(tmp);
+ }
+ }
+};
+
+#else // NO_THREADS
+
+// Effectively the same structure without the atomics. It's probably possible to simplify it but the semantics shouldn't differ greatly.
+template <class T, class A = DefaultAllocator>
+class SafeList {
+ struct SafeListNode {
+ SafeListNode *next = nullptr;
+
+ // If the node is logically deleted, this pointer will typically point to the previous list item in time that was also logically deleted.
+ SafeListNode *graveyard_next = nullptr;
+
+ std::function<void(T)> deletion_fn = [](T t) { return; };
+
+ T val;
+ };
+
+ SafeListNode *head = nullptr;
+ SafeListNode *graveyard_head = nullptr;
+
+ unsigned int active_iterator_count = 0;
+
+public:
+ class Iterator {
+ friend class SafeList;
+
+ SafeListNode *cursor;
+ SafeList *list;
+
+ public:
+ Iterator(SafeListNode *p_cursor, SafeList *p_list) :
+ cursor(p_cursor), list(p_list) {
+ list->active_iterator_count++;
+ }
+
+ ~Iterator() {
+ list->active_iterator_count--;
+ }
+
+ T &operator*() {
+ return cursor->val;
+ }
+
+ Iterator &operator++() {
+ cursor = cursor->next;
+ return *this;
+ }
+
+ // These two operators are mostly useful for comparisons to nullptr.
+ bool operator==(const void *p_other) const {
+ return cursor == p_other;
+ }
+
+ bool operator!=(const void *p_other) const {
+ return cursor != p_other;
+ }
+
+ // These two allow easy range-based for loops.
+ bool operator==(const Iterator &p_other) const {
+ return cursor == p_other.cursor;
+ }
+
+ bool operator!=(const Iterator &p_other) const {
+ return cursor != p_other.cursor;
+ }
+ };
+
+public:
+ // Calling this will cause an allocation.
+ void insert(T p_value) {
+ SafeListNode *new_node = memnew_allocator(SafeListNode, A);
+ new_node->val = p_value;
+ new_node->next = head;
+ head = new_node;
+ }
+
+ Iterator find(T p_value) {
+ for (Iterator it = begin(); it != end(); ++it) {
+ if (*it == p_value) {
+ return it;
+ }
+ }
+ return end();
+ }
+
+ void erase(T p_value, std::function<void(T)> p_deletion_fn) {
+ erase(find(p_value), p_deletion_fn);
+ }
+
+ void erase(T p_value) {
+ erase(find(p_value), [](T t) { return; });
+ }
+
+ void erase(Iterator p_iterator, std::function<void(T)> p_deletion_fn) {
+ p_iterator.cursor->deletion_fn = p_deletion_fn;
+ erase(p_iterator);
+ }
+
+ void erase(Iterator p_iterator) {
+ Iterator prev = begin();
+ for (; prev != end(); ++prev) {
+ if (prev.cursor && prev.cursor->next == p_iterator.cursor) {
+ break;
+ }
+ }
+ if (prev == end()) {
+ // Not in the list, nothing to do.
+ return;
+ }
+ // First, remove the node from the list.
+ prev.cursor->next = p_iterator.cursor->next;
+
+ // Then queue it for deletion by putting it in the node graveyard. Don't touch `next` because an iterator might still be pointing at this node.
+ p_iterator.cursor->graveyard_next = graveyard_head;
+ graveyard_head = p_iterator.cursor;
+ }
+
+ Iterator begin() {
+ return Iterator(head, this);
+ }
+
+ Iterator end() {
+ return Iterator(nullptr, this);
+ }
+
+ // Calling this will cause zero to many deallocations.
+ void maybe_cleanup() {
+ SafeListNode *cursor = graveyard_head;
+ if (active_iterator_count != 0) {
+ // It's not safe to clean up with an active iterator, because that iterator could be pointing to an element that we want to delete.
+ return;
+ }
+ graveyard_head = nullptr;
+ // Our graveyard list is now unreachable by any active iterators, detached from the main graveyard head and ready for deletion.
+ while (cursor) {
+ SafeListNode *tmp = cursor;
+ cursor = cursor->next;
+ tmp->deletion_fn(tmp->val);
+ memdelete_allocator<SafeListNode, A>(tmp);
+ }
+ }
+};
+
+#endif
+
+#endif // SAFE_LIST_H
diff --git a/core/templates/set.h b/core/templates/set.h
index 3036ecf27d..0a80ceefb5 100644
--- a/core/templates/set.h
+++ b/core/templates/set.h
@@ -35,7 +35,7 @@
#include "core/typedefs.h"
// based on the very nice implementation of rb-trees by:
-// https://web.archive.org/web/20120507164830/http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
+// https://web.archive.org/web/20120507164830/https://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
template <class T, class C = Comparator<T>, class A = DefaultAllocator>
class Set {
@@ -71,12 +71,94 @@ public:
Element *prev() {
return _prev;
}
+ T &get() {
+ return value;
+ }
const T &get() const {
return value;
};
Element() {}
};
+ typedef T ValueType;
+
+ struct Iterator {
+ _FORCE_INLINE_ T &operator*() const {
+ return E->get();
+ }
+ _FORCE_INLINE_ T *operator->() const { return &E->get(); }
+ _FORCE_INLINE_ Iterator &operator++() {
+ E = E->next();
+ return *this;
+ }
+ _FORCE_INLINE_ Iterator &operator--() {
+ E = E->prev();
+ return *this;
+ }
+
+ _FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
+ _FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
+
+ Iterator(Element *p_E) { E = p_E; }
+ Iterator() {}
+ Iterator(const Iterator &p_it) { E = p_it.E; }
+
+ private:
+ Element *E = nullptr;
+ };
+
+ struct ConstIterator {
+ _FORCE_INLINE_ const T &operator*() const {
+ return E->get();
+ }
+ _FORCE_INLINE_ const T *operator->() const { return &E->get(); }
+ _FORCE_INLINE_ ConstIterator &operator++() {
+ E = E->next();
+ return *this;
+ }
+ _FORCE_INLINE_ ConstIterator &operator--() {
+ E = E->prev();
+ return *this;
+ }
+
+ _FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; }
+ _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; }
+
+ _FORCE_INLINE_ ConstIterator(const Element *p_E) { E = p_E; }
+ _FORCE_INLINE_ ConstIterator() {}
+ _FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
+
+ private:
+ const Element *E = nullptr;
+ };
+
+ _FORCE_INLINE_ Iterator begin() {
+ return Iterator(front());
+ }
+ _FORCE_INLINE_ Iterator end() {
+ return Iterator(nullptr);
+ }
+
+#if 0
+ //to use when replacing find()
+ _FORCE_INLINE_ Iterator find(const K &p_key) {
+ return Iterator(find(p_key));
+ }
+#endif
+
+ _FORCE_INLINE_ ConstIterator begin() const {
+ return ConstIterator(front());
+ }
+ _FORCE_INLINE_ ConstIterator end() const {
+ return ConstIterator(nullptr);
+ }
+
+#if 0
+ //to use when replacing find()
+ _FORCE_INLINE_ ConstIterator find(const K &p_key) const {
+ return ConstIterator(find(p_key));
+ }
+#endif
private:
struct _Data {
Element *_root = nullptr;
diff --git a/core/templates/thread_work_pool.h b/core/templates/thread_work_pool.h
index 9f7a692cc5..b242648bc8 100644
--- a/core/templates/thread_work_pool.h
+++ b/core/templates/thread_work_pool.h
@@ -105,7 +105,7 @@ public:
}
bool is_done_dispatching() const {
- ERR_FAIL_COND_V(current_work == nullptr, false);
+ ERR_FAIL_COND_V(current_work == nullptr, true);
return index.load(std::memory_order_acquire) >= current_work->max_elements;
}
diff --git a/core/templates/vector.h b/core/templates/vector.h
index dae8874a87..2600604eb7 100644
--- a/core/templates/vector.h
+++ b/core/templates/vector.h
@@ -92,7 +92,7 @@ public:
void append_array(Vector<T> p_other);
- bool has(const T &p_val) {
+ bool has(const T &p_val) const {
return find(p_val, 0) != -1;
}
@@ -187,6 +187,70 @@ public:
return false;
}
+ struct Iterator {
+ _FORCE_INLINE_ T &operator*() const {
+ return *elem_ptr;
+ }
+ _FORCE_INLINE_ T *operator->() const { return elem_ptr; }
+ _FORCE_INLINE_ Iterator &operator++() {
+ elem_ptr++;
+ return *this;
+ }
+ _FORCE_INLINE_ Iterator &operator--() {
+ elem_ptr--;
+ return *this;
+ }
+
+ _FORCE_INLINE_ bool operator==(const Iterator &b) const { return elem_ptr == b.elem_ptr; }
+ _FORCE_INLINE_ bool operator!=(const Iterator &b) const { return elem_ptr != b.elem_ptr; }
+
+ Iterator(T *p_ptr) { elem_ptr = p_ptr; }
+ Iterator() {}
+ Iterator(const Iterator &p_it) { elem_ptr = p_it.elem_ptr; }
+
+ private:
+ T *elem_ptr = nullptr;
+ };
+
+ struct ConstIterator {
+ _FORCE_INLINE_ const T &operator*() const {
+ return *elem_ptr;
+ }
+ _FORCE_INLINE_ const T *operator->() const { return elem_ptr; }
+ _FORCE_INLINE_ ConstIterator &operator++() {
+ elem_ptr++;
+ return *this;
+ }
+ _FORCE_INLINE_ ConstIterator &operator--() {
+ elem_ptr--;
+ return *this;
+ }
+
+ _FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return elem_ptr == b.elem_ptr; }
+ _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return elem_ptr != b.elem_ptr; }
+
+ ConstIterator(const T *p_ptr) { elem_ptr = p_ptr; }
+ ConstIterator() {}
+ ConstIterator(const ConstIterator &p_it) { elem_ptr = p_it.elem_ptr; }
+
+ private:
+ const T *elem_ptr = nullptr;
+ };
+
+ _FORCE_INLINE_ Iterator begin() {
+ return Iterator(ptrw());
+ }
+ _FORCE_INLINE_ Iterator end() {
+ return Iterator(ptrw() + size());
+ }
+
+ _FORCE_INLINE_ ConstIterator begin() const {
+ return ConstIterator(ptr());
+ }
+ _FORCE_INLINE_ ConstIterator end() const {
+ return ConstIterator(ptr() + size());
+ }
+
_FORCE_INLINE_ Vector() {}
_FORCE_INLINE_ Vector(const Vector &p_from) { _cowdata._ref(p_from._cowdata); }
diff --git a/core/typedefs.h b/core/typedefs.h
index cdbfb34e56..dde254af23 100644
--- a/core/typedefs.h
+++ b/core/typedefs.h
@@ -281,4 +281,11 @@ struct BuildIndexSequence<0, Is...> : IndexSequence<Is...> {};
#define DEBUG_METHODS_ENABLED
#endif
+// Macro GD_IS_DEFINED() allows to check if a macro is defined. It needs to be defined to anything (say 1) to work.
+#define __GDARG_PLACEHOLDER_1 0,
+#define __gd_take_second_arg(__ignored, val, ...) val
+#define ____gd_is_defined(arg1_or_junk) __gd_take_second_arg(arg1_or_junk 1, 0)
+#define ___gd_is_defined(val) ____gd_is_defined(__GDARG_PLACEHOLDER_##val)
+#define GD_IS_DEFINED(x) ___gd_is_defined(x)
+
#endif // TYPEDEFS_H
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index 09cf785390..8373cbd4e8 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -203,9 +203,9 @@ Error Array::resize(int p_new_size) {
return _p->array.resize(p_new_size);
}
-void Array::insert(int p_pos, const Variant &p_value) {
- ERR_FAIL_COND(!_p->typed.validate(p_value, "insert"));
- _p->array.insert(p_pos, p_value);
+Error Array::insert(int p_pos, const Variant &p_value) {
+ ERR_FAIL_COND_V(!_p->typed.validate(p_value, "insert"), ERR_INVALID_PARAMETER);
+ return _p->array.insert(p_pos, p_value);
}
void Array::fill(const Variant &p_value) {
@@ -535,8 +535,8 @@ void Array::push_front(const Variant &p_value) {
Variant Array::pop_back() {
if (!_p->array.is_empty()) {
- int n = _p->array.size() - 1;
- Variant ret = _p->array.get(n);
+ const int n = _p->array.size() - 1;
+ const Variant ret = _p->array.get(n);
_p->array.resize(n);
return ret;
}
@@ -545,13 +545,38 @@ Variant Array::pop_back() {
Variant Array::pop_front() {
if (!_p->array.is_empty()) {
- Variant ret = _p->array.get(0);
+ const Variant ret = _p->array.get(0);
_p->array.remove(0);
return ret;
}
return Variant();
}
+Variant Array::pop_at(int p_pos) {
+ if (_p->array.is_empty()) {
+ // Return `null` without printing an error to mimic `pop_back()` and `pop_front()` behavior.
+ return Variant();
+ }
+
+ if (p_pos < 0) {
+ // Relative offset from the end
+ p_pos = _p->array.size() + p_pos;
+ }
+
+ ERR_FAIL_INDEX_V_MSG(
+ p_pos,
+ _p->array.size(),
+ Variant(),
+ vformat(
+ "The calculated index %s is out of bounds (the array has %s elements). Leaving the array untouched and returning `null`.",
+ p_pos,
+ _p->array.size()));
+
+ const Variant ret = _p->array.get(p_pos);
+ _p->array.remove(p_pos);
+ return ret;
+}
+
Variant Array::min() const {
Variant minval;
for (int i = 0; i < size(); i++) {
diff --git a/core/variant/array.h b/core/variant/array.h
index 540dcb1f4e..4a1b25c4a9 100644
--- a/core/variant/array.h
+++ b/core/variant/array.h
@@ -72,7 +72,7 @@ public:
void append_array(const Array &p_array);
Error resize(int p_new_size);
- void insert(int p_pos, const Variant &p_value);
+ Error insert(int p_pos, const Variant &p_value);
void remove(int p_pos);
void fill(const Variant &p_value);
@@ -97,6 +97,7 @@ public:
void push_front(const Variant &p_value);
Variant pop_back();
Variant pop_front();
+ Variant pop_at(int p_pos);
Array duplicate(bool p_deep = false) const;
diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h
index 830e0a5cbd..3b2c837096 100644
--- a/core/variant/binder_common.h
+++ b/core/variant/binder_common.h
@@ -31,7 +31,9 @@
#ifndef BINDER_COMMON_H
#define BINDER_COMMON_H
+#include "core/input/input_enums.h"
#include "core/object/object.h"
+#include "core/os/keyboard.h"
#include "core/templates/list.h"
#include "core/templates/simple_type.h"
#include "core/typedefs.h"
@@ -68,16 +70,17 @@ struct VariantCaster<const T &> {
template <> \
struct VariantCaster<m_enum> { \
static _FORCE_INLINE_ m_enum cast(const Variant &p_variant) { \
- return (m_enum)p_variant.operator int(); \
+ return (m_enum)p_variant.operator int64_t(); \
} \
}; \
template <> \
struct PtrToArg<m_enum> { \
_FORCE_INLINE_ static m_enum convert(const void *p_ptr) { \
- return m_enum(*reinterpret_cast<const int *>(p_ptr)); \
+ return m_enum(*reinterpret_cast<const int64_t *>(p_ptr)); \
} \
+ typedef int64_t EncodeT; \
_FORCE_INLINE_ static void encode(m_enum p_val, const void *p_ptr) { \
- *(int *)p_ptr = p_val; \
+ *(int64_t *)p_ptr = p_val; \
} \
};
@@ -90,9 +93,18 @@ VARIANT_ENUM_CAST(Error);
VARIANT_ENUM_CAST(Side);
VARIANT_ENUM_CAST(ClockDirection);
VARIANT_ENUM_CAST(Corner);
+VARIANT_ENUM_CAST(HatDir);
+VARIANT_ENUM_CAST(HatMask);
+VARIANT_ENUM_CAST(JoyAxis);
+VARIANT_ENUM_CAST(JoyButton);
+VARIANT_ENUM_CAST(Key);
+VARIANT_ENUM_CAST(KeyModifierMask);
+VARIANT_ENUM_CAST(MIDIMessage);
+VARIANT_ENUM_CAST(MouseButton);
VARIANT_ENUM_CAST(Orientation);
VARIANT_ENUM_CAST(HAlign);
VARIANT_ENUM_CAST(VAlign);
+VARIANT_ENUM_CAST(InlineAlign);
VARIANT_ENUM_CAST(PropertyHint);
VARIANT_ENUM_CAST(PropertyUsageFlags);
VARIANT_ENUM_CAST(Variant::Type);
@@ -110,6 +122,7 @@ struct PtrToArg<char32_t> {
_FORCE_INLINE_ static char32_t convert(const void *p_ptr) {
return char32_t(*reinterpret_cast<const int *>(p_ptr));
}
+ typedef int64_t EncodeT;
_FORCE_INLINE_ static void encode(char32_t p_val, const void *p_ptr) {
*(int *)p_ptr = p_val;
}
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index 5c87042f6b..f487e718f4 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -33,7 +33,7 @@
#include "callable_bind.h"
#include "core/object/message_queue.h"
#include "core/object/object.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
#include "core/object/script_language.h"
void Callable::call_deferred(const Variant **p_arguments, int p_argcount) const {
@@ -89,6 +89,10 @@ Callable Callable::unbind(int p_argcount) const {
return Callable(memnew(CallableCustomUnbind(*this, p_argcount)));
}
+bool Callable::is_valid() const {
+ return get_object() && (is_custom() || get_object()->has_method(get_method()));
+}
+
Object *Callable::get_object() const {
if (is_null()) {
return nullptr;
@@ -403,8 +407,8 @@ Array Signal::get_connections() const {
object->get_signal_connection_list(name, &connections);
Array arr;
- for (List<Object::Connection>::Element *E = connections.front(); E; E = E->next()) {
- arr.push_back(E->get());
+ for (const Object::Connection &E : connections) {
+ arr.push_back(E);
}
return arr;
}
diff --git a/core/variant/callable.h b/core/variant/callable.h
index 20d0804292..52094af3aa 100644
--- a/core/variant/callable.h
+++ b/core/variant/callable.h
@@ -81,6 +81,7 @@ public:
_FORCE_INLINE_ bool is_standard() const {
return method != StringName();
}
+ bool is_valid() const;
Callable bind(const Variant **p_arguments, int p_argcount) const;
Callable unbind(int p_argcount) const;
diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp
index 10446a5ec1..56eda6e703 100644
--- a/core/variant/callable_bind.cpp
+++ b/core/variant/callable_bind.cpp
@@ -169,7 +169,8 @@ CallableCustomUnbind::~CallableCustomUnbind() {
}
Callable callable_bind(const Callable &p_callable, const Variant &p_arg1) {
- return p_callable.bind((const Variant **)&p_arg1, 1);
+ const Variant *args[1] = { &p_arg1 };
+ return p_callable.bind(args, 1);
}
Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2) {
diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp
index b2f7c6aa0a..07b3a9a675 100644
--- a/core/variant/dictionary.cpp
+++ b/core/variant/dictionary.cpp
@@ -33,6 +33,11 @@
#include "core/templates/ordered_hash_map.h"
#include "core/templates/safe_refcount.h"
#include "core/variant/variant.h"
+// required in this order by VariantInternal, do not remove this comment.
+#include "core/object/class_db.h"
+#include "core/object/object.h"
+#include "core/variant/type_info.h"
+#include "core/variant/variant_internal.h"
struct DictionaryPrivate {
SafeRefCount refcount;
@@ -74,15 +79,32 @@ Variant Dictionary::get_value_at_index(int p_index) const {
}
Variant &Dictionary::operator[](const Variant &p_key) {
- return _p->variant_map[p_key];
+ if (p_key.get_type() == Variant::STRING_NAME) {
+ const StringName *sn = VariantInternal::get_string_name(&p_key);
+ return _p->variant_map[sn->operator String()];
+ } else {
+ return _p->variant_map[p_key];
+ }
}
const Variant &Dictionary::operator[](const Variant &p_key) const {
- return _p->variant_map[p_key];
+ if (p_key.get_type() == Variant::STRING_NAME) {
+ const StringName *sn = VariantInternal::get_string_name(&p_key);
+ return _p->variant_map[sn->operator String()];
+ } else {
+ return _p->variant_map[p_key];
+ }
}
const Variant *Dictionary::getptr(const Variant &p_key) const {
- OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstElement E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key);
+ OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstElement E;
+
+ if (p_key.get_type() == Variant::STRING_NAME) {
+ const StringName *sn = VariantInternal::get_string_name(&p_key);
+ E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(sn->operator String());
+ } else {
+ E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key);
+ }
if (!E) {
return nullptr;
@@ -91,8 +113,14 @@ const Variant *Dictionary::getptr(const Variant &p_key) const {
}
Variant *Dictionary::getptr(const Variant &p_key) {
- OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.find(p_key);
+ OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E;
+ if (p_key.get_type() == Variant::STRING_NAME) {
+ const StringName *sn = VariantInternal::get_string_name(&p_key);
+ E = ((OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(sn->operator String());
+ } else {
+ E = ((OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key);
+ }
if (!E) {
return nullptr;
}
@@ -100,7 +128,14 @@ Variant *Dictionary::getptr(const Variant &p_key) {
}
Variant Dictionary::get_valid(const Variant &p_key) const {
- OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstElement E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key);
+ OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstElement E;
+
+ if (p_key.get_type() == Variant::STRING_NAME) {
+ const StringName *sn = VariantInternal::get_string_name(&p_key);
+ E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(sn->operator String());
+ } else {
+ E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key);
+ }
if (!E) {
return Variant();
@@ -126,7 +161,12 @@ bool Dictionary::is_empty() const {
}
bool Dictionary::has(const Variant &p_key) const {
- return _p->variant_map.has(p_key);
+ if (p_key.get_type() == Variant::STRING_NAME) {
+ const StringName *sn = VariantInternal::get_string_name(&p_key);
+ return _p->variant_map.has(sn->operator String());
+ } else {
+ return _p->variant_map.has(p_key);
+ }
}
bool Dictionary::has_all(const Array &p_keys) const {
@@ -139,7 +179,12 @@ bool Dictionary::has_all(const Array &p_keys) const {
}
bool Dictionary::erase(const Variant &p_key) {
- return _p->variant_map.erase(p_key);
+ if (p_key.get_type() == Variant::STRING_NAME) {
+ const StringName *sn = VariantInternal::get_string_name(&p_key);
+ return _p->variant_map.erase(sn->operator String());
+ } else {
+ return _p->variant_map.erase(p_key);
+ }
}
bool Dictionary::operator==(const Dictionary &p_dictionary) const {
diff --git a/core/variant/method_ptrcall.h b/core/variant/method_ptrcall.h
index e91029f330..98304621f2 100644
--- a/core/variant/method_ptrcall.h
+++ b/core/variant/method_ptrcall.h
@@ -45,6 +45,7 @@ struct PtrToArg {};
_FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
return *reinterpret_cast<const m_type *>(p_ptr); \
} \
+ typedef m_type EncodeT; \
_FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \
*((m_type *)p_ptr) = p_val; \
} \
@@ -54,6 +55,7 @@ struct PtrToArg {};
_FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
return *reinterpret_cast<const m_type *>(p_ptr); \
} \
+ typedef m_type EncodeT; \
_FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \
*((m_type *)p_ptr) = p_val; \
} \
@@ -65,6 +67,7 @@ struct PtrToArg {};
_FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
return static_cast<m_type>(*reinterpret_cast<const m_conv *>(p_ptr)); \
} \
+ typedef m_conv EncodeT; \
_FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \
*((m_conv *)p_ptr) = static_cast<m_conv>(p_val); \
} \
@@ -74,6 +77,7 @@ struct PtrToArg {};
_FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
return static_cast<m_type>(*reinterpret_cast<const m_conv *>(p_ptr)); \
} \
+ typedef m_conv EncodeT; \
_FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \
*((m_conv *)p_ptr) = static_cast<m_conv>(p_val); \
} \
@@ -85,6 +89,7 @@ struct PtrToArg {};
_FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
return *reinterpret_cast<const m_type *>(p_ptr); \
} \
+ typedef m_type EncodeT; \
_FORCE_INLINE_ static void encode(const m_type &p_val, void *p_ptr) { \
*((m_type *)p_ptr) = p_val; \
} \
@@ -94,12 +99,13 @@ struct PtrToArg {};
_FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
return *reinterpret_cast<const m_type *>(p_ptr); \
} \
+ typedef m_type EncodeT; \
_FORCE_INLINE_ static void encode(const m_type &p_val, void *p_ptr) { \
*((m_type *)p_ptr) = p_val; \
} \
}
-MAKE_PTRARG(bool);
+MAKE_PTRARGCONV(bool, uint8_t);
// Integer types.
MAKE_PTRARGCONV(uint8_t, int64_t);
MAKE_PTRARGCONV(int8_t, int64_t);
@@ -122,10 +128,10 @@ MAKE_PTRARG_BY_REFERENCE(Vector3);
MAKE_PTRARG_BY_REFERENCE(Vector3i);
MAKE_PTRARG(Transform2D);
MAKE_PTRARG_BY_REFERENCE(Plane);
-MAKE_PTRARG(Quat);
+MAKE_PTRARG(Quaternion);
MAKE_PTRARG_BY_REFERENCE(AABB);
MAKE_PTRARG_BY_REFERENCE(Basis);
-MAKE_PTRARG_BY_REFERENCE(Transform);
+MAKE_PTRARG_BY_REFERENCE(Transform3D);
MAKE_PTRARG_BY_REFERENCE(Color);
MAKE_PTRARG(StringName);
MAKE_PTRARG(NodePath);
@@ -153,7 +159,7 @@ struct PtrToArg<T *> {
_FORCE_INLINE_ static T *convert(const void *p_ptr) {
return const_cast<T *>(reinterpret_cast<const T *>(p_ptr));
}
-
+ typedef Object *EncodeT;
_FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) {
*((T **)p_ptr) = p_var;
}
@@ -164,7 +170,7 @@ struct PtrToArg<const T *> {
_FORCE_INLINE_ static const T *convert(const void *p_ptr) {
return reinterpret_cast<const T *>(p_ptr);
}
-
+ typedef const Object *EncodeT;
_FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) {
*((T **)p_ptr) = p_var;
}
@@ -177,7 +183,7 @@ struct PtrToArg<ObjectID> {
_FORCE_INLINE_ static const ObjectID convert(const void *p_ptr) {
return ObjectID(*reinterpret_cast<const uint64_t *>(p_ptr));
}
-
+ typedef uint64_t EncodeT;
_FORCE_INLINE_ static void encode(const ObjectID &p_val, void *p_ptr) {
*((uint64_t *)p_ptr) = p_val;
}
@@ -185,6 +191,7 @@ struct PtrToArg<ObjectID> {
// This is for the special cases used by Variant.
+// No EncodeT because direct pointer conversion not possible.
#define MAKE_VECARG(m_type) \
template <> \
struct PtrToArg<Vector<m_type>> { \
@@ -230,6 +237,7 @@ struct PtrToArg<ObjectID> {
} \
}
+// No EncodeT because direct pointer conversion not possible.
#define MAKE_VECARG_ALT(m_type, m_type_alt) \
template <> \
struct PtrToArg<Vector<m_type_alt>> { \
@@ -279,6 +287,7 @@ MAKE_VECARG_ALT(String, StringName);
// For stuff that gets converted to Array vectors.
+// No EncodeT because direct pointer conversion not possible.
#define MAKE_VECARR(m_type) \
template <> \
struct PtrToArg<Vector<m_type>> { \
@@ -319,6 +328,7 @@ MAKE_VECARR(Variant);
MAKE_VECARR(RID);
MAKE_VECARR(Plane);
+// No EncodeT because direct pointer conversion not possible.
#define MAKE_DVECARR(m_type) \
template <> \
struct PtrToArg<Vector<m_type>> { \
@@ -366,6 +376,7 @@ MAKE_VECARR(Plane);
// Special case for IPAddress.
+// No EncodeT because direct pointer conversion not possible.
#define MAKE_STRINGCONV_BY_REFERENCE(m_type) \
template <> \
struct PtrToArg<m_type> { \
@@ -389,6 +400,7 @@ MAKE_VECARR(Plane);
MAKE_STRINGCONV_BY_REFERENCE(IPAddress);
+// No EncodeT because direct pointer conversion not possible.
template <>
struct PtrToArg<Vector<Face3>> {
_FORCE_INLINE_ static Vector<Face3> convert(const void *p_ptr) {
@@ -423,6 +435,7 @@ struct PtrToArg<Vector<Face3>> {
}
};
+// No EncodeT because direct pointer conversion not possible.
template <>
struct PtrToArg<const Vector<Face3> &> {
_FORCE_INLINE_ static Vector<Face3> convert(const void *p_ptr) {
diff --git a/core/variant/native_ptr.h b/core/variant/native_ptr.h
new file mode 100644
index 0000000000..b4ec0df7d6
--- /dev/null
+++ b/core/variant/native_ptr.h
@@ -0,0 +1,130 @@
+/*************************************************************************/
+/* native_ptr.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef NATIVE_PTR_H
+#define NATIVE_PTR_H
+
+#include "core/math/audio_frame.h"
+#include "core/variant/method_ptrcall.h"
+#include "core/variant/type_info.h"
+
+template <class T>
+struct GDNativeConstPtr {
+ const T *data = nullptr;
+ GDNativeConstPtr(const T *p_assign) { data = p_assign; }
+ static const char *get_name() { return "const void"; }
+ operator const T *() const { return data; }
+ operator Variant() const { return uint64_t(data); }
+};
+
+template <class T>
+struct GDNativePtr {
+ T *data = nullptr;
+ GDNativePtr(T *p_assign) { data = p_assign; }
+ static const char *get_name() { return "void"; }
+ operator T *() const { return data; }
+ operator Variant() const { return uint64_t(data); }
+};
+
+#define GDVIRTUAL_NATIVE_PTR(m_type) \
+ template <> \
+ struct GDNativeConstPtr<m_type> { \
+ const m_type *data = nullptr; \
+ GDNativeConstPtr(const m_type *p_assign) { data = p_assign; } \
+ static const char *get_name() { return "const " #m_type; } \
+ operator const m_type *() const { return data; } \
+ operator Variant() const { return uint64_t(data); } \
+ }; \
+ template <> \
+ struct GDNativePtr<m_type> { \
+ m_type *data = nullptr; \
+ GDNativePtr(m_type *p_assign) { data = p_assign; } \
+ static const char *get_name() { return #m_type; } \
+ operator m_type *() const { return data; } \
+ operator Variant() const { return uint64_t(data); } \
+ };
+
+template <class T>
+struct GetTypeInfo<GDNativeConstPtr<T>> {
+ static const Variant::Type VARIANT_TYPE = Variant::NIL;
+ static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;
+ static inline PropertyInfo get_class_info() {
+ return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_INT_IS_POINTER, GDNativeConstPtr<T>::get_name());
+ }
+};
+
+template <class T>
+struct GetTypeInfo<GDNativePtr<T>> {
+ static const Variant::Type VARIANT_TYPE = Variant::NIL;
+ static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;
+ static inline PropertyInfo get_class_info() {
+ return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_INT_IS_POINTER, GDNativePtr<T>::get_name());
+ }
+};
+
+template <class T>
+struct PtrToArg<GDNativeConstPtr<T>> {
+ _FORCE_INLINE_ static GDNativeConstPtr<T> convert(const void *p_ptr) {
+ return GDNativeConstPtr<T>(reinterpret_cast<const T *>(p_ptr));
+ }
+ typedef const T *EncodeT;
+ _FORCE_INLINE_ static void encode(GDNativeConstPtr<T> p_val, void *p_ptr) {
+ *((const T **)p_ptr) = p_val.data;
+ }
+};
+template <class T>
+struct PtrToArg<GDNativePtr<T>> {
+ _FORCE_INLINE_ static GDNativePtr<T> convert(const void *p_ptr) {
+ return GDNativePtr<T>(reinterpret_cast<const T *>(p_ptr));
+ }
+ typedef T *EncodeT;
+ _FORCE_INLINE_ static void encode(GDNativePtr<T> p_val, void *p_ptr) {
+ *((T **)p_ptr) = p_val.data;
+ }
+};
+
+GDVIRTUAL_NATIVE_PTR(AudioFrame)
+GDVIRTUAL_NATIVE_PTR(bool)
+GDVIRTUAL_NATIVE_PTR(char)
+GDVIRTUAL_NATIVE_PTR(char16_t)
+GDVIRTUAL_NATIVE_PTR(char32_t)
+GDVIRTUAL_NATIVE_PTR(wchar_t)
+GDVIRTUAL_NATIVE_PTR(uint8_t)
+GDVIRTUAL_NATIVE_PTR(int8_t)
+GDVIRTUAL_NATIVE_PTR(uint16_t)
+GDVIRTUAL_NATIVE_PTR(int16_t)
+GDVIRTUAL_NATIVE_PTR(uint32_t)
+GDVIRTUAL_NATIVE_PTR(int32_t)
+GDVIRTUAL_NATIVE_PTR(int64_t)
+GDVIRTUAL_NATIVE_PTR(uint64_t)
+GDVIRTUAL_NATIVE_PTR(float)
+GDVIRTUAL_NATIVE_PTR(double)
+
+#endif // NATIVE_PTR_H
diff --git a/core/variant/type_info.h b/core/variant/type_info.h
index d5b6d85dfb..b70d29bbac 100644
--- a/core/variant/type_info.h
+++ b/core/variant/type_info.h
@@ -146,10 +146,10 @@ MAKE_TYPE_INFO(Rect2i, Variant::RECT2I)
MAKE_TYPE_INFO(Vector3i, Variant::VECTOR3I)
MAKE_TYPE_INFO(Transform2D, Variant::TRANSFORM2D)
MAKE_TYPE_INFO(Plane, Variant::PLANE)
-MAKE_TYPE_INFO(Quat, Variant::QUAT)
+MAKE_TYPE_INFO(Quaternion, Variant::QUATERNION)
MAKE_TYPE_INFO(AABB, Variant::AABB)
MAKE_TYPE_INFO(Basis, Variant::BASIS)
-MAKE_TYPE_INFO(Transform, Variant::TRANSFORM)
+MAKE_TYPE_INFO(Transform3D, Variant::TRANSFORM3D)
MAKE_TYPE_INFO(Color, Variant::COLOR)
MAKE_TYPE_INFO(StringName, Variant::STRING_NAME)
MAKE_TYPE_INFO(NodePath, Variant::NODE_PATH)
@@ -241,14 +241,27 @@ struct GetTypeInfo<const T *, typename EnableIf<TypeInherits<Object, T>::value>:
}
};
-#define TEMPL_MAKE_ENUM_TYPE_INFO(m_enum, m_impl) \
- template <> \
- struct GetTypeInfo<m_impl> { \
- static const Variant::Type VARIANT_TYPE = Variant::INT; \
- static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; \
- static inline PropertyInfo get_class_info() { \
- return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_CLASS_IS_ENUM, String(#m_enum).replace("::", ".")); \
- } \
+namespace godot {
+namespace details {
+inline String enum_qualified_name_to_class_info_name(const String &p_qualified_name) {
+ Vector<String> parts = p_qualified_name.split("::", false);
+ if (parts.size() <= 2)
+ return String(".").join(parts);
+ // Contains namespace. We only want the class and enum names.
+ return parts[parts.size() - 2] + "." + parts[parts.size() - 1];
+}
+} // namespace details
+} // namespace godot
+
+#define TEMPL_MAKE_ENUM_TYPE_INFO(m_enum, m_impl) \
+ template <> \
+ struct GetTypeInfo<m_impl> { \
+ static const Variant::Type VARIANT_TYPE = Variant::INT; \
+ static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; \
+ static inline PropertyInfo get_class_info() { \
+ return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_CLASS_IS_ENUM, \
+ godot::details::enum_qualified_name_to_class_info_name(String(#m_enum))); \
+ } \
};
#define MAKE_ENUM_TYPE_INFO(m_enum) \
diff --git a/core/variant/typed_array.h b/core/variant/typed_array.h
index e0309aa3fe..2e96f4e445 100644
--- a/core/variant/typed_array.h
+++ b/core/variant/typed_array.h
@@ -98,10 +98,10 @@ MAKE_TYPED_ARRAY(Vector3, Variant::VECTOR3)
MAKE_TYPED_ARRAY(Vector3i, Variant::VECTOR3I)
MAKE_TYPED_ARRAY(Transform2D, Variant::TRANSFORM2D)
MAKE_TYPED_ARRAY(Plane, Variant::PLANE)
-MAKE_TYPED_ARRAY(Quat, Variant::QUAT)
+MAKE_TYPED_ARRAY(Quaternion, Variant::QUATERNION)
MAKE_TYPED_ARRAY(AABB, Variant::AABB)
MAKE_TYPED_ARRAY(Basis, Variant::BASIS)
-MAKE_TYPED_ARRAY(Transform, Variant::TRANSFORM)
+MAKE_TYPED_ARRAY(Transform3D, Variant::TRANSFORM3D)
MAKE_TYPED_ARRAY(Color, Variant::COLOR)
MAKE_TYPED_ARRAY(StringName, Variant::STRING_NAME)
MAKE_TYPED_ARRAY(NodePath, Variant::NODE_PATH)
@@ -125,7 +125,7 @@ struct PtrToArg<TypedArray<T>> {
_FORCE_INLINE_ static TypedArray<T> convert(const void *p_ptr) {
return TypedArray<T>(*reinterpret_cast<const Array *>(p_ptr));
}
-
+ typedef Array EncodeT;
_FORCE_INLINE_ static void encode(TypedArray<T> p_val, void *p_ptr) {
*(Array *)p_ptr = p_val;
}
@@ -133,13 +133,13 @@ struct PtrToArg<TypedArray<T>> {
template <class T>
struct PtrToArg<const TypedArray<T> &> {
- _FORCE_INLINE_ static TypedArray<T> convert(const void *p_ptr) {
+ typedef Array EncodeT;
+ _FORCE_INLINE_ static TypedArray<T>
+ convert(const void *p_ptr) {
return TypedArray<T>(*reinterpret_cast<const Array *>(p_ptr));
}
};
-#ifdef DEBUG_METHODS_ENABLED
-
template <class T>
struct GetTypeInfo<TypedArray<T>> {
static const Variant::Type VARIANT_TYPE = Variant::ARRAY;
@@ -196,10 +196,10 @@ MAKE_TYPED_ARRAY_INFO(Vector3, Variant::VECTOR3)
MAKE_TYPED_ARRAY_INFO(Vector3i, Variant::VECTOR3I)
MAKE_TYPED_ARRAY_INFO(Transform2D, Variant::TRANSFORM2D)
MAKE_TYPED_ARRAY_INFO(Plane, Variant::PLANE)
-MAKE_TYPED_ARRAY_INFO(Quat, Variant::QUAT)
+MAKE_TYPED_ARRAY_INFO(Quaternion, Variant::QUATERNION)
MAKE_TYPED_ARRAY_INFO(AABB, Variant::AABB)
MAKE_TYPED_ARRAY_INFO(Basis, Variant::BASIS)
-MAKE_TYPED_ARRAY_INFO(Transform, Variant::TRANSFORM)
+MAKE_TYPED_ARRAY_INFO(Transform3D, Variant::TRANSFORM3D)
MAKE_TYPED_ARRAY_INFO(Color, Variant::COLOR)
MAKE_TYPED_ARRAY_INFO(StringName, Variant::STRING_NAME)
MAKE_TYPED_ARRAY_INFO(NodePath, Variant::NODE_PATH)
@@ -218,6 +218,4 @@ MAKE_TYPED_ARRAY_INFO(Vector<Vector2>, Variant::PACKED_VECTOR2_ARRAY)
MAKE_TYPED_ARRAY_INFO(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY)
MAKE_TYPED_ARRAY_INFO(Vector<Color>, Variant::PACKED_COLOR_ARRAY)
-#endif
-
#endif // TYPED_ARRAY_H
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 333dd8e8d1..0dbeb6e4cb 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -32,6 +32,7 @@
#include "core/core_string_names.h"
#include "core/debugger/engine_debugger.h"
+#include "core/io/json.h"
#include "core/io/marshalls.h"
#include "core/io/resource.h"
#include "core/math/math_funcs.h"
@@ -91,16 +92,16 @@ String Variant::get_type_name(Variant::Type p_type) {
case AABB: {
return "AABB";
} break;
- case QUAT: {
- return "Quat";
+ case QUATERNION: {
+ return "Quaternion";
} break;
case BASIS: {
return "Basis";
} break;
- case TRANSFORM: {
- return "Transform";
+ case TRANSFORM3D: {
+ return "Transform3D";
} break;
@@ -275,7 +276,7 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) {
} break;
case TRANSFORM2D: {
static const Type valid[] = {
- TRANSFORM,
+ TRANSFORM3D,
NIL
};
@@ -300,7 +301,7 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) {
} break;
- case QUAT: {
+ case QUATERNION: {
static const Type valid[] = {
BASIS,
NIL
@@ -311,7 +312,7 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) {
} break;
case BASIS: {
static const Type valid[] = {
- QUAT,
+ QUATERNION,
VECTOR3,
NIL
};
@@ -319,10 +320,10 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) {
valid_types = valid;
} break;
- case TRANSFORM: {
+ case TRANSFORM3D: {
static const Type valid[] = {
TRANSFORM2D,
- QUAT,
+ QUATERNION,
BASIS,
NIL
};
@@ -582,7 +583,7 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type
} break;
case TRANSFORM2D: {
static const Type valid[] = {
- TRANSFORM,
+ TRANSFORM3D,
NIL
};
@@ -607,7 +608,7 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type
} break;
- case QUAT: {
+ case QUATERNION: {
static const Type valid[] = {
BASIS,
NIL
@@ -618,7 +619,7 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type
} break;
case BASIS: {
static const Type valid[] = {
- QUAT,
+ QUATERNION,
VECTOR3,
NIL
};
@@ -626,10 +627,10 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type
valid_types = valid;
} break;
- case TRANSFORM: {
+ case TRANSFORM3D: {
static const Type valid[] = {
TRANSFORM2D,
- QUAT,
+ QUATERNION,
BASIS,
NIL
};
@@ -873,16 +874,16 @@ bool Variant::is_zero() const {
case AABB: {
return *_data._aabb == ::AABB();
} break;
- case QUAT: {
- return *reinterpret_cast<const Quat *>(_data._mem) == Quat();
+ case QUATERNION: {
+ return *reinterpret_cast<const Quaternion *>(_data._mem) == Quaternion();
} break;
case BASIS: {
return *_data._basis == Basis();
} break;
- case TRANSFORM: {
- return *_data._transform == Transform();
+ case TRANSFORM3D: {
+ return *_data._transform3d == Transform3D();
} break;
@@ -1092,16 +1093,16 @@ void Variant::reference(const Variant &p_variant) {
case AABB: {
_data._aabb = memnew(::AABB(*p_variant._data._aabb));
} break;
- case QUAT: {
- memnew_placement(_data._mem, Quat(*reinterpret_cast<const Quat *>(p_variant._data._mem)));
+ case QUATERNION: {
+ memnew_placement(_data._mem, Quaternion(*reinterpret_cast<const Quaternion *>(p_variant._data._mem)));
} break;
case BASIS: {
_data._basis = memnew(Basis(*p_variant._data._basis));
} break;
- case TRANSFORM: {
- _data._transform = memnew(Transform(*p_variant._data._transform));
+ case TRANSFORM3D: {
+ _data._transform3d = memnew(Transform3D(*p_variant._data._transform3d));
} break;
// misc types
@@ -1115,9 +1116,9 @@ void Variant::reference(const Variant &p_variant) {
case OBJECT: {
memnew_placement(_data._mem, ObjData);
- if (p_variant._get_obj().obj && p_variant._get_obj().id.is_reference()) {
- Reference *reference = static_cast<Reference *>(p_variant._get_obj().obj);
- if (!reference->reference()) {
+ if (p_variant._get_obj().obj && p_variant._get_obj().id.is_ref_counted()) {
+ RefCounted *ref_counted = static_cast<RefCounted *>(p_variant._get_obj().obj);
+ if (!ref_counted->reference()) {
_get_obj().obj = nullptr;
_get_obj().id = ObjectID();
break;
@@ -1254,8 +1255,8 @@ void Variant::zero() {
case PLANE:
*reinterpret_cast<Plane *>(this->_data._mem) = Plane();
break;
- case QUAT:
- *reinterpret_cast<Quat *>(this->_data._mem) = Quat();
+ case QUATERNION:
+ *reinterpret_cast<Quaternion *>(this->_data._mem) = Quaternion();
break;
case COLOR:
*reinterpret_cast<Color *>(this->_data._mem) = Color();
@@ -1275,7 +1276,7 @@ void Variant::_clear_internal() {
// no point, they don't allocate memory
VECTOR3,
PLANE,
- QUAT,
+ QUATERNION,
COLOR,
VECTOR2,
RECT2
@@ -1289,8 +1290,8 @@ void Variant::_clear_internal() {
case BASIS: {
memdelete(_data._basis);
} break;
- case TRANSFORM: {
- memdelete(_data._transform);
+ case TRANSFORM3D: {
+ memdelete(_data._transform3d);
} break;
// misc types
@@ -1301,11 +1302,11 @@ void Variant::_clear_internal() {
reinterpret_cast<NodePath *>(_data._mem)->~NodePath();
} break;
case OBJECT: {
- if (_get_obj().id.is_reference()) {
+ if (_get_obj().id.is_ref_counted()) {
//we are safe that there is a reference here
- Reference *reference = static_cast<Reference *>(_get_obj().obj);
- if (reference->unreference()) {
- memdelete(reference);
+ RefCounted *ref_counted = static_cast<RefCounted *>(_get_obj().obj);
+ if (ref_counted->unreference()) {
+ memdelete(ref_counted);
}
}
_get_obj().obj = nullptr;
@@ -1626,9 +1627,9 @@ Variant::operator String() const {
String Variant::stringify(List<const void *> &stack) const {
switch (type) {
case NIL:
- return "Null";
+ return "null";
case BOOL:
- return _data._bool ? "True" : "False";
+ return _data._bool ? "true" : "false";
case INT:
return itos(_data._int);
case FLOAT:
@@ -1636,60 +1637,35 @@ String Variant::stringify(List<const void *> &stack) const {
case STRING:
return *reinterpret_cast<const String *>(_data._mem);
case VECTOR2:
- return "(" + operator Vector2() + ")";
+ return operator Vector2();
case VECTOR2I:
- return "(" + operator Vector2i() + ")";
+ return operator Vector2i();
case RECT2:
- return "(" + operator Rect2() + ")";
+ return operator Rect2();
case RECT2I:
- return "(" + operator Rect2i() + ")";
- case TRANSFORM2D: {
- Transform2D mat32 = operator Transform2D();
- return "(" + Variant(mat32.elements[0]).operator String() + ", " + Variant(mat32.elements[1]).operator String() + ", " + Variant(mat32.elements[2]).operator String() + ")";
- } break;
+ return operator Rect2i();
+ case TRANSFORM2D:
+ return operator Transform2D();
case VECTOR3:
- return "(" + operator Vector3() + ")";
+ return operator Vector3();
case VECTOR3I:
- return "(" + operator Vector3i() + ")";
+ return operator Vector3i();
case PLANE:
return operator Plane();
- //case QUAT:
case AABB:
return operator ::AABB();
- case QUAT:
- return "(" + operator Quat() + ")";
- case BASIS: {
- Basis mat3 = operator Basis();
-
- String mtx("(");
- for (int i = 0; i < 3; i++) {
- if (i != 0) {
- mtx += ", ";
- }
-
- mtx += "(";
-
- for (int j = 0; j < 3; j++) {
- if (j != 0) {
- mtx += ", ";
- }
-
- mtx += Variant(mat3.elements[i][j]).operator String();
- }
-
- mtx += ")";
- }
-
- return mtx + ")";
- } break;
- case TRANSFORM:
- return operator Transform();
+ case QUATERNION:
+ return operator Quaternion();
+ case BASIS:
+ return operator Basis();
+ case TRANSFORM3D:
+ return operator Transform3D();
case STRING_NAME:
return operator StringName();
case NODE_PATH:
return operator NodePath();
case COLOR:
- return String::num(operator Color().r) + "," + String::num(operator Color().g) + "," + String::num(operator Color().b) + "," + String::num(operator Color().a);
+ return operator Color();
case DICTIONARY: {
const Dictionary &d = *reinterpret_cast<const Dictionary *>(_data._mem);
if (stack.find(d.id())) {
@@ -1705,10 +1681,10 @@ String Variant::stringify(List<const void *> &stack) const {
Vector<_VariantStrPair> pairs;
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+ for (const Variant &E : keys) {
_VariantStrPair sp;
- sp.key = E->get().stringify(stack);
- sp.value = d[E->get()].stringify(stack);
+ sp.key = E.stringify(stack);
+ sp.value = d[E].stringify(stack);
pairs.push_back(sp);
}
@@ -1723,6 +1699,7 @@ String Variant::stringify(List<const void *> &stack) const {
}
str += "}";
+ stack.erase(d.id());
return str;
} break;
case PACKED_VECTOR2_ARRAY: {
@@ -1826,12 +1803,13 @@ String Variant::stringify(List<const void *> &stack) const {
}
str += "]";
+ stack.erase(arr.id());
return str;
} break;
case OBJECT: {
if (_get_obj().obj) {
- if (!_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (!_get_obj().id.is_ref_counted() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
return "[Freed Object]";
}
@@ -1861,6 +1839,11 @@ String Variant::stringify(List<const void *> &stack) const {
return "";
}
+String Variant::to_json_string() const {
+ JSON json;
+ return json.stringify(*this);
+}
+
Variant::operator Vector2() const {
if (type == VECTOR2) {
return *reinterpret_cast<const Vector2 *>(_data._mem);
@@ -1956,39 +1939,39 @@ Variant::operator ::AABB() const {
Variant::operator Basis() const {
if (type == BASIS) {
return *_data._basis;
- } else if (type == QUAT) {
- return *reinterpret_cast<const Quat *>(_data._mem);
+ } else if (type == QUATERNION) {
+ return *reinterpret_cast<const Quaternion *>(_data._mem);
} else if (type == VECTOR3) {
return Basis(*reinterpret_cast<const Vector3 *>(_data._mem));
- } else if (type == TRANSFORM) { // unexposed in Variant::can_convert?
- return _data._transform->basis;
+ } else if (type == TRANSFORM3D) { // unexposed in Variant::can_convert?
+ return _data._transform3d->basis;
} else {
return Basis();
}
}
-Variant::operator Quat() const {
- if (type == QUAT) {
- return *reinterpret_cast<const Quat *>(_data._mem);
+Variant::operator Quaternion() const {
+ if (type == QUATERNION) {
+ return *reinterpret_cast<const Quaternion *>(_data._mem);
} else if (type == BASIS) {
return *_data._basis;
- } else if (type == TRANSFORM) {
- return _data._transform->basis;
+ } else if (type == TRANSFORM3D) {
+ return _data._transform3d->basis;
} else {
- return Quat();
+ return Quaternion();
}
}
-Variant::operator Transform() const {
- if (type == TRANSFORM) {
- return *_data._transform;
+Variant::operator Transform3D() const {
+ if (type == TRANSFORM3D) {
+ return *_data._transform3d;
} else if (type == BASIS) {
- return Transform(*_data._basis, Vector3());
- } else if (type == QUAT) {
- return Transform(Basis(*reinterpret_cast<const Quat *>(_data._mem)), Vector3());
+ return Transform3D(*_data._basis, Vector3());
+ } else if (type == QUATERNION) {
+ return Transform3D(Basis(*reinterpret_cast<const Quaternion *>(_data._mem)), Vector3());
} else if (type == TRANSFORM2D) {
const Transform2D &t = *_data._transform2d;
- Transform m;
+ Transform3D m;
m.basis.elements[0][0] = t.elements[0][0];
m.basis.elements[1][0] = t.elements[0][1];
m.basis.elements[0][1] = t.elements[1][0];
@@ -1997,15 +1980,15 @@ Variant::operator Transform() const {
m.origin[1] = t.elements[2][1];
return m;
} else {
- return Transform();
+ return Transform3D();
}
}
Variant::operator Transform2D() const {
if (type == TRANSFORM2D) {
return *_data._transform2d;
- } else if (type == TRANSFORM) {
- const Transform &t = *_data._transform;
+ } else if (type == TRANSFORM3D) {
+ const Transform3D &t = *_data._transform3d;
Transform2D m;
m.elements[0][0] = t.basis.elements[0][0];
m.elements[0][1] = t.basis.elements[1][0];
@@ -2495,14 +2478,14 @@ Variant::Variant(const Basis &p_matrix) {
_data._basis = memnew(Basis(p_matrix));
}
-Variant::Variant(const Quat &p_quat) {
- type = QUAT;
- memnew_placement(_data._mem, Quat(p_quat));
+Variant::Variant(const Quaternion &p_quaternion) {
+ type = QUATERNION;
+ memnew_placement(_data._mem, Quaternion(p_quaternion));
}
-Variant::Variant(const Transform &p_transform) {
- type = TRANSFORM;
- _data._transform = memnew(Transform(p_transform));
+Variant::Variant(const Transform3D &p_transform) {
+ type = TRANSFORM3D;
+ _data._transform3d = memnew(Transform3D(p_transform));
}
Variant::Variant(const Transform2D &p_transform) {
@@ -2531,9 +2514,9 @@ Variant::Variant(const Object *p_object) {
memnew_placement(_data._mem, ObjData);
if (p_object) {
- if (p_object->is_reference()) {
- Reference *reference = const_cast<Reference *>(static_cast<const Reference *>(p_object));
- if (!reference->init_ref()) {
+ if (p_object->is_ref_counted()) {
+ RefCounted *ref_counted = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_object));
+ if (!ref_counted->init_ref()) {
_get_obj().obj = nullptr;
_get_obj().id = ObjectID();
return;
@@ -2739,14 +2722,14 @@ void Variant::operator=(const Variant &p_variant) {
case AABB: {
*_data._aabb = *(p_variant._data._aabb);
} break;
- case QUAT: {
- *reinterpret_cast<Quat *>(_data._mem) = *reinterpret_cast<const Quat *>(p_variant._data._mem);
+ case QUATERNION: {
+ *reinterpret_cast<Quaternion *>(_data._mem) = *reinterpret_cast<const Quaternion *>(p_variant._data._mem);
} break;
case BASIS: {
*_data._basis = *(p_variant._data._basis);
} break;
- case TRANSFORM: {
- *_data._transform = *(p_variant._data._transform);
+ case TRANSFORM3D: {
+ *_data._transform3d = *(p_variant._data._transform3d);
} break;
// misc types
@@ -2757,17 +2740,17 @@ void Variant::operator=(const Variant &p_variant) {
*reinterpret_cast<::RID *>(_data._mem) = *reinterpret_cast<const ::RID *>(p_variant._data._mem);
} break;
case OBJECT: {
- if (_get_obj().id.is_reference()) {
+ if (_get_obj().id.is_ref_counted()) {
//we are safe that there is a reference here
- Reference *reference = static_cast<Reference *>(_get_obj().obj);
- if (reference->unreference()) {
- memdelete(reference);
+ RefCounted *ref_counted = static_cast<RefCounted *>(_get_obj().obj);
+ if (ref_counted->unreference()) {
+ memdelete(ref_counted);
}
}
- if (p_variant._get_obj().obj && p_variant._get_obj().id.is_reference()) {
- Reference *reference = static_cast<Reference *>(p_variant._get_obj().obj);
- if (!reference->reference()) {
+ if (p_variant._get_obj().obj && p_variant._get_obj().id.is_ref_counted()) {
+ RefCounted *ref_counted = static_cast<RefCounted *>(p_variant._get_obj().obj);
+ if (!ref_counted->reference()) {
_get_obj().obj = nullptr;
_get_obj().id = ObjectID();
break;
@@ -2849,7 +2832,7 @@ uint32_t Variant::hash() const {
return _data._bool ? 1 : 0;
} break;
case INT: {
- return _data._int;
+ return hash_one_uint64((uint64_t)_data._int);
} break;
case FLOAT: {
return hash_djb2_one_float(_data._float);
@@ -2864,8 +2847,8 @@ uint32_t Variant::hash() const {
return hash_djb2_one_float(reinterpret_cast<const Vector2 *>(_data._mem)->y, hash);
} break;
case VECTOR2I: {
- uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Vector2i *>(_data._mem)->x);
- return hash_djb2_one_32(reinterpret_cast<const Vector2i *>(_data._mem)->y, hash);
+ uint32_t hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector2i *>(_data._mem)->x);
+ return hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector2i *>(_data._mem)->y, hash);
} break;
case RECT2: {
uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Rect2 *>(_data._mem)->position.x);
@@ -2874,10 +2857,10 @@ uint32_t Variant::hash() const {
return hash_djb2_one_float(reinterpret_cast<const Rect2 *>(_data._mem)->size.y, hash);
} break;
case RECT2I: {
- uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->position.x);
- hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->position.y, hash);
- hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->size.x, hash);
- return hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->size.y, hash);
+ uint32_t hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->position.x);
+ hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->position.y, hash);
+ hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->size.x, hash);
+ return hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->size.y, hash);
} break;
case TRANSFORM2D: {
uint32_t hash = 5831;
@@ -2895,9 +2878,9 @@ uint32_t Variant::hash() const {
return hash_djb2_one_float(reinterpret_cast<const Vector3 *>(_data._mem)->z, hash);
} break;
case VECTOR3I: {
- uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->x);
- hash = hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->y, hash);
- return hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->z, hash);
+ uint32_t hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector3i *>(_data._mem)->x);
+ hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector3i *>(_data._mem)->y, hash);
+ return hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector3i *>(_data._mem)->z, hash);
} break;
case PLANE: {
uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Plane *>(_data._mem)->normal.x);
@@ -2916,11 +2899,11 @@ uint32_t Variant::hash() const {
return hash;
} break;
- case QUAT: {
- uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Quat *>(_data._mem)->x);
- hash = hash_djb2_one_float(reinterpret_cast<const Quat *>(_data._mem)->y, hash);
- hash = hash_djb2_one_float(reinterpret_cast<const Quat *>(_data._mem)->z, hash);
- return hash_djb2_one_float(reinterpret_cast<const Quat *>(_data._mem)->w, hash);
+ case QUATERNION: {
+ uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Quaternion *>(_data._mem)->x);
+ hash = hash_djb2_one_float(reinterpret_cast<const Quaternion *>(_data._mem)->y, hash);
+ hash = hash_djb2_one_float(reinterpret_cast<const Quaternion *>(_data._mem)->z, hash);
+ return hash_djb2_one_float(reinterpret_cast<const Quaternion *>(_data._mem)->w, hash);
} break;
case BASIS: {
@@ -2934,13 +2917,13 @@ uint32_t Variant::hash() const {
return hash;
} break;
- case TRANSFORM: {
+ case TRANSFORM3D: {
uint32_t hash = 5831;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
- hash = hash_djb2_one_float(_data._transform->basis.elements[i][j], hash);
+ hash = hash_djb2_one_float(_data._transform3d->basis.elements[i][j], hash);
}
- hash = hash_djb2_one_float(_data._transform->origin[i], hash);
+ hash = hash_djb2_one_float(_data._transform3d->origin[i], hash);
}
return hash;
@@ -3127,7 +3110,7 @@ uint32_t Variant::hash() const {
(hash_compare_scalar((p_lhs).y, (p_rhs).y)) && \
(hash_compare_scalar((p_lhs).z, (p_rhs).z))
-#define hash_compare_quat(p_lhs, p_rhs) \
+#define hash_compare_quaternion(p_lhs, p_rhs) \
(hash_compare_scalar((p_lhs).x, (p_rhs).x)) && \
(hash_compare_scalar((p_lhs).y, (p_rhs).y)) && \
(hash_compare_scalar((p_lhs).z, (p_rhs).z)) && \
@@ -3235,11 +3218,11 @@ bool Variant::hash_compare(const Variant &p_variant) const {
} break;
- case QUAT: {
- const Quat *l = reinterpret_cast<const Quat *>(_data._mem);
- const Quat *r = reinterpret_cast<const Quat *>(p_variant._data._mem);
+ case QUATERNION: {
+ const Quaternion *l = reinterpret_cast<const Quaternion *>(_data._mem);
+ const Quaternion *r = reinterpret_cast<const Quaternion *>(p_variant._data._mem);
- return hash_compare_quat(*l, *r);
+ return hash_compare_quaternion(*l, *r);
} break;
case BASIS: {
@@ -3255,9 +3238,9 @@ bool Variant::hash_compare(const Variant &p_variant) const {
return true;
} break;
- case TRANSFORM: {
- const Transform *l = _data._transform;
- const Transform *r = p_variant._data._transform;
+ case TRANSFORM3D: {
+ const Transform3D *l = _data._transform3d;
+ const Transform3D *r = p_variant._data._transform3d;
for (int i = 0; i < 3; i++) {
if (!(hash_compare_vector3(l->basis.elements[i], r->basis.elements[i]))) {
@@ -3324,7 +3307,7 @@ bool Variant::hash_compare(const Variant &p_variant) const {
}
bool Variant::is_ref() const {
- return type == OBJECT && _get_obj().id.is_reference();
+ return type == OBJECT && _get_obj().id.is_ref_counted();
}
Vector<Variant> varray() {
@@ -3550,12 +3533,13 @@ void Variant::register_types() {
_register_variant_methods();
_register_variant_setters_getters();
_register_variant_constructors();
+ _register_variant_destructors();
_register_variant_utility_functions();
}
void Variant::unregister_types() {
_unregister_variant_operators();
_unregister_variant_methods();
_unregister_variant_setters_getters();
- _unregister_variant_constructors();
+ _unregister_variant_destructors();
_unregister_variant_utility_functions();
}
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 7f3c3477fc..9ec131a1b8 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -37,9 +37,9 @@
#include "core/math/color.h"
#include "core/math/face3.h"
#include "core/math/plane.h"
-#include "core/math/quat.h"
-#include "core/math/transform.h"
+#include "core/math/quaternion.h"
#include "core/math/transform_2d.h"
+#include "core/math/transform_3d.h"
#include "core/math/vector3.h"
#include "core/math/vector3i.h"
#include "core/object/object_id.h"
@@ -88,10 +88,10 @@ public:
VECTOR3I,
TRANSFORM2D,
PLANE,
- QUAT,
+ QUATERNION,
AABB,
BASIS,
- TRANSFORM,
+ TRANSFORM3D,
// misc types
COLOR,
@@ -118,6 +118,11 @@ public:
VARIANT_MAX
};
+ enum {
+ // Maximum recursion depth allowed when serializing variants.
+ MAX_RECURSION_DEPTH = 1024,
+ };
+
private:
friend struct _VariantCall;
friend class VariantInternal;
@@ -200,10 +205,10 @@ private:
Transform2D *_transform2d;
::AABB *_aabb;
Basis *_basis;
- Transform *_transform;
+ Transform3D *_transform3d;
PackedArrayRefBase *packed_array;
void *_ptr; //generic pointer
- uint8_t _mem[sizeof(ObjData) > (sizeof(real_t) * 4) ? sizeof(ObjData) : (sizeof(real_t) * 4)];
+ uint8_t _mem[sizeof(ObjData) > (sizeof(real_t) * 4) ? sizeof(ObjData) : (sizeof(real_t) * 4)]{ 0 };
} _data alignas(8);
void reference(const Variant &p_variant);
@@ -225,7 +230,7 @@ private:
false, //VECTOR3I,
true, //TRANSFORM2D,
false, //PLANE,
- false, //QUAT,
+ false, //QUATERNION,
true, //AABB,
true, //BASIS,
true, //TRANSFORM,
@@ -253,7 +258,7 @@ private:
true, //PACKED_COLOR_ARRAY,
};
- if (unlikely(needs_deinit[type])) { //make it fast for types that dont need deinit
+ if (unlikely(needs_deinit[type])) { // Make it fast for types that don't need deinit.
_clear_internal();
}
type = NIL;
@@ -266,6 +271,8 @@ private:
static void _register_variant_setters_getters();
static void _unregister_variant_setters_getters();
static void _register_variant_constructors();
+ static void _unregister_variant_destructors();
+ static void _register_variant_destructors();
static void _unregister_variant_constructors();
static void _register_variant_utility_functions();
static void _unregister_variant_utility_functions();
@@ -320,10 +327,10 @@ public:
operator Vector3i() const;
operator Plane() const;
operator ::AABB() const;
- operator Quat() const;
+ operator Quaternion() const;
operator Basis() const;
- operator Transform() const;
operator Transform2D() const;
+ operator Transform3D() const;
operator Color() const;
operator NodePath() const;
@@ -392,10 +399,10 @@ public:
Variant(const Vector3i &p_vector3i);
Variant(const Plane &p_plane);
Variant(const ::AABB &p_aabb);
- Variant(const Quat &p_quat);
+ Variant(const Quaternion &p_quat);
Variant(const Basis &p_matrix);
Variant(const Transform2D &p_transform);
- Variant(const Transform &p_transform);
+ Variant(const Transform3D &p_transform);
Variant(const Color &p_color);
Variant(const NodePath &p_node_path);
Variant(const ::RID &p_rid);
@@ -499,9 +506,10 @@ public:
static bool is_builtin_method_vararg(Variant::Type p_type, const StringName &p_method);
static void get_builtin_method_list(Variant::Type p_type, List<StringName> *p_list);
static int get_builtin_method_count(Variant::Type p_type);
+ static uint32_t get_builtin_method_hash(Variant::Type p_type, const StringName &p_method);
void call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error);
- Variant call(const StringName &p_method, const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant());
+ Variant call(const StringName &p_method, const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant(), const Variant &p_arg6 = Variant(), const Variant &p_arg7 = Variant(), const Variant &p_arg8 = Variant());
static void call_static(Variant::Type p_type, const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error);
@@ -528,6 +536,14 @@ public:
static void get_constructor_list(Type p_type, List<MethodInfo> *r_list); //convenience
+ /* Destructors */
+
+ // Only ptrcall is available.
+ typedef void (*PTRDestructor)(void *base);
+
+ static PTRDestructor get_ptr_destructor(Variant::Type p_type);
+ static bool has_destructor(Variant::Type p_type);
+
/* Properties */
void set_named(const StringName &p_member, const Variant &p_value, bool &r_valid);
@@ -586,7 +602,7 @@ public:
typedef void (*PTRKeyedSetter)(void *base, const void *key, const void *value);
typedef void (*PTRKeyedGetter)(const void *base, const void *key, void *value);
- typedef bool (*PTRKeyedChecker)(const void *base, const void *key);
+ typedef uint32_t (*PTRKeyedChecker)(const void *base, const void *key);
static PTRKeyedSetter get_member_ptr_keyed_setter(Variant::Type p_type);
static PTRKeyedGetter get_member_ptr_keyed_getter(Variant::Type p_type);
@@ -631,6 +647,7 @@ public:
static bool has_utility_function_return_value(const StringName &p_name);
static Variant::Type get_utility_function_return_type(const StringName &p_name);
static bool is_utility_function_vararg(const StringName &p_name);
+ static uint32_t get_utility_function_hash(const StringName &p_name);
static void get_utility_function_list(List<StringName> *r_functions);
static int get_utility_function_count();
@@ -645,6 +662,7 @@ public:
bool hash_compare(const Variant &p_variant) const;
bool booleanize() const;
String stringify(List<const void *> &stack) const;
+ String to_json_string() const;
void static_assign(const Variant &p_variant);
static void get_constants_for_type(Variant::Type p_type, List<StringName> *p_constants);
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 063611d415..39207df9e7 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -750,6 +750,42 @@ struct _VariantCall {
return 0;
}
+ static PackedInt32Array func_PackedByteArray_decode_s32_array(PackedByteArray *p_instance) {
+ uint64_t size = p_instance->size();
+ const uint8_t *r = p_instance->ptr();
+ PackedInt32Array dest;
+ dest.resize(size / sizeof(int32_t));
+ memcpy(dest.ptrw(), r, size);
+ return dest;
+ }
+
+ static PackedInt64Array func_PackedByteArray_decode_s64_array(PackedByteArray *p_instance) {
+ uint64_t size = p_instance->size();
+ const uint8_t *r = p_instance->ptr();
+ PackedInt64Array dest;
+ dest.resize(size / sizeof(int64_t));
+ memcpy(dest.ptrw(), r, size);
+ return dest;
+ }
+
+ static PackedFloat32Array func_PackedByteArray_decode_float_array(PackedByteArray *p_instance) {
+ uint64_t size = p_instance->size();
+ const uint8_t *r = p_instance->ptr();
+ PackedFloat32Array dest;
+ dest.resize(size / sizeof(float));
+ memcpy(dest.ptrw(), r, size);
+ return dest;
+ }
+
+ static PackedFloat64Array func_PackedByteArray_decode_double_array(PackedByteArray *p_instance) {
+ uint64_t size = p_instance->size();
+ const uint8_t *r = p_instance->ptr();
+ PackedFloat64Array dest;
+ dest.resize(size / sizeof(double));
+ memcpy(dest.ptrw(), r, size);
+ return dest;
+ }
+
static void func_PackedByteArray_encode_u8(PackedByteArray *p_instance, int64_t p_offset, int64_t p_value) {
uint64_t size = p_instance->size();
ERR_FAIL_COND(p_offset < 0 || p_offset > int64_t(size) - 1);
@@ -972,7 +1008,7 @@ void Variant::call(const StringName &p_method, const Variant **p_args, int p_arg
return;
}
#ifdef DEBUG_ENABLED
- if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (EngineDebugger::is_active() && !_get_obj().id.is_ref_counted() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
return;
}
@@ -1088,8 +1124,8 @@ bool Variant::has_builtin_method_return_value(Variant::Type p_type, const String
void Variant::get_builtin_method_list(Variant::Type p_type, List<StringName> *p_list) {
ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX);
- for (List<StringName>::Element *E = builtin_method_names[p_type].front(); E; E = E->next()) {
- p_list->push_back(E->get());
+ for (const StringName &E : builtin_method_names[p_type]) {
+ p_list->push_back(E);
}
}
@@ -1126,6 +1162,25 @@ bool Variant::is_builtin_method_vararg(Variant::Type p_type, const StringName &p
return method->is_vararg;
}
+uint32_t Variant::get_builtin_method_hash(Variant::Type p_type, const StringName &p_method) {
+ ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, 0);
+ const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
+ ERR_FAIL_COND_V(!method, 0);
+ uint32_t hash = hash_djb2_one_32(method->is_const);
+ hash = hash_djb2_one_32(method->is_static, hash);
+ hash = hash_djb2_one_32(method->is_vararg, hash);
+ hash = hash_djb2_one_32(method->has_return_type, hash);
+ if (method->has_return_type) {
+ hash = hash_djb2_one_32(method->return_type, hash);
+ }
+ hash = hash_djb2_one_32(method->argument_count, hash);
+ for (int i = 0; i < method->argument_count; i++) {
+ hash = method->get_argument_type(i);
+ }
+
+ return hash;
+}
+
void Variant::get_method_list(List<MethodInfo> *p_list) const {
if (type == OBJECT) {
Object *obj = get_validated_object();
@@ -1133,12 +1188,12 @@ void Variant::get_method_list(List<MethodInfo> *p_list) const {
obj->get_method_list(p_list);
}
} else {
- for (List<StringName>::Element *E = builtin_method_names[type].front(); E; E = E->next()) {
- const VariantBuiltInMethodInfo *method = builtin_method_info[type].lookup_ptr(E->get());
+ for (const StringName &E : builtin_method_names[type]) {
+ const VariantBuiltInMethodInfo *method = builtin_method_info[type].lookup_ptr(E);
ERR_CONTINUE(!method);
MethodInfo mi;
- mi.name = E->get();
+ mi.name = E;
//return type
if (method->has_return_type) {
@@ -1365,8 +1420,9 @@ static void _register_variant_builtin_methods() {
// FIXME: Static function, not sure how to bind
//bind_method(String, humanize_size, sarray("size"), varray());
- bind_method(String, is_abs_path, sarray(), varray());
- bind_method(String, is_rel_path, sarray(), varray());
+ bind_method(String, is_absolute_path, sarray(), varray());
+ bind_method(String, is_relative_path, sarray(), varray());
+ bind_method(String, simplify_path, sarray(), varray());
bind_method(String, get_base_dir, sarray(), varray());
bind_method(String, get_file, sarray(), varray());
bind_method(String, xml_escape, sarray("escape_quotes"), varray(false));
@@ -1380,7 +1436,7 @@ static void _register_variant_builtin_methods() {
bind_method(String, validate_node_name, sarray(), varray());
bind_method(String, is_valid_identifier, sarray(), varray());
- bind_method(String, is_valid_integer, sarray(), varray());
+ bind_method(String, is_valid_int, sarray(), varray());
bind_method(String, is_valid_float, sarray(), varray());
bind_method(String, is_valid_hex_number, sarray("with_prefix"), varray(false));
bind_method(String, is_valid_html_color, sarray(), varray());
@@ -1419,6 +1475,7 @@ static void _register_variant_builtin_methods() {
bind_method(Vector2, distance_squared_to, sarray("to"), varray());
bind_method(Vector2, length, sarray(), varray());
bind_method(Vector2, length_squared, sarray(), varray());
+ bind_method(Vector2, limit_length, sarray("length"), varray(1.0));
bind_method(Vector2, normalized, sarray(), varray());
bind_method(Vector2, is_normalized, sarray(), varray());
bind_method(Vector2, is_equal_approx, sarray("to"), varray());
@@ -1442,14 +1499,17 @@ static void _register_variant_builtin_methods() {
bind_method(Vector2, cross, sarray("with"), varray());
bind_method(Vector2, abs, sarray(), varray());
bind_method(Vector2, sign, sarray(), varray());
+ bind_method(Vector2, clamp, sarray("min", "max"), varray());
bind_method(Vector2, snapped, sarray("step"), varray());
- bind_method(Vector2, clamped, sarray("length"), varray());
+
+ bind_static_method(Vector2, from_angle, sarray("angle"), varray());
/* Vector2i */
bind_method(Vector2i, aspect, sarray(), varray());
bind_method(Vector2i, sign, sarray(), varray());
bind_method(Vector2i, abs, sarray(), varray());
+ bind_method(Vector2i, clamp, sarray("min", "max"), varray());
/* Rect2 */
@@ -1493,10 +1553,12 @@ static void _register_variant_builtin_methods() {
bind_method(Vector3, distance_squared_to, sarray("b"), varray());
bind_method(Vector3, length, sarray(), varray());
bind_method(Vector3, length_squared, sarray(), varray());
+ bind_method(Vector3, limit_length, sarray("length"), varray(1.0));
bind_method(Vector3, normalized, sarray(), varray());
bind_method(Vector3, is_normalized, sarray(), varray());
bind_method(Vector3, is_equal_approx, sarray("to"), varray());
bind_method(Vector3, inverse, sarray(), varray());
+ bind_method(Vector3, clamp, sarray("min", "max"), varray());
bind_method(Vector3, snapped, sarray("step"), varray());
bind_method(Vector3, rotated, sarray("by_axis", "phi"), varray());
bind_method(Vector3, lerp, sarray("to", "weight"), varray());
@@ -1525,6 +1587,7 @@ static void _register_variant_builtin_methods() {
bind_method(Vector3i, max_axis, sarray(), varray());
bind_method(Vector3i, sign, sarray(), varray());
bind_method(Vector3i, abs, sarray(), varray());
+ bind_method(Vector3i, clamp, sarray("min", "max"), varray());
/* Plane */
@@ -1539,19 +1602,20 @@ static void _register_variant_builtin_methods() {
bind_methodv(Plane, intersects_ray, &Plane::intersects_ray_bind, sarray("from", "dir"), varray());
bind_methodv(Plane, intersects_segment, &Plane::intersects_segment_bind, sarray("from", "to"), varray());
- /* Quat */
-
- bind_method(Quat, length, sarray(), varray());
- bind_method(Quat, length_squared, sarray(), varray());
- bind_method(Quat, normalized, sarray(), varray());
- bind_method(Quat, is_normalized, sarray(), varray());
- bind_method(Quat, is_equal_approx, sarray("to"), varray());
- bind_method(Quat, inverse, sarray(), varray());
- bind_method(Quat, dot, sarray("with"), varray());
- bind_method(Quat, slerp, sarray("to", "weight"), varray());
- bind_method(Quat, slerpni, sarray("to", "weight"), varray());
- bind_method(Quat, cubic_slerp, sarray("b", "pre_a", "post_b", "weight"), varray());
- bind_method(Quat, get_euler, sarray(), varray());
+ /* Quaternion */
+
+ bind_method(Quaternion, length, sarray(), varray());
+ bind_method(Quaternion, length_squared, sarray(), varray());
+ bind_method(Quaternion, normalized, sarray(), varray());
+ bind_method(Quaternion, is_normalized, sarray(), varray());
+ bind_method(Quaternion, is_equal_approx, sarray("to"), varray());
+ bind_method(Quaternion, inverse, sarray(), varray());
+ bind_method(Quaternion, angle_to, sarray("to"), varray());
+ bind_method(Quaternion, dot, sarray("with"), varray());
+ bind_method(Quaternion, slerp, sarray("to", "weight"), varray());
+ bind_method(Quaternion, slerpni, sarray("to", "weight"), varray());
+ bind_method(Quaternion, cubic_slerp, sarray("b", "pre_a", "post_b", "weight"), varray());
+ bind_method(Quaternion, get_euler, sarray(), varray());
/* Color */
@@ -1562,6 +1626,7 @@ static void _register_variant_builtin_methods() {
bind_method(Color, to_abgr64, sarray(), varray());
bind_method(Color, to_rgba64, sarray(), varray());
+ bind_method(Color, clamp, sarray("min", "max"), varray(Color(0, 0, 0, 0), Color(1, 1, 1, 1)));
bind_method(Color, inverted, sarray(), varray());
bind_method(Color, lerp, sarray("to", "weight"), varray());
bind_method(Color, lightened, sarray("amount"), varray());
@@ -1604,6 +1669,7 @@ static void _register_variant_builtin_methods() {
bind_method(Callable, is_null, sarray(), varray());
bind_method(Callable, is_custom, sarray(), varray());
bind_method(Callable, is_standard, sarray(), varray());
+ bind_method(Callable, is_valid, sarray(), varray());
bind_method(Callable, get_object, sarray(), varray());
bind_method(Callable, get_object_id, sarray(), varray());
bind_method(Callable, get_method, sarray(), varray());
@@ -1645,6 +1711,8 @@ static void _register_variant_builtin_methods() {
bind_method(Transform2D, basis_xform_inv, sarray("v"), varray());
bind_method(Transform2D, interpolate_with, sarray("xform", "weight"), varray());
bind_method(Transform2D, is_equal_approx, sarray("xform"), varray());
+ bind_method(Transform2D, set_rotation, sarray("rotation"), varray());
+ bind_method(Transform2D, looking_at, sarray("target"), varray(Vector2()));
/* Basis */
@@ -1662,7 +1730,8 @@ static void _register_variant_builtin_methods() {
bind_method(Basis, get_orthogonal_index, sarray(), varray());
bind_method(Basis, slerp, sarray("to", "weight"), varray());
bind_method(Basis, is_equal_approx, sarray("b"), varray());
- bind_method(Basis, get_rotation_quat, sarray(), varray());
+ bind_method(Basis, get_rotation_quaternion, sarray(), varray());
+ bind_static_method(Basis, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0)));
/* AABB */
@@ -1690,17 +1759,17 @@ static void _register_variant_builtin_methods() {
bind_methodv(AABB, intersects_segment, &AABB::intersects_segment_bind, sarray("from", "to"), varray());
bind_methodv(AABB, intersects_ray, &AABB::intersects_ray_bind, sarray("from", "dir"), varray());
- /* Transform */
+ /* Transform3D */
- bind_method(Transform, inverse, sarray(), varray());
- bind_method(Transform, affine_inverse, sarray(), varray());
- bind_method(Transform, orthonormalized, sarray(), varray());
- bind_method(Transform, rotated, sarray("axis", "phi"), varray());
- bind_method(Transform, scaled, sarray("scale"), varray());
- bind_method(Transform, translated, sarray("offset"), varray());
- bind_method(Transform, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0)));
- bind_method(Transform, interpolate_with, sarray("xform", "weight"), varray());
- bind_method(Transform, is_equal_approx, sarray("xform"), varray());
+ bind_method(Transform3D, inverse, sarray(), varray());
+ bind_method(Transform3D, affine_inverse, sarray(), varray());
+ bind_method(Transform3D, orthonormalized, sarray(), varray());
+ bind_method(Transform3D, rotated, sarray("axis", "phi"), varray());
+ bind_method(Transform3D, scaled, sarray("scale"), varray());
+ bind_method(Transform3D, translated, sarray("offset"), varray());
+ bind_method(Transform3D, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0)));
+ bind_method(Transform3D, interpolate_with, sarray("xform", "weight"), varray());
+ bind_method(Transform3D, is_equal_approx, sarray("xform"), varray());
/* Dictionary */
@@ -1740,6 +1809,7 @@ static void _register_variant_builtin_methods() {
bind_method(Array, has, sarray("value"), varray());
bind_method(Array, pop_back, sarray(), varray());
bind_method(Array, pop_front, sarray(), varray());
+ bind_method(Array, pop_at, sarray("position"), varray());
bind_method(Array, sort, sarray(), varray());
bind_method(Array, sort_custom, sarray("func"), varray());
bind_method(Array, shuffle, sarray(), varray());
@@ -1795,6 +1865,11 @@ static void _register_variant_builtin_methods() {
bind_function(PackedByteArray, decode_var, _VariantCall::func_PackedByteArray_decode_var, sarray("byte_offset", "allow_objects"), varray(false));
bind_function(PackedByteArray, decode_var_size, _VariantCall::func_PackedByteArray_decode_var_size, sarray("byte_offset", "allow_objects"), varray(false));
+ bind_function(PackedByteArray, to_int32_array, _VariantCall::func_PackedByteArray_decode_s32_array, sarray(), varray());
+ bind_function(PackedByteArray, to_int64_array, _VariantCall::func_PackedByteArray_decode_s64_array, sarray(), varray());
+ bind_function(PackedByteArray, to_float32_array, _VariantCall::func_PackedByteArray_decode_float_array, sarray(), varray());
+ bind_function(PackedByteArray, to_float64_array, _VariantCall::func_PackedByteArray_decode_double_array, sarray(), varray());
+
bind_functionnc(PackedByteArray, encode_u8, _VariantCall::func_PackedByteArray_encode_u8, sarray("byte_offset", "value"), varray());
bind_functionnc(PackedByteArray, encode_s8, _VariantCall::func_PackedByteArray_encode_s8, sarray("byte_offset", "value"), varray());
bind_functionnc(PackedByteArray, encode_u16, _VariantCall::func_PackedByteArray_encode_u16, sarray("byte_offset", "value"), varray());
@@ -1973,7 +2048,7 @@ static void _register_variant_builtin_methods() {
_VariantCall::add_variant_constant(Variant::VECTOR3, "ZERO", Vector3(0, 0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR3, "ONE", Vector3(1, 1, 1));
- _VariantCall::add_variant_constant(Variant::VECTOR3, "INF", Vector3(Math_INF, Math_INF, Math_INF));
+ _VariantCall::add_variant_constant(Variant::VECTOR3, "INF", Vector3(INFINITY, INFINITY, INFINITY));
_VariantCall::add_variant_constant(Variant::VECTOR3, "LEFT", Vector3(-1, 0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR3, "RIGHT", Vector3(1, 0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR3, "UP", Vector3(0, 1, 0));
@@ -2002,7 +2077,7 @@ static void _register_variant_builtin_methods() {
_VariantCall::add_variant_constant(Variant::VECTOR2, "ZERO", Vector2(0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR2, "ONE", Vector2(1, 1));
- _VariantCall::add_variant_constant(Variant::VECTOR2, "INF", Vector2(Math_INF, Math_INF));
+ _VariantCall::add_variant_constant(Variant::VECTOR2, "INF", Vector2(INFINITY, INFINITY));
_VariantCall::add_variant_constant(Variant::VECTOR2, "LEFT", Vector2(-1, 0));
_VariantCall::add_variant_constant(Variant::VECTOR2, "RIGHT", Vector2(1, 0));
_VariantCall::add_variant_constant(Variant::VECTOR2, "UP", Vector2(0, -1));
@@ -2019,14 +2094,14 @@ static void _register_variant_builtin_methods() {
_VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_X", Transform2D(-1, 0, 0, 1, 0, 0));
_VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_Y", Transform2D(1, 0, 0, -1, 0, 0));
- Transform identity_transform = Transform();
- Transform flip_x_transform = Transform(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0);
- Transform flip_y_transform = Transform(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0);
- Transform flip_z_transform = Transform(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0);
- _VariantCall::add_variant_constant(Variant::TRANSFORM, "IDENTITY", identity_transform);
- _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_X", flip_x_transform);
- _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Y", flip_y_transform);
- _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Z", flip_z_transform);
+ Transform3D identity_transform = Transform3D();
+ Transform3D flip_x_transform = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0);
+ Transform3D flip_y_transform = Transform3D(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0);
+ Transform3D flip_z_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0);
+ _VariantCall::add_variant_constant(Variant::TRANSFORM3D, "IDENTITY", identity_transform);
+ _VariantCall::add_variant_constant(Variant::TRANSFORM3D, "FLIP_X", flip_x_transform);
+ _VariantCall::add_variant_constant(Variant::TRANSFORM3D, "FLIP_Y", flip_y_transform);
+ _VariantCall::add_variant_constant(Variant::TRANSFORM3D, "FLIP_Z", flip_z_transform);
Basis identity_basis = Basis();
Basis flip_x_basis = Basis(-1, 0, 0, 0, 1, 0, 0, 0, 1);
@@ -2041,7 +2116,7 @@ static void _register_variant_builtin_methods() {
_VariantCall::add_variant_constant(Variant::PLANE, "PLANE_XZ", Plane(Vector3(0, 1, 0), 0));
_VariantCall::add_variant_constant(Variant::PLANE, "PLANE_XY", Plane(Vector3(0, 0, 1), 0));
- _VariantCall::add_variant_constant(Variant::QUAT, "IDENTITY", Quat(0, 0, 0, 1));
+ _VariantCall::add_variant_constant(Variant::QUATERNION, "IDENTITY", Quaternion(0, 0, 0, 1));
}
void Variant::_register_variant_methods() {
diff --git a/core/variant/variant_construct.cpp b/core/variant/variant_construct.cpp
index f0c9e52b46..a1a2bec369 100644
--- a/core/variant/variant_construct.cpp
+++ b/core/variant/variant_construct.cpp
@@ -28,543 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "variant.h"
-
-#include "core/core_string_names.h"
-#include "core/crypto/crypto_core.h"
-#include "core/debugger/engine_debugger.h"
-#include "core/io/compression.h"
-#include "core/object/class_db.h"
-#include "core/os/os.h"
-#include "core/templates/local_vector.h"
-#include "core/templates/oa_hash_map.h"
-
-template <class T>
-struct PtrConstruct {};
-
-#define MAKE_PTRCONSTRUCT(m_type) \
- template <> \
- struct PtrConstruct<m_type> { \
- _FORCE_INLINE_ static void construct(const m_type &p_value, void *p_ptr) { \
- memnew_placement(p_ptr, m_type(p_value)); \
- } \
- };
-
-MAKE_PTRCONSTRUCT(bool);
-MAKE_PTRCONSTRUCT(int64_t);
-MAKE_PTRCONSTRUCT(double);
-MAKE_PTRCONSTRUCT(String);
-MAKE_PTRCONSTRUCT(Vector2);
-MAKE_PTRCONSTRUCT(Vector2i);
-MAKE_PTRCONSTRUCT(Rect2);
-MAKE_PTRCONSTRUCT(Rect2i);
-MAKE_PTRCONSTRUCT(Vector3);
-MAKE_PTRCONSTRUCT(Vector3i);
-MAKE_PTRCONSTRUCT(Transform2D);
-MAKE_PTRCONSTRUCT(Plane);
-MAKE_PTRCONSTRUCT(Quat);
-MAKE_PTRCONSTRUCT(AABB);
-MAKE_PTRCONSTRUCT(Basis);
-MAKE_PTRCONSTRUCT(Transform);
-MAKE_PTRCONSTRUCT(Color);
-MAKE_PTRCONSTRUCT(StringName);
-MAKE_PTRCONSTRUCT(NodePath);
-MAKE_PTRCONSTRUCT(RID);
-
-template <>
-struct PtrConstruct<Object *> {
- _FORCE_INLINE_ static void construct(Object *p_value, void *p_ptr) {
- *((Object **)p_ptr) = p_value;
- }
-};
-
-MAKE_PTRCONSTRUCT(Callable);
-MAKE_PTRCONSTRUCT(Signal);
-MAKE_PTRCONSTRUCT(Dictionary);
-MAKE_PTRCONSTRUCT(Array);
-MAKE_PTRCONSTRUCT(PackedByteArray);
-MAKE_PTRCONSTRUCT(PackedInt32Array);
-MAKE_PTRCONSTRUCT(PackedInt64Array);
-MAKE_PTRCONSTRUCT(PackedFloat32Array);
-MAKE_PTRCONSTRUCT(PackedFloat64Array);
-MAKE_PTRCONSTRUCT(PackedStringArray);
-MAKE_PTRCONSTRUCT(PackedVector2Array);
-MAKE_PTRCONSTRUCT(PackedVector3Array);
-MAKE_PTRCONSTRUCT(PackedColorArray);
-MAKE_PTRCONSTRUCT(Variant);
-
-template <class T, class... P>
-class VariantConstructor {
- template <size_t... Is>
- static _FORCE_INLINE_ void construct_helper(T &base, const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) {
- r_error.error = Callable::CallError::CALL_OK;
-
-#ifdef DEBUG_METHODS_ENABLED
- base = T(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...);
-#else
- base = T(VariantCaster<P>::cast(*p_args[Is])...);
-#endif
- }
-
- template <size_t... Is>
- static _FORCE_INLINE_ void validated_construct_helper(T &base, const Variant **p_args, IndexSequence<Is...>) {
- base = T((*VariantGetInternalPtr<P>::get_ptr(p_args[Is]))...);
- }
-
- template <size_t... Is>
- static _FORCE_INLINE_ void ptr_construct_helper(void *base, const void **p_args, IndexSequence<Is...>) {
- PtrConstruct<T>::construct(T(PtrToArg<P>::convert(p_args[Is])...), base);
- }
-
-public:
- static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
- VariantTypeChanger<T>::change(&r_ret);
- construct_helper(*VariantGetInternalPtr<T>::get_ptr(&r_ret), p_args, r_error, BuildIndexSequence<sizeof...(P)>{});
- }
-
- static void validated_construct(Variant *r_ret, const Variant **p_args) {
- VariantTypeChanger<T>::change(r_ret);
- validated_construct_helper(*VariantGetInternalPtr<T>::get_ptr(r_ret), p_args, BuildIndexSequence<sizeof...(P)>{});
- }
- static void ptr_construct(void *base, const void **p_args) {
- ptr_construct_helper(base, p_args, BuildIndexSequence<sizeof...(P)>{});
- }
-
- static int get_argument_count() {
- return sizeof...(P);
- }
-
- static Variant::Type get_argument_type(int p_arg) {
- return call_get_argument_type<P...>(p_arg);
- }
-
- static Variant::Type get_base_type() {
- return GetTypeInfo<T>::VARIANT_TYPE;
- }
-};
-
-class VariantConstructorObject {
-public:
- static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
- VariantInternal::clear(&r_ret);
- if (p_args[0]->get_type() == Variant::NIL) {
- VariantInternal::object_assign_null(&r_ret);
- r_error.error = Callable::CallError::CALL_OK;
- } else if (p_args[0]->get_type() == Variant::OBJECT) {
- VariantInternal::object_assign(&r_ret, p_args[0]);
- r_error.error = Callable::CallError::CALL_OK;
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- }
- }
-
- static void validated_construct(Variant *r_ret, const Variant **p_args) {
- VariantInternal::clear(r_ret);
- VariantInternal::object_assign(r_ret, p_args[0]);
- }
- static void ptr_construct(void *base, const void **p_args) {
- PtrConstruct<Object *>::construct(PtrToArg<Object *>::convert(p_args[0]), base);
- }
-
- static int get_argument_count() {
- return 1;
- }
-
- static Variant::Type get_argument_type(int p_arg) {
- return Variant::OBJECT;
- }
-
- static Variant::Type get_base_type() {
- return Variant::OBJECT;
- }
-};
-
-class VariantConstructorNilObject {
-public:
- static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
- if (p_args[0]->get_type() != Variant::NIL) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::NIL;
- }
-
- VariantInternal::clear(&r_ret);
- VariantInternal::object_assign_null(&r_ret);
- }
-
- static void validated_construct(Variant *r_ret, const Variant **p_args) {
- VariantInternal::clear(r_ret);
- VariantInternal::object_assign_null(r_ret);
- }
- static void ptr_construct(void *base, const void **p_args) {
- PtrConstruct<Object *>::construct(nullptr, base);
- }
-
- static int get_argument_count() {
- return 1;
- }
-
- static Variant::Type get_argument_type(int p_arg) {
- return Variant::NIL;
- }
-
- static Variant::Type get_base_type() {
- return Variant::OBJECT;
- }
-};
-
-class VariantConstructorCallableArgs {
-public:
- static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
- ObjectID object_id;
- StringName method;
-
- if (p_args[0]->get_type() == Variant::NIL) {
- // leave as is
- } else if (p_args[0]->get_type() == Variant::OBJECT) {
- object_id = VariantInternal::get_object_id(p_args[0]);
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- return;
- }
-
- if (p_args[1]->get_type() == Variant::STRING_NAME) {
- method = *VariantGetInternalPtr<StringName>::get_ptr(p_args[1]);
- } else if (p_args[1]->get_type() == Variant::STRING) {
- method = *VariantGetInternalPtr<String>::get_ptr(p_args[1]);
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::STRING_NAME;
- return;
- }
-
- VariantTypeChanger<Callable>::change(&r_ret);
- *VariantGetInternalPtr<Callable>::get_ptr(&r_ret) = Callable(object_id, method);
- }
-
- static void validated_construct(Variant *r_ret, const Variant **p_args) {
- VariantTypeChanger<Callable>::change(r_ret);
- *VariantGetInternalPtr<Callable>::get_ptr(r_ret) = Callable(VariantInternal::get_object_id(p_args[0]), *VariantGetInternalPtr<StringName>::get_ptr(p_args[1]));
- }
- static void ptr_construct(void *base, const void **p_args) {
- PtrConstruct<Callable>::construct(Callable(PtrToArg<Object *>::convert(p_args[0]), PtrToArg<StringName>::convert(p_args[1])), base);
- }
-
- static int get_argument_count() {
- return 2;
- }
-
- static Variant::Type get_argument_type(int p_arg) {
- if (p_arg == 0) {
- return Variant::OBJECT;
- } else {
- return Variant::STRING_NAME;
- }
- }
-
- static Variant::Type get_base_type() {
- return Variant::CALLABLE;
- }
-};
-
-class VariantConstructorSignalArgs {
-public:
- static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
- ObjectID object_id;
- StringName method;
-
- if (p_args[0]->get_type() == Variant::NIL) {
- // leave as is
- } else if (p_args[0]->get_type() == Variant::OBJECT) {
- object_id = VariantInternal::get_object_id(p_args[0]);
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- return;
- }
-
- if (p_args[1]->get_type() == Variant::STRING_NAME) {
- method = *VariantGetInternalPtr<StringName>::get_ptr(p_args[1]);
- } else if (p_args[1]->get_type() == Variant::STRING) {
- method = *VariantGetInternalPtr<String>::get_ptr(p_args[1]);
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::STRING_NAME;
- return;
- }
-
- VariantTypeChanger<Signal>::change(&r_ret);
- *VariantGetInternalPtr<Signal>::get_ptr(&r_ret) = Signal(object_id, method);
- }
-
- static void validated_construct(Variant *r_ret, const Variant **p_args) {
- VariantTypeChanger<Signal>::change(r_ret);
- *VariantGetInternalPtr<Signal>::get_ptr(r_ret) = Signal(VariantInternal::get_object_id(p_args[0]), *VariantGetInternalPtr<StringName>::get_ptr(p_args[1]));
- }
- static void ptr_construct(void *base, const void **p_args) {
- PtrConstruct<Signal>::construct(Signal(PtrToArg<Object *>::convert(p_args[0]), PtrToArg<StringName>::convert(p_args[1])), base);
- }
-
- static int get_argument_count() {
- return 2;
- }
-
- static Variant::Type get_argument_type(int p_arg) {
- if (p_arg == 0) {
- return Variant::OBJECT;
- } else {
- return Variant::STRING_NAME;
- }
- }
-
- static Variant::Type get_base_type() {
- return Variant::SIGNAL;
- }
-};
-
-template <class T>
-class VariantConstructorToArray {
-public:
- static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
- if (p_args[0]->get_type() != GetTypeInfo<T>::VARIANT_TYPE) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = GetTypeInfo<T>::VARIANT_TYPE;
- return;
- }
-
- VariantTypeChanger<Array>::change(&r_ret);
- Array &dst_arr = *VariantGetInternalPtr<Array>::get_ptr(&r_ret);
- const T &src_arr = *VariantGetInternalPtr<T>::get_ptr(p_args[0]);
-
- int size = src_arr.size();
- dst_arr.resize(size);
- for (int i = 0; i < size; i++) {
- dst_arr[i] = src_arr[i];
- }
- }
-
- static void validated_construct(Variant *r_ret, const Variant **p_args) {
- VariantTypeChanger<Array>::change(r_ret);
- Array &dst_arr = *VariantGetInternalPtr<Array>::get_ptr(r_ret);
- const T &src_arr = *VariantGetInternalPtr<T>::get_ptr(p_args[0]);
-
- int size = src_arr.size();
- dst_arr.resize(size);
- for (int i = 0; i < size; i++) {
- dst_arr[i] = src_arr[i];
- }
- }
- static void ptr_construct(void *base, const void **p_args) {
- Array dst_arr;
- T src_arr = PtrToArg<T>::convert(p_args[0]);
-
- int size = src_arr.size();
- dst_arr.resize(size);
- for (int i = 0; i < size; i++) {
- dst_arr[i] = src_arr[i];
- }
-
- PtrConstruct<Array>::construct(dst_arr, base);
- }
-
- static int get_argument_count() {
- return 1;
- }
-
- static Variant::Type get_argument_type(int p_arg) {
- return GetTypeInfo<T>::VARIANT_TYPE;
- }
-
- static Variant::Type get_base_type() {
- return Variant::ARRAY;
- }
-};
-
-template <class T>
-class VariantConstructorFromArray {
-public:
- static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
- if (p_args[0]->get_type() != Variant::ARRAY) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::ARRAY;
- return;
- }
-
- VariantTypeChanger<T>::change(&r_ret);
- const Array &src_arr = *VariantGetInternalPtr<Array>::get_ptr(p_args[0]);
- T &dst_arr = *VariantGetInternalPtr<T>::get_ptr(&r_ret);
-
- int size = src_arr.size();
- dst_arr.resize(size);
- for (int i = 0; i < size; i++) {
- dst_arr.write[i] = src_arr[i];
- }
- }
-
- static void validated_construct(Variant *r_ret, const Variant **p_args) {
- VariantTypeChanger<T>::change(r_ret);
- const Array &src_arr = *VariantGetInternalPtr<Array>::get_ptr(p_args[0]);
- T &dst_arr = *VariantGetInternalPtr<T>::get_ptr(r_ret);
-
- int size = src_arr.size();
- dst_arr.resize(size);
- for (int i = 0; i < size; i++) {
- dst_arr.write[i] = src_arr[i];
- }
- }
- static void ptr_construct(void *base, const void **p_args) {
- Array src_arr = PtrToArg<Array>::convert(p_args[0]);
- T dst_arr;
-
- int size = src_arr.size();
- dst_arr.resize(size);
- for (int i = 0; i < size; i++) {
- dst_arr.write[i] = src_arr[i];
- }
-
- PtrConstruct<T>::construct(dst_arr, base);
- }
-
- static int get_argument_count() {
- return 1;
- }
-
- static Variant::Type get_argument_type(int p_arg) {
- return Variant::ARRAY;
- }
-
- static Variant::Type get_base_type() {
- return GetTypeInfo<T>::VARIANT_TYPE;
- }
-};
-
-class VariantConstructorNil {
-public:
- static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
- if (p_args[0]->get_type() != Variant::NIL) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::NIL;
- return;
- }
-
- r_error.error = Callable::CallError::CALL_OK;
- VariantInternal::clear(&r_ret);
- }
-
- static void validated_construct(Variant *r_ret, const Variant **p_args) {
- VariantInternal::clear(r_ret);
- }
- static void ptr_construct(void *base, const void **p_args) {
- PtrConstruct<Variant>::construct(Variant(), base);
- }
-
- static int get_argument_count() {
- return 1;
- }
-
- static Variant::Type get_argument_type(int p_arg) {
- return Variant::NIL;
- }
-
- static Variant::Type get_base_type() {
- return Variant::NIL;
- }
-};
-
-template <class T>
-class VariantConstructNoArgs {
-public:
- static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
- VariantTypeChanger<T>::change_and_reset(&r_ret);
- r_error.error = Callable::CallError::CALL_OK;
- }
-
- static void validated_construct(Variant *r_ret, const Variant **p_args) {
- VariantTypeChanger<T>::change_and_reset(r_ret);
- }
- static void ptr_construct(void *base, const void **p_args) {
- PtrConstruct<T>::construct(T(), base);
- }
-
- static int get_argument_count() {
- return 0;
- }
-
- static Variant::Type get_argument_type(int p_arg) {
- return Variant::NIL;
- }
-
- static Variant::Type get_base_type() {
- return GetTypeInfo<T>::VARIANT_TYPE;
- }
-};
-
-class VariantConstructNoArgsNil {
-public:
- static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
- VariantInternal::clear(&r_ret);
- r_error.error = Callable::CallError::CALL_OK;
- }
-
- static void validated_construct(Variant *r_ret, const Variant **p_args) {
- VariantInternal::clear(r_ret);
- }
- static void ptr_construct(void *base, const void **p_args) {
- ERR_FAIL_MSG("can't ptrcall nil constructor");
- }
-
- static int get_argument_count() {
- return 0;
- }
-
- static Variant::Type get_argument_type(int p_arg) {
- return Variant::NIL;
- }
-
- static Variant::Type get_base_type() {
- return Variant::NIL;
- }
-};
-
-class VariantConstructNoArgsObject {
-public:
- static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
- VariantInternal::clear(&r_ret);
- VariantInternal::object_assign_null(&r_ret);
- r_error.error = Callable::CallError::CALL_OK;
- }
-
- static void validated_construct(Variant *r_ret, const Variant **p_args) {
- VariantInternal::clear(r_ret);
- VariantInternal::object_assign_null(r_ret);
- }
- static void ptr_construct(void *base, const void **p_args) {
- PtrConstruct<Object *>::construct(nullptr, base);
- }
-
- static int get_argument_count() {
- return 0;
- }
-
- static Variant::Type get_argument_type(int p_arg) {
- return Variant::NIL;
- }
-
- static Variant::Type get_base_type() {
- return Variant::OBJECT;
- }
-};
+#include "variant_construct.h"
struct VariantConstructData {
void (*construct)(Variant &r_base, const Variant **p_args, Callable::CallError &r_error);
@@ -659,13 +123,13 @@ void Variant::_register_variant_constructors() {
add_constructor<VariantConstructor<Plane, Vector3, Vector3, Vector3>>(sarray("point1", "point2", "point3"));
add_constructor<VariantConstructor<Plane, double, double, double, double>>(sarray("a", "b", "c", "d"));
- add_constructor<VariantConstructNoArgs<Quat>>(sarray());
- add_constructor<VariantConstructor<Quat, Quat>>(sarray("from"));
- add_constructor<VariantConstructor<Quat, Basis>>(sarray("from"));
- add_constructor<VariantConstructor<Quat, Vector3>>(sarray("euler"));
- add_constructor<VariantConstructor<Quat, Vector3, double>>(sarray("axis", "angle"));
- add_constructor<VariantConstructor<Quat, Vector3, Vector3>>(sarray("arc_from", "arc_to"));
- add_constructor<VariantConstructor<Quat, double, double, double, double>>(sarray("x", "y", "z", "w"));
+ add_constructor<VariantConstructNoArgs<Quaternion>>(sarray());
+ add_constructor<VariantConstructor<Quaternion, Quaternion>>(sarray("from"));
+ add_constructor<VariantConstructor<Quaternion, Basis>>(sarray("from"));
+ add_constructor<VariantConstructor<Quaternion, Vector3>>(sarray("euler"));
+ add_constructor<VariantConstructor<Quaternion, Vector3, double>>(sarray("axis", "angle"));
+ add_constructor<VariantConstructor<Quaternion, Vector3, Vector3>>(sarray("arc_from", "arc_to"));
+ add_constructor<VariantConstructor<Quaternion, double, double, double, double>>(sarray("x", "y", "z", "w"));
add_constructor<VariantConstructNoArgs<::AABB>>(sarray());
add_constructor<VariantConstructor<::AABB, ::AABB>>(sarray("from"));
@@ -673,15 +137,15 @@ void Variant::_register_variant_constructors() {
add_constructor<VariantConstructNoArgs<Basis>>(sarray());
add_constructor<VariantConstructor<Basis, Basis>>(sarray("from"));
- add_constructor<VariantConstructor<Basis, Quat>>(sarray("from"));
+ add_constructor<VariantConstructor<Basis, Quaternion>>(sarray("from"));
add_constructor<VariantConstructor<Basis, Vector3>>(sarray("euler"));
add_constructor<VariantConstructor<Basis, Vector3, double>>(sarray("axis", "phi"));
add_constructor<VariantConstructor<Basis, Vector3, Vector3, Vector3>>(sarray("x_axis", "y_axis", "z_axis"));
- add_constructor<VariantConstructNoArgs<Transform>>(sarray());
- add_constructor<VariantConstructor<Transform, Transform>>(sarray("from"));
- add_constructor<VariantConstructor<Transform, Basis, Vector3>>(sarray("basis", "origin"));
- add_constructor<VariantConstructor<Transform, Vector3, Vector3, Vector3, Vector3>>(sarray("x_axis", "y_axis", "z_axis", "origin"));
+ add_constructor<VariantConstructNoArgs<Transform3D>>(sarray());
+ add_constructor<VariantConstructor<Transform3D, Transform3D>>(sarray("from"));
+ add_constructor<VariantConstructor<Transform3D, Basis, Vector3>>(sarray("basis", "origin"));
+ add_constructor<VariantConstructor<Transform3D, Vector3, Vector3, Vector3, Vector3>>(sarray("x_axis", "y_axis", "z_axis", "origin"));
add_constructor<VariantConstructNoArgs<Color>>(sarray());
add_constructor<VariantConstructor<Color, Color>>(sarray("from"));
@@ -836,9 +300,9 @@ String Variant::get_constructor_argument_name(Variant::Type p_type, int p_constr
void VariantInternal::object_assign(Variant *v, const Object *o) {
if (o) {
- if (o->is_reference()) {
- Reference *reference = const_cast<Reference *>(static_cast<const Reference *>(o));
- if (!reference->init_ref()) {
+ if (o->is_ref_counted()) {
+ RefCounted *ref_counted = const_cast<RefCounted *>(static_cast<const RefCounted *>(o));
+ if (!ref_counted->init_ref()) {
v->_get_obj().obj = nullptr;
v->_get_obj().id = ObjectID();
return;
diff --git a/core/variant/variant_construct.h b/core/variant/variant_construct.h
new file mode 100644
index 0000000000..b03f4a8d3b
--- /dev/null
+++ b/core/variant/variant_construct.h
@@ -0,0 +1,572 @@
+/*************************************************************************/
+/* variant_construct.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef VARIANT_CONSTRUCT_H
+#define VARIANT_CONSTRUCT_H
+
+#include "variant.h"
+
+#include "core/core_string_names.h"
+#include "core/crypto/crypto_core.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/io/compression.h"
+#include "core/object/class_db.h"
+#include "core/os/os.h"
+#include "core/templates/local_vector.h"
+#include "core/templates/oa_hash_map.h"
+
+template <class T>
+struct PtrConstruct {};
+
+#define MAKE_PTRCONSTRUCT(m_type) \
+ template <> \
+ struct PtrConstruct<m_type> { \
+ _FORCE_INLINE_ static void construct(const m_type &p_value, void *p_ptr) { \
+ memnew_placement(p_ptr, m_type(p_value)); \
+ } \
+ };
+
+MAKE_PTRCONSTRUCT(bool);
+MAKE_PTRCONSTRUCT(int64_t);
+MAKE_PTRCONSTRUCT(double);
+MAKE_PTRCONSTRUCT(String);
+MAKE_PTRCONSTRUCT(Vector2);
+MAKE_PTRCONSTRUCT(Vector2i);
+MAKE_PTRCONSTRUCT(Rect2);
+MAKE_PTRCONSTRUCT(Rect2i);
+MAKE_PTRCONSTRUCT(Vector3);
+MAKE_PTRCONSTRUCT(Vector3i);
+MAKE_PTRCONSTRUCT(Transform2D);
+MAKE_PTRCONSTRUCT(Plane);
+MAKE_PTRCONSTRUCT(Quaternion);
+MAKE_PTRCONSTRUCT(AABB);
+MAKE_PTRCONSTRUCT(Basis);
+MAKE_PTRCONSTRUCT(Transform3D);
+MAKE_PTRCONSTRUCT(Color);
+MAKE_PTRCONSTRUCT(StringName);
+MAKE_PTRCONSTRUCT(NodePath);
+MAKE_PTRCONSTRUCT(RID);
+
+template <>
+struct PtrConstruct<Object *> {
+ _FORCE_INLINE_ static void construct(Object *p_value, void *p_ptr) {
+ *((Object **)p_ptr) = p_value;
+ }
+};
+
+MAKE_PTRCONSTRUCT(Callable);
+MAKE_PTRCONSTRUCT(Signal);
+MAKE_PTRCONSTRUCT(Dictionary);
+MAKE_PTRCONSTRUCT(Array);
+MAKE_PTRCONSTRUCT(PackedByteArray);
+MAKE_PTRCONSTRUCT(PackedInt32Array);
+MAKE_PTRCONSTRUCT(PackedInt64Array);
+MAKE_PTRCONSTRUCT(PackedFloat32Array);
+MAKE_PTRCONSTRUCT(PackedFloat64Array);
+MAKE_PTRCONSTRUCT(PackedStringArray);
+MAKE_PTRCONSTRUCT(PackedVector2Array);
+MAKE_PTRCONSTRUCT(PackedVector3Array);
+MAKE_PTRCONSTRUCT(PackedColorArray);
+MAKE_PTRCONSTRUCT(Variant);
+
+template <class T, class... P>
+class VariantConstructor {
+ template <size_t... Is>
+ static _FORCE_INLINE_ void construct_helper(T &base, const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) {
+ r_error.error = Callable::CallError::CALL_OK;
+
+#ifdef DEBUG_METHODS_ENABLED
+ base = T(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...);
+#else
+ base = T(VariantCaster<P>::cast(*p_args[Is])...);
+#endif
+ }
+
+ template <size_t... Is>
+ static _FORCE_INLINE_ void validated_construct_helper(T &base, const Variant **p_args, IndexSequence<Is...>) {
+ base = T((*VariantGetInternalPtr<P>::get_ptr(p_args[Is]))...);
+ }
+
+ template <size_t... Is>
+ static _FORCE_INLINE_ void ptr_construct_helper(void *base, const void **p_args, IndexSequence<Is...>) {
+ PtrConstruct<T>::construct(T(PtrToArg<P>::convert(p_args[Is])...), base);
+ }
+
+public:
+ static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+ VariantTypeChanger<T>::change(&r_ret);
+ construct_helper(*VariantGetInternalPtr<T>::get_ptr(&r_ret), p_args, r_error, BuildIndexSequence<sizeof...(P)>{});
+ }
+
+ static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
+ VariantTypeChanger<T>::change(r_ret);
+ validated_construct_helper(*VariantGetInternalPtr<T>::get_ptr(r_ret), p_args, BuildIndexSequence<sizeof...(P)>{});
+ }
+ static void ptr_construct(void *base, const void **p_args) {
+ ptr_construct_helper(base, p_args, BuildIndexSequence<sizeof...(P)>{});
+ }
+
+ static int get_argument_count() {
+ return sizeof...(P);
+ }
+
+ static Variant::Type get_argument_type(int p_arg) {
+ return call_get_argument_type<P...>(p_arg);
+ }
+
+ static Variant::Type get_base_type() {
+ return GetTypeInfo<T>::VARIANT_TYPE;
+ }
+};
+
+class VariantConstructorObject {
+public:
+ static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
+ VariantInternal::clear(&r_ret);
+ if (p_args[0]->get_type() == Variant::NIL) {
+ VariantInternal::object_assign_null(&r_ret);
+ r_error.error = Callable::CallError::CALL_OK;
+ } else if (p_args[0]->get_type() == Variant::OBJECT) {
+ VariantInternal::object_assign(&r_ret, p_args[0]);
+ r_error.error = Callable::CallError::CALL_OK;
+ } else {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ }
+ }
+
+ static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
+ VariantInternal::clear(r_ret);
+ VariantInternal::object_assign(r_ret, p_args[0]);
+ }
+ static void ptr_construct(void *base, const void **p_args) {
+ PtrConstruct<Object *>::construct(PtrToArg<Object *>::convert(p_args[0]), base);
+ }
+
+ static int get_argument_count() {
+ return 1;
+ }
+
+ static Variant::Type get_argument_type(int p_arg) {
+ return Variant::OBJECT;
+ }
+
+ static Variant::Type get_base_type() {
+ return Variant::OBJECT;
+ }
+};
+
+class VariantConstructorNilObject {
+public:
+ static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
+ if (p_args[0]->get_type() != Variant::NIL) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ }
+
+ VariantInternal::clear(&r_ret);
+ VariantInternal::object_assign_null(&r_ret);
+ }
+
+ static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
+ VariantInternal::clear(r_ret);
+ VariantInternal::object_assign_null(r_ret);
+ }
+ static void ptr_construct(void *base, const void **p_args) {
+ PtrConstruct<Object *>::construct(nullptr, base);
+ }
+
+ static int get_argument_count() {
+ return 1;
+ }
+
+ static Variant::Type get_argument_type(int p_arg) {
+ return Variant::NIL;
+ }
+
+ static Variant::Type get_base_type() {
+ return Variant::OBJECT;
+ }
+};
+
+class VariantConstructorCallableArgs {
+public:
+ static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
+ ObjectID object_id;
+ StringName method;
+
+ if (p_args[0]->get_type() == Variant::NIL) {
+ // leave as is
+ } else if (p_args[0]->get_type() == Variant::OBJECT) {
+ object_id = VariantInternal::get_object_id(p_args[0]);
+ } else {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ return;
+ }
+
+ if (p_args[1]->get_type() == Variant::STRING_NAME) {
+ method = *VariantGetInternalPtr<StringName>::get_ptr(p_args[1]);
+ } else if (p_args[1]->get_type() == Variant::STRING) {
+ method = *VariantGetInternalPtr<String>::get_ptr(p_args[1]);
+ } else {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 1;
+ r_error.expected = Variant::STRING_NAME;
+ return;
+ }
+
+ VariantTypeChanger<Callable>::change(&r_ret);
+ *VariantGetInternalPtr<Callable>::get_ptr(&r_ret) = Callable(object_id, method);
+ }
+
+ static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
+ VariantTypeChanger<Callable>::change(r_ret);
+ *VariantGetInternalPtr<Callable>::get_ptr(r_ret) = Callable(VariantInternal::get_object_id(p_args[0]), *VariantGetInternalPtr<StringName>::get_ptr(p_args[1]));
+ }
+ static void ptr_construct(void *base, const void **p_args) {
+ PtrConstruct<Callable>::construct(Callable(PtrToArg<Object *>::convert(p_args[0]), PtrToArg<StringName>::convert(p_args[1])), base);
+ }
+
+ static int get_argument_count() {
+ return 2;
+ }
+
+ static Variant::Type get_argument_type(int p_arg) {
+ if (p_arg == 0) {
+ return Variant::OBJECT;
+ } else {
+ return Variant::STRING_NAME;
+ }
+ }
+
+ static Variant::Type get_base_type() {
+ return Variant::CALLABLE;
+ }
+};
+
+class VariantConstructorSignalArgs {
+public:
+ static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
+ ObjectID object_id;
+ StringName method;
+
+ if (p_args[0]->get_type() == Variant::NIL) {
+ // leave as is
+ } else if (p_args[0]->get_type() == Variant::OBJECT) {
+ object_id = VariantInternal::get_object_id(p_args[0]);
+ } else {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ return;
+ }
+
+ if (p_args[1]->get_type() == Variant::STRING_NAME) {
+ method = *VariantGetInternalPtr<StringName>::get_ptr(p_args[1]);
+ } else if (p_args[1]->get_type() == Variant::STRING) {
+ method = *VariantGetInternalPtr<String>::get_ptr(p_args[1]);
+ } else {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 1;
+ r_error.expected = Variant::STRING_NAME;
+ return;
+ }
+
+ VariantTypeChanger<Signal>::change(&r_ret);
+ *VariantGetInternalPtr<Signal>::get_ptr(&r_ret) = Signal(object_id, method);
+ }
+
+ static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
+ VariantTypeChanger<Signal>::change(r_ret);
+ *VariantGetInternalPtr<Signal>::get_ptr(r_ret) = Signal(VariantInternal::get_object_id(p_args[0]), *VariantGetInternalPtr<StringName>::get_ptr(p_args[1]));
+ }
+ static void ptr_construct(void *base, const void **p_args) {
+ PtrConstruct<Signal>::construct(Signal(PtrToArg<Object *>::convert(p_args[0]), PtrToArg<StringName>::convert(p_args[1])), base);
+ }
+
+ static int get_argument_count() {
+ return 2;
+ }
+
+ static Variant::Type get_argument_type(int p_arg) {
+ if (p_arg == 0) {
+ return Variant::OBJECT;
+ } else {
+ return Variant::STRING_NAME;
+ }
+ }
+
+ static Variant::Type get_base_type() {
+ return Variant::SIGNAL;
+ }
+};
+
+template <class T>
+class VariantConstructorToArray {
+public:
+ static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
+ if (p_args[0]->get_type() != GetTypeInfo<T>::VARIANT_TYPE) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = GetTypeInfo<T>::VARIANT_TYPE;
+ return;
+ }
+
+ VariantTypeChanger<Array>::change(&r_ret);
+ Array &dst_arr = *VariantGetInternalPtr<Array>::get_ptr(&r_ret);
+ const T &src_arr = *VariantGetInternalPtr<T>::get_ptr(p_args[0]);
+
+ int size = src_arr.size();
+ dst_arr.resize(size);
+ for (int i = 0; i < size; i++) {
+ dst_arr[i] = src_arr[i];
+ }
+ }
+
+ static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
+ VariantTypeChanger<Array>::change(r_ret);
+ Array &dst_arr = *VariantGetInternalPtr<Array>::get_ptr(r_ret);
+ const T &src_arr = *VariantGetInternalPtr<T>::get_ptr(p_args[0]);
+
+ int size = src_arr.size();
+ dst_arr.resize(size);
+ for (int i = 0; i < size; i++) {
+ dst_arr[i] = src_arr[i];
+ }
+ }
+ static void ptr_construct(void *base, const void **p_args) {
+ Array dst_arr;
+ T src_arr = PtrToArg<T>::convert(p_args[0]);
+
+ int size = src_arr.size();
+ dst_arr.resize(size);
+ for (int i = 0; i < size; i++) {
+ dst_arr[i] = src_arr[i];
+ }
+
+ PtrConstruct<Array>::construct(dst_arr, base);
+ }
+
+ static int get_argument_count() {
+ return 1;
+ }
+
+ static Variant::Type get_argument_type(int p_arg) {
+ return GetTypeInfo<T>::VARIANT_TYPE;
+ }
+
+ static Variant::Type get_base_type() {
+ return Variant::ARRAY;
+ }
+};
+
+template <class T>
+class VariantConstructorFromArray {
+public:
+ static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
+ if (p_args[0]->get_type() != Variant::ARRAY) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::ARRAY;
+ return;
+ }
+
+ VariantTypeChanger<T>::change(&r_ret);
+ const Array &src_arr = *VariantGetInternalPtr<Array>::get_ptr(p_args[0]);
+ T &dst_arr = *VariantGetInternalPtr<T>::get_ptr(&r_ret);
+
+ int size = src_arr.size();
+ dst_arr.resize(size);
+ for (int i = 0; i < size; i++) {
+ dst_arr.write[i] = src_arr[i];
+ }
+ }
+
+ static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
+ VariantTypeChanger<T>::change(r_ret);
+ const Array &src_arr = *VariantGetInternalPtr<Array>::get_ptr(p_args[0]);
+ T &dst_arr = *VariantGetInternalPtr<T>::get_ptr(r_ret);
+
+ int size = src_arr.size();
+ dst_arr.resize(size);
+ for (int i = 0; i < size; i++) {
+ dst_arr.write[i] = src_arr[i];
+ }
+ }
+ static void ptr_construct(void *base, const void **p_args) {
+ Array src_arr = PtrToArg<Array>::convert(p_args[0]);
+ T dst_arr;
+
+ int size = src_arr.size();
+ dst_arr.resize(size);
+ for (int i = 0; i < size; i++) {
+ dst_arr.write[i] = src_arr[i];
+ }
+
+ PtrConstruct<T>::construct(dst_arr, base);
+ }
+
+ static int get_argument_count() {
+ return 1;
+ }
+
+ static Variant::Type get_argument_type(int p_arg) {
+ return Variant::ARRAY;
+ }
+
+ static Variant::Type get_base_type() {
+ return GetTypeInfo<T>::VARIANT_TYPE;
+ }
+};
+
+class VariantConstructorNil {
+public:
+ static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
+ if (p_args[0]->get_type() != Variant::NIL) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return;
+ }
+
+ r_error.error = Callable::CallError::CALL_OK;
+ VariantInternal::clear(&r_ret);
+ }
+
+ static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
+ VariantInternal::clear(r_ret);
+ }
+ static void ptr_construct(void *base, const void **p_args) {
+ PtrConstruct<Variant>::construct(Variant(), base);
+ }
+
+ static int get_argument_count() {
+ return 1;
+ }
+
+ static Variant::Type get_argument_type(int p_arg) {
+ return Variant::NIL;
+ }
+
+ static Variant::Type get_base_type() {
+ return Variant::NIL;
+ }
+};
+
+template <class T>
+class VariantConstructNoArgs {
+public:
+ static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
+ VariantTypeChanger<T>::change_and_reset(&r_ret);
+ r_error.error = Callable::CallError::CALL_OK;
+ }
+
+ static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
+ VariantTypeChanger<T>::change_and_reset(r_ret);
+ }
+ static void ptr_construct(void *base, const void **p_args) {
+ PtrConstruct<T>::construct(T(), base);
+ }
+
+ static int get_argument_count() {
+ return 0;
+ }
+
+ static Variant::Type get_argument_type(int p_arg) {
+ return Variant::NIL;
+ }
+
+ static Variant::Type get_base_type() {
+ return GetTypeInfo<T>::VARIANT_TYPE;
+ }
+};
+
+class VariantConstructNoArgsNil {
+public:
+ static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
+ VariantInternal::clear(&r_ret);
+ r_error.error = Callable::CallError::CALL_OK;
+ }
+
+ static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
+ VariantInternal::clear(r_ret);
+ }
+ static void ptr_construct(void *base, const void **p_args) {
+ ERR_FAIL_MSG("can't ptrcall nil constructor");
+ }
+
+ static int get_argument_count() {
+ return 0;
+ }
+
+ static Variant::Type get_argument_type(int p_arg) {
+ return Variant::NIL;
+ }
+
+ static Variant::Type get_base_type() {
+ return Variant::NIL;
+ }
+};
+
+class VariantConstructNoArgsObject {
+public:
+ static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) {
+ VariantInternal::clear(&r_ret);
+ VariantInternal::object_assign_null(&r_ret);
+ r_error.error = Callable::CallError::CALL_OK;
+ }
+
+ static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
+ VariantInternal::clear(r_ret);
+ VariantInternal::object_assign_null(r_ret);
+ }
+ static void ptr_construct(void *base, const void **p_args) {
+ PtrConstruct<Object *>::construct(nullptr, base);
+ }
+
+ static int get_argument_count() {
+ return 0;
+ }
+
+ static Variant::Type get_argument_type(int p_arg) {
+ return Variant::NIL;
+ }
+
+ static Variant::Type get_base_type() {
+ return Variant::OBJECT;
+ }
+};
+
+#endif // VARIANT_CONSTRUCT_H
diff --git a/core/variant/variant_destruct.cpp b/core/variant/variant_destruct.cpp
new file mode 100644
index 0000000000..366b71df3a
--- /dev/null
+++ b/core/variant/variant_destruct.cpp
@@ -0,0 +1,78 @@
+/*************************************************************************/
+/* variant_destruct.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "variant_destruct.h"
+
+#include "core/templates/local_vector.h"
+
+static Variant::PTRDestructor destruct_pointers[Variant::VARIANT_MAX] = { nullptr };
+
+template <class T>
+static void add_destructor() {
+ destruct_pointers[T::get_base_type()] = T::ptr_destruct;
+}
+
+void Variant::_register_variant_destructors() {
+ add_destructor<VariantDestruct<String>>();
+ add_destructor<VariantDestruct<Transform2D>>();
+ add_destructor<VariantDestruct<::AABB>>();
+ add_destructor<VariantDestruct<Basis>>();
+ add_destructor<VariantDestruct<Transform3D>>();
+ add_destructor<VariantDestruct<StringName>>();
+ add_destructor<VariantDestruct<NodePath>>();
+ add_destructor<VariantDestruct<::RID>>();
+ add_destructor<VariantDestruct<Callable>>();
+ add_destructor<VariantDestruct<Signal>>();
+ add_destructor<VariantDestruct<Dictionary>>();
+ add_destructor<VariantDestruct<Array>>();
+ add_destructor<VariantDestruct<PackedByteArray>>();
+ add_destructor<VariantDestruct<PackedInt32Array>>();
+ add_destructor<VariantDestruct<PackedInt64Array>>();
+ add_destructor<VariantDestruct<PackedFloat32Array>>();
+ add_destructor<VariantDestruct<PackedFloat64Array>>();
+ add_destructor<VariantDestruct<PackedStringArray>>();
+ add_destructor<VariantDestruct<PackedVector2Array>>();
+ add_destructor<VariantDestruct<PackedVector3Array>>();
+ add_destructor<VariantDestruct<PackedColorArray>>();
+}
+
+void Variant::_unregister_variant_destructors() {
+ // Nothing to be done.
+}
+
+Variant::PTRDestructor Variant::get_ptr_destructor(Variant::Type p_type) {
+ ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr);
+ return destruct_pointers[p_type];
+}
+
+bool Variant::has_destructor(Variant::Type p_type) {
+ ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false);
+ return destruct_pointers[p_type] != nullptr;
+}
diff --git a/core/variant/variant_destruct.h b/core/variant/variant_destruct.h
new file mode 100644
index 0000000000..7356e42201
--- /dev/null
+++ b/core/variant/variant_destruct.h
@@ -0,0 +1,76 @@
+/*************************************************************************/
+/* variant_destruct.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef VARIANT_DESTRUCT_H
+#define VARIANT_DESTRUCT_H
+
+#include "core/variant/variant.h"
+
+#include "core/object/class_db.h"
+
+template <class T>
+struct VariantDestruct {};
+
+#define MAKE_PTRDESTRUCT(m_type) \
+ template <> \
+ struct VariantDestruct<m_type> { \
+ _FORCE_INLINE_ static void ptr_destruct(void *p_ptr) { \
+ reinterpret_cast<m_type *>(p_ptr)->~m_type(); \
+ } \
+ _FORCE_INLINE_ static Variant::Type get_base_type() { \
+ return GetTypeInfo<m_type>::VARIANT_TYPE; \
+ } \
+ }
+
+MAKE_PTRDESTRUCT(String);
+MAKE_PTRDESTRUCT(Transform2D);
+MAKE_PTRDESTRUCT(AABB);
+MAKE_PTRDESTRUCT(Basis);
+MAKE_PTRDESTRUCT(Transform3D);
+MAKE_PTRDESTRUCT(StringName);
+MAKE_PTRDESTRUCT(NodePath);
+MAKE_PTRDESTRUCT(RID);
+MAKE_PTRDESTRUCT(Callable);
+MAKE_PTRDESTRUCT(Signal);
+MAKE_PTRDESTRUCT(Dictionary);
+MAKE_PTRDESTRUCT(Array);
+MAKE_PTRDESTRUCT(PackedByteArray);
+MAKE_PTRDESTRUCT(PackedInt32Array);
+MAKE_PTRDESTRUCT(PackedInt64Array);
+MAKE_PTRDESTRUCT(PackedFloat32Array);
+MAKE_PTRDESTRUCT(PackedFloat64Array);
+MAKE_PTRDESTRUCT(PackedStringArray);
+MAKE_PTRDESTRUCT(PackedVector2Array);
+MAKE_PTRDESTRUCT(PackedVector3Array);
+MAKE_PTRDESTRUCT(PackedColorArray);
+
+#undef MAKE_PTRDESTRUCT
+
+#endif // VARIANT_DESTRUCT_H
diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h
index fb791f8c0c..40c8a1bfde 100644
--- a/core/variant/variant_internal.h
+++ b/core/variant/variant_internal.h
@@ -55,7 +55,7 @@ public:
case Variant::BASIS:
init_basis(v);
break;
- case Variant::TRANSFORM:
+ case Variant::TRANSFORM3D:
init_transform(v);
break;
case Variant::STRING_NAME:
@@ -104,7 +104,7 @@ public:
init_color_array(v);
break;
case Variant::OBJECT:
- object_assign_null(v);
+ init_object(v);
break;
default:
break;
@@ -138,14 +138,14 @@ public:
_FORCE_INLINE_ static const Transform2D *get_transform2d(const Variant *v) { return v->_data._transform2d; }
_FORCE_INLINE_ static Plane *get_plane(Variant *v) { return reinterpret_cast<Plane *>(v->_data._mem); }
_FORCE_INLINE_ static const Plane *get_plane(const Variant *v) { return reinterpret_cast<const Plane *>(v->_data._mem); }
- _FORCE_INLINE_ static Quat *get_quat(Variant *v) { return reinterpret_cast<Quat *>(v->_data._mem); }
- _FORCE_INLINE_ static const Quat *get_quat(const Variant *v) { return reinterpret_cast<const Quat *>(v->_data._mem); }
+ _FORCE_INLINE_ static Quaternion *get_quaternion(Variant *v) { return reinterpret_cast<Quaternion *>(v->_data._mem); }
+ _FORCE_INLINE_ static const Quaternion *get_quaternion(const Variant *v) { return reinterpret_cast<const Quaternion *>(v->_data._mem); }
_FORCE_INLINE_ static ::AABB *get_aabb(Variant *v) { return v->_data._aabb; }
_FORCE_INLINE_ static const ::AABB *get_aabb(const Variant *v) { return v->_data._aabb; }
_FORCE_INLINE_ static Basis *get_basis(Variant *v) { return v->_data._basis; }
_FORCE_INLINE_ static const Basis *get_basis(const Variant *v) { return v->_data._basis; }
- _FORCE_INLINE_ static Transform *get_transform(Variant *v) { return v->_data._transform; }
- _FORCE_INLINE_ static const Transform *get_transform(const Variant *v) { return v->_data._transform; }
+ _FORCE_INLINE_ static Transform3D *get_transform(Variant *v) { return v->_data._transform3d; }
+ _FORCE_INLINE_ static const Transform3D *get_transform(const Variant *v) { return v->_data._transform3d; }
// Misc types.
_FORCE_INLINE_ static Color *get_color(Variant *v) { return reinterpret_cast<Color *>(v->_data._mem); }
@@ -217,8 +217,8 @@ public:
v->type = Variant::BASIS;
}
_FORCE_INLINE_ static void init_transform(Variant *v) {
- v->_data._transform = memnew(Transform);
- v->type = Variant::TRANSFORM;
+ v->_data._transform3d = memnew(Transform3D);
+ v->type = Variant::TRANSFORM3D;
}
_FORCE_INLINE_ static void init_string_name(Variant *v) {
memnew_placement(v->_data._mem, StringName);
@@ -280,12 +280,16 @@ public:
v->_data.packed_array = Variant::PackedArrayRef<Color>::create(Vector<Color>());
v->type = Variant::PACKED_COLOR_ARRAY;
}
+ _FORCE_INLINE_ static void init_object(Variant *v) {
+ object_assign_null(v);
+ v->type = Variant::OBJECT;
+ }
_FORCE_INLINE_ static void clear(Variant *v) {
v->clear();
}
- static void object_assign(Variant *v, const Object *o); // Needs Reference, so it's implemented elsewhere.
+ static void object_assign(Variant *v, const Object *o); // Needs RefCounted, so it's implemented elsewhere.
_FORCE_INLINE_ static void object_assign(Variant *v, const Variant *o) {
object_assign(v, o->_get_obj().obj);
@@ -320,12 +324,12 @@ public:
return get_rect2(v);
case Variant::RECT2I:
return get_rect2i(v);
- case Variant::TRANSFORM:
+ case Variant::TRANSFORM3D:
return get_transform(v);
case Variant::TRANSFORM2D:
return get_transform2d(v);
- case Variant::QUAT:
- return get_quat(v);
+ case Variant::QUATERNION:
+ return get_quaternion(v);
case Variant::PLANE:
return get_plane(v);
case Variant::BASIS:
@@ -398,12 +402,12 @@ public:
return get_rect2(v);
case Variant::RECT2I:
return get_rect2i(v);
- case Variant::TRANSFORM:
+ case Variant::TRANSFORM3D:
return get_transform(v);
case Variant::TRANSFORM2D:
return get_transform2d(v);
- case Variant::QUAT:
- return get_quat(v);
+ case Variant::QUATERNION:
+ return get_quaternion(v);
case Variant::PLANE:
return get_plane(v);
case Variant::BASIS:
@@ -590,9 +594,9 @@ struct VariantGetInternalPtr<Transform2D> {
};
template <>
-struct VariantGetInternalPtr<Transform> {
- static Transform *get_ptr(Variant *v) { return VariantInternal::get_transform(v); }
- static const Transform *get_ptr(const Variant *v) { return VariantInternal::get_transform(v); }
+struct VariantGetInternalPtr<Transform3D> {
+ static Transform3D *get_ptr(Variant *v) { return VariantInternal::get_transform(v); }
+ static const Transform3D *get_ptr(const Variant *v) { return VariantInternal::get_transform(v); }
};
template <>
@@ -602,9 +606,9 @@ struct VariantGetInternalPtr<Plane> {
};
template <>
-struct VariantGetInternalPtr<Quat> {
- static Quat *get_ptr(Variant *v) { return VariantInternal::get_quat(v); }
- static const Quat *get_ptr(const Variant *v) { return VariantInternal::get_quat(v); }
+struct VariantGetInternalPtr<Quaternion> {
+ static Quaternion *get_ptr(Variant *v) { return VariantInternal::get_quaternion(v); }
+ static const Quaternion *get_ptr(const Variant *v) { return VariantInternal::get_quaternion(v); }
};
template <>
@@ -819,9 +823,9 @@ struct VariantInternalAccessor<Transform2D> {
};
template <>
-struct VariantInternalAccessor<Transform> {
- static _FORCE_INLINE_ const Transform &get(const Variant *v) { return *VariantInternal::get_transform(v); }
- static _FORCE_INLINE_ void set(Variant *v, const Transform &p_value) { *VariantInternal::get_transform(v) = p_value; }
+struct VariantInternalAccessor<Transform3D> {
+ static _FORCE_INLINE_ const Transform3D &get(const Variant *v) { return *VariantInternal::get_transform(v); }
+ static _FORCE_INLINE_ void set(Variant *v, const Transform3D &p_value) { *VariantInternal::get_transform(v) = p_value; }
};
template <>
@@ -831,9 +835,9 @@ struct VariantInternalAccessor<Plane> {
};
template <>
-struct VariantInternalAccessor<Quat> {
- static _FORCE_INLINE_ const Quat &get(const Variant *v) { return *VariantInternal::get_quat(v); }
- static _FORCE_INLINE_ void set(Variant *v, const Quat &p_value) { *VariantInternal::get_quat(v) = p_value; }
+struct VariantInternalAccessor<Quaternion> {
+ static _FORCE_INLINE_ const Quaternion &get(const Variant *v) { return *VariantInternal::get_quaternion(v); }
+ static _FORCE_INLINE_ void set(Variant *v, const Quaternion &p_value) { *VariantInternal::get_quaternion(v) = p_value; }
};
template <>
@@ -1067,8 +1071,8 @@ struct VariantInitializer<Plane> {
};
template <>
-struct VariantInitializer<Quat> {
- static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<Quat>(v); }
+struct VariantInitializer<Quaternion> {
+ static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<Quaternion>(v); }
};
template <>
@@ -1082,7 +1086,7 @@ struct VariantInitializer<Basis> {
};
template <>
-struct VariantInitializer<Transform> {
+struct VariantInitializer<Transform3D> {
static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_transform(v); }
};
@@ -1171,6 +1175,11 @@ struct VariantInitializer<PackedColorArray> {
static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_color_array(v); }
};
+template <>
+struct VariantInitializer<Object *> {
+ static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_object(v); }
+};
+
template <class T>
struct VariantZeroAssigner {
};
@@ -1241,8 +1250,8 @@ struct VariantZeroAssigner<Plane> {
};
template <>
-struct VariantZeroAssigner<Quat> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_quat(v) = Quat(); }
+struct VariantZeroAssigner<Quaternion> {
+ static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_quaternion(v) = Quaternion(); }
};
template <>
@@ -1256,8 +1265,8 @@ struct VariantZeroAssigner<Basis> {
};
template <>
-struct VariantZeroAssigner<Transform> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_transform(v) = Transform(); }
+struct VariantZeroAssigner<Transform3D> {
+ static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_transform(v) = Transform3D(); }
};
template <>
@@ -1385,4 +1394,37 @@ struct VariantTypeAdjust<Object *> {
}
};
+// GDNative extension helpers.
+
+template <class T>
+struct VariantTypeConstructor {
+ _FORCE_INLINE_ static void variant_from_type(void *p_variant, void *p_value) {
+ Variant *variant = reinterpret_cast<Variant *>(p_variant);
+ VariantInitializer<T>::init(variant);
+ VariantInternalAccessor<T>::set(variant, *((T *)p_value));
+ }
+
+ _FORCE_INLINE_ static void type_from_variant(void *p_value, void *p_variant) {
+ *((T *)p_value) = VariantInternalAccessor<T>::get(reinterpret_cast<Variant *>(p_variant));
+ }
+};
+
+template <>
+struct VariantTypeConstructor<Object *> {
+ _FORCE_INLINE_ static void variant_from_type(void *p_variant, void *p_value) {
+ Variant *variant = reinterpret_cast<Variant *>(p_variant);
+ VariantInitializer<Object *>::init(variant);
+ Object *value = *(reinterpret_cast<Object **>(p_value));
+ if (value) {
+ VariantInternalAccessor<Object *>::set(variant, value);
+ VariantInternalAccessor<ObjectID>::set(variant, value->get_instance_id());
+ }
+ }
+
+ _FORCE_INLINE_ static void type_from_variant(void *p_value, void *p_variant) {
+ Object **value = reinterpret_cast<Object **>(p_value);
+ *value = VariantInternalAccessor<Object *>::get(reinterpret_cast<Variant *>(p_variant));
+ }
+};
+
#endif // VARIANT_INTERNAL_H
diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp
index 8cfa793c0e..a245aff35b 100644
--- a/core/variant/variant_op.cpp
+++ b/core/variant/variant_op.cpp
@@ -28,1342 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "variant.h"
-
-#include "core/core_string_names.h"
-#include "core/debugger/engine_debugger.h"
-#include "core/object/class_db.h"
-
-template <class R, class A, class B>
-class OperatorEvaluatorAdd {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a + b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) + *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<A>::convert(left) + PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A, class B>
-class OperatorEvaluatorSub {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a - b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) - *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<A>::convert(left) - PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A, class B>
-class OperatorEvaluatorMul {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a * b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) * *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<A>::convert(left) * PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A, class B>
-class OperatorEvaluatorXForm {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a.xform(b);
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = VariantGetInternalPtr<A>::get_ptr(left)->xform(*VariantGetInternalPtr<B>::get_ptr(right));
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<A>::convert(left).xform(PtrToArg<B>::convert(right)), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A, class B>
-class OperatorEvaluatorXFormInv {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = b.xform_inv(a);
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = VariantGetInternalPtr<B>::get_ptr(right)->xform_inv(*VariantGetInternalPtr<A>::get_ptr(left));
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<B>::convert(right).xform_inv(PtrToArg<A>::convert(left)), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A, class B>
-class OperatorEvaluatorDiv {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a / b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) / *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<A>::convert(left) / PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A, class B>
-class OperatorEvaluatorDivNZ {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- if (b == 0) {
- r_valid = false;
- *r_ret = "Division by zero error";
- return;
- }
- *r_ret = a / b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) / *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<A>::convert(left) / PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A, class B>
-class OperatorEvaluatorMod {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a % b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) % *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<A>::convert(left) % PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A, class B>
-class OperatorEvaluatorModNZ {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- if (b == 0) {
- r_valid = false;
- *r_ret = "Module by zero error";
- return;
- }
- *r_ret = a % b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) % *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<A>::convert(left) % PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A>
-class OperatorEvaluatorNeg {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- *r_ret = -a;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = -*VariantGetInternalPtr<A>::get_ptr(left);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(-PtrToArg<A>::convert(left), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A>
-class OperatorEvaluatorPos {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- *r_ret = a;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<A>::convert(left), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A, class B>
-class OperatorEvaluatorShiftLeft {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
-
-#if defined(DEBUG_ENABLED)
- if (b < 0 || a < 0) {
- *r_ret = "Invalid operands for bit shifting. Only positive operands are supported.";
- r_valid = false;
- return;
- }
-#endif
- *r_ret = a << b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) << *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<A>::convert(left) << PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A, class B>
-class OperatorEvaluatorShiftRight {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
-
-#if defined(DEBUG_ENABLED)
- if (b < 0 || a < 0) {
- *r_ret = "Invalid operands for bit shifting. Only positive operands are supported.";
- r_valid = false;
- return;
- }
-#endif
- *r_ret = a >> b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) >> *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<A>::convert(left) >> PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A, class B>
-class OperatorEvaluatorBitOr {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a | b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) | *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<A>::convert(left) | PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A, class B>
-class OperatorEvaluatorBitAnd {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a & b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) & *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<A>::convert(left) & PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A, class B>
-class OperatorEvaluatorBitXor {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a ^ b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) ^ *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(PtrToArg<A>::convert(left) ^ PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class R, class A>
-class OperatorEvaluatorBitNeg {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- *r_ret = ~a;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<R>::change(r_ret);
- *VariantGetInternalPtr<R>::get_ptr(r_ret) = ~*VariantGetInternalPtr<A>::get_ptr(left);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<R>::encode(~PtrToArg<A>::convert(left), r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
-};
-
-template <class A, class B>
-class OperatorEvaluatorEqual {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a == b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) == *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<A>::convert(left) == PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorEqualObject {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Object *a = p_left.get_validated_object();
- const Object *b = p_right.get_validated_object();
- *r_ret = a == b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Object *a = left->get_validated_object();
- const Object *b = right->get_validated_object();
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == b;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) == PtrToArg<Object *>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorEqualObjectNil {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Object *a = p_left.get_validated_object();
- *r_ret = a == nullptr;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Object *a = left->get_validated_object();
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == nullptr;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) == nullptr, r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorEqualNilObject {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Object *b = p_right.get_validated_object();
- *r_ret = nullptr == b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Object *b = right->get_validated_object();
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr == b;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(nullptr == PtrToArg<Object *>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-template <class A, class B>
-class OperatorEvaluatorNotEqual {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a != b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) != *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<A>::convert(left) != PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorNotEqualObject {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- Object *a = p_left.get_validated_object();
- Object *b = p_right.get_validated_object();
- *r_ret = a != b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- Object *a = left->get_validated_object();
- Object *b = right->get_validated_object();
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != b;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) != PtrToArg<Object *>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorNotEqualObjectNil {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- Object *a = p_left.get_validated_object();
- *r_ret = a != nullptr;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- Object *a = left->get_validated_object();
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != nullptr;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) != nullptr, r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorNotEqualNilObject {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- Object *b = p_right.get_validated_object();
- *r_ret = nullptr != b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- Object *b = right->get_validated_object();
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr != b;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(nullptr != PtrToArg<Object *>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-template <class A, class B>
-class OperatorEvaluatorLess {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a < b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) < *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<A>::convert(left) < PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-template <class A, class B>
-class OperatorEvaluatorLessEqual {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a <= b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) <= *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<A>::convert(left) <= PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-template <class A, class B>
-class OperatorEvaluatorGreater {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a > b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) > *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<A>::convert(left) > PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-template <class A, class B>
-class OperatorEvaluatorGreaterEqual {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a >= b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) >= *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<A>::convert(left) >= PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-template <class A, class B>
-class OperatorEvaluatorAnd {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a && b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) && *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<A>::convert(left) && PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-template <class A, class B>
-class OperatorEvaluatorOr {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = a || b;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) || *VariantGetInternalPtr<B>::get_ptr(right);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<A>::convert(left) || PtrToArg<B>::convert(right), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-#define XOR_OP(m_a, m_b) (((m_a) || (m_b)) && !((m_a) && (m_b)))
-template <class A, class B>
-class OperatorEvaluatorXor {
-public:
- _FORCE_INLINE_ static bool xor_op(const A &a, const B &b) {
- return ((a) || (b)) && !((a) && (b));
- }
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
- *r_ret = xor_op(a, b);
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = xor_op(*VariantGetInternalPtr<A>::get_ptr(left), *VariantGetInternalPtr<B>::get_ptr(right));
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(xor_op(PtrToArg<A>::convert(left), PtrToArg<B>::convert(right)), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-template <class A>
-class OperatorEvaluatorNot {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- *r_ret = !a;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = !*VariantGetInternalPtr<A>::get_ptr(left);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(!PtrToArg<A>::convert(left));
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-//// CUSTOM ////
-
-class OperatorEvaluatorAddArray {
-public:
- _FORCE_INLINE_ static void _add_arrays(Array &sum, const Array &array_a, const Array &array_b) {
- int asize = array_a.size();
- int bsize = array_b.size();
- sum.resize(asize + bsize);
- for (int i = 0; i < asize; i++) {
- sum[i] = array_a[i];
- }
- for (int i = 0; i < bsize; i++) {
- sum[i + asize] = array_b[i];
- }
- }
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Array &array_a = *VariantGetInternalPtr<Array>::get_ptr(&p_left);
- const Array &array_b = *VariantGetInternalPtr<Array>::get_ptr(&p_right);
- Array sum;
- _add_arrays(sum, array_a, array_b);
- *r_ret = sum;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<Array>::change(r_ret);
- _add_arrays(*VariantGetInternalPtr<Array>::get_ptr(r_ret), *VariantGetInternalPtr<Array>::get_ptr(left), *VariantGetInternalPtr<Array>::get_ptr(right));
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- Array ret;
- _add_arrays(ret, PtrToArg<Array>::convert(left), PtrToArg<Array>::convert(right));
- PtrToArg<Array>::encode(ret, r_ret);
- }
- static Variant::Type get_return_type() { return Variant::ARRAY; }
-};
-
-template <class T>
-class OperatorEvaluatorAppendArray {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Vector<T> &array_a = *VariantGetInternalPtr<Vector<T>>::get_ptr(&p_left);
- const Vector<T> &array_b = *VariantGetInternalPtr<Vector<T>>::get_ptr(&p_right);
- Vector<T> sum = array_a;
- sum.append_array(array_b);
- *r_ret = sum;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<Vector<T>>::change(r_ret);
- *VariantGetInternalPtr<Vector<T>>::get_ptr(r_ret) = *VariantGetInternalPtr<Vector<T>>::get_ptr(left);
- VariantGetInternalPtr<Vector<T>>::get_ptr(r_ret)->append_array(*VariantGetInternalPtr<Vector<T>>::get_ptr(right));
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- Vector<T> sum = PtrToArg<Vector<T>>::convert(left);
- sum.append_array(PtrToArg<Vector<T>>::convert(right));
- PtrToArg<Vector<T>>::encode(sum, r_ret);
- }
- static Variant::Type get_return_type() { return GetTypeInfo<Vector<T>>::VARIANT_TYPE; }
-};
-
-class OperatorEvaluatorStringModNil {
-public:
- _FORCE_INLINE_ static String do_mod(const String &s, bool *r_valid) {
- Array values;
- values.push_back(Variant());
-
- String a = s.sprintf(values, r_valid);
- if (r_valid) {
- *r_valid = !*r_valid;
- }
- return a;
- }
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left);
- *r_ret = do_mod(a, &r_valid);
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<String>::change(r_ret);
- *VariantGetInternalPtr<String>::get_ptr(r_ret) = do_mod(*VariantGetInternalPtr<String>::get_ptr(left), nullptr);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<String>::encode(do_mod(PtrToArg<String>::convert(left), nullptr), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::STRING; }
-};
-
-class OperatorEvaluatorStringModArray {
-public:
- _FORCE_INLINE_ static String do_mod(const String &s, const Array &p_values, bool *r_valid) {
- String a = s.sprintf(p_values, r_valid);
- if (r_valid) {
- *r_valid = !*r_valid;
- }
- return a;
- }
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left);
- *r_ret = do_mod(a, *VariantGetInternalPtr<Array>::get_ptr(&p_right), &r_valid);
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<String>::change(r_ret);
- *VariantGetInternalPtr<String>::get_ptr(r_ret) = do_mod(*VariantGetInternalPtr<String>::get_ptr(left), *VariantGetInternalPtr<Array>::get_ptr(right), nullptr);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<String>::encode(do_mod(PtrToArg<String>::convert(left), PtrToArg<Array>::convert(right), nullptr), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::STRING; }
-};
-
-class OperatorEvaluatorStringModObject {
-public:
- _FORCE_INLINE_ static String do_mod(const String &s, const Object *p_object, bool *r_valid) {
- Array values;
- values.push_back(p_object);
- String a = s.sprintf(values, r_valid);
- if (r_valid) {
- *r_valid = !*r_valid;
- }
-
- return a;
- }
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left);
- *r_ret = do_mod(a, p_right.get_validated_object(), &r_valid);
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<String>::change(r_ret);
- *VariantGetInternalPtr<String>::get_ptr(r_ret) = do_mod(*VariantGetInternalPtr<String>::get_ptr(left), right->get_validated_object(), nullptr);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<String>::encode(do_mod(PtrToArg<String>::convert(left), PtrToArg<Object *>::convert(right), nullptr), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::STRING; }
-};
-
-template <class T>
-class OperatorEvaluatorStringModT {
-public:
- _FORCE_INLINE_ static String do_mod(const String &s, const T &p_value, bool *r_valid) {
- Array values;
- values.push_back(p_value);
- String a = s.sprintf(values, r_valid);
- if (r_valid) {
- *r_valid = !*r_valid;
- }
- return a;
- }
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left);
- *r_ret = do_mod(a, *VariantGetInternalPtr<T>::get_ptr(&p_right), &r_valid);
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<String>::change(r_ret);
- *VariantGetInternalPtr<String>::get_ptr(r_ret) = do_mod(*VariantGetInternalPtr<String>::get_ptr(left), *VariantGetInternalPtr<T>::get_ptr(right), nullptr);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<String>::encode(do_mod(PtrToArg<String>::convert(left), PtrToArg<T>::convert(right), nullptr), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::STRING; }
-};
-
-template <Variant::Operator op, Variant::Type type_left, Variant::Type type_right>
-class OperatorEvaluatorAlwaysTrue {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- *r_ret = true;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = true;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(true, r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-template <Variant::Operator op, Variant::Type type_left, Variant::Type type_right>
-class OperatorEvaluatorAlwaysFalse {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- *r_ret = false;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = false;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(false, r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-///// OR ///////
-
-_FORCE_INLINE_ static bool _operate_or(bool p_left, bool p_right) {
- return p_left || p_right;
-}
-
-_FORCE_INLINE_ static bool _operate_and(bool p_left, bool p_right) {
- return p_left && p_right;
-}
-
-_FORCE_INLINE_ static bool _operate_xor(bool p_left, bool p_right) {
- return (p_left || p_right) && !(p_left && p_right);
-}
-
-_FORCE_INLINE_ static bool _operate_get_nil(const Variant *p_ptr) {
- return p_ptr->get_validated_object() != nullptr;
-}
-
-_FORCE_INLINE_ static bool _operate_get_bool(const Variant *p_ptr) {
- return *VariantGetInternalPtr<bool>::get_ptr(p_ptr);
-}
-
-_FORCE_INLINE_ static bool _operate_get_int(const Variant *p_ptr) {
- return *VariantGetInternalPtr<int64_t>::get_ptr(p_ptr) != 0;
-}
-
-_FORCE_INLINE_ static bool _operate_get_float(const Variant *p_ptr) {
- return *VariantGetInternalPtr<double>::get_ptr(p_ptr) != 0.0;
-}
-
-_FORCE_INLINE_ static bool _operate_get_object(const Variant *p_ptr) {
- return p_ptr->get_validated_object() != nullptr;
-}
-
-_FORCE_INLINE_ static bool _operate_get_ptr_nil(const void *p_ptr) {
- return false;
-}
-
-_FORCE_INLINE_ static bool _operate_get_ptr_bool(const void *p_ptr) {
- return PtrToArg<bool>::convert(p_ptr);
-}
-
-_FORCE_INLINE_ static bool _operate_get_ptr_int(const void *p_ptr) {
- return PtrToArg<int64_t>::convert(p_ptr) != 0;
-}
-
-_FORCE_INLINE_ static bool _operate_get_ptr_float(const void *p_ptr) {
- return PtrToArg<double>::convert(p_ptr) != 0.0;
-}
-
-_FORCE_INLINE_ static bool _operate_get_ptr_object(const void *p_ptr) {
- return PtrToArg<Object *>::convert(p_ptr) != nullptr;
-}
-
-#define OP_EVALUATOR(m_class_name, m_left, m_right, m_op) \
- class m_class_name { \
- public: \
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { \
- *r_ret = m_op(_operate_get_##m_left(&p_left), _operate_get_##m_right(&p_right)); \
- r_valid = true; \
- } \
- \
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { \
- VariantTypeChanger<bool>::change(r_ret); \
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = m_op(_operate_get_##m_left(left), _operate_get_##m_right(right)); \
- } \
- \
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) { \
- PtrToArg<bool>::encode(m_op(_operate_get_ptr_##m_left(left), _operate_get_ptr_##m_right(right)), r_ret); \
- } \
- \
- static Variant::Type get_return_type() { \
- return Variant::BOOL; \
- } \
- };
-
-// OR
-
-// nil
-OP_EVALUATOR(OperatorEvaluatorNilXBoolOr, nil, bool, _operate_or)
-OP_EVALUATOR(OperatorEvaluatorBoolXNilOr, bool, nil, _operate_or)
-
-OP_EVALUATOR(OperatorEvaluatorNilXIntOr, nil, int, _operate_or)
-OP_EVALUATOR(OperatorEvaluatorIntXNilOr, int, nil, _operate_or)
-
-OP_EVALUATOR(OperatorEvaluatorNilXFloatOr, nil, float, _operate_or)
-OP_EVALUATOR(OperatorEvaluatorFloatXNilOr, float, nil, _operate_or)
-
-OP_EVALUATOR(OperatorEvaluatorObjectXNilOr, object, nil, _operate_or)
-OP_EVALUATOR(OperatorEvaluatorNilXObjectOr, nil, object, _operate_or)
-
-// bool
-OP_EVALUATOR(OperatorEvaluatorBoolXBoolOr, bool, bool, _operate_or)
-
-OP_EVALUATOR(OperatorEvaluatorBoolXIntOr, bool, int, _operate_or)
-OP_EVALUATOR(OperatorEvaluatorIntXBoolOr, int, bool, _operate_or)
-
-OP_EVALUATOR(OperatorEvaluatorBoolXFloatOr, bool, float, _operate_or)
-OP_EVALUATOR(OperatorEvaluatorFloatXBoolOr, float, bool, _operate_or)
-
-OP_EVALUATOR(OperatorEvaluatorBoolXObjectOr, bool, object, _operate_or)
-OP_EVALUATOR(OperatorEvaluatorObjectXBoolOr, object, bool, _operate_or)
-
-// int
-OP_EVALUATOR(OperatorEvaluatorIntXIntOr, int, int, _operate_or)
-
-OP_EVALUATOR(OperatorEvaluatorIntXFloatOr, int, float, _operate_or)
-OP_EVALUATOR(OperatorEvaluatorFloatXIntOr, float, int, _operate_or)
-
-OP_EVALUATOR(OperatorEvaluatorIntXObjectOr, int, object, _operate_or)
-OP_EVALUATOR(OperatorEvaluatorObjectXIntOr, object, int, _operate_or)
-
-// float
-OP_EVALUATOR(OperatorEvaluatorFloatXFloatOr, float, float, _operate_or)
-
-OP_EVALUATOR(OperatorEvaluatorFloatXObjectOr, float, object, _operate_or)
-OP_EVALUATOR(OperatorEvaluatorObjectXFloatOr, object, float, _operate_or)
-
-// object
-OP_EVALUATOR(OperatorEvaluatorObjectXObjectOr, object, object, _operate_or)
-
-// AND
-
-// nil
-OP_EVALUATOR(OperatorEvaluatorNilXBoolAnd, nil, bool, _operate_and)
-OP_EVALUATOR(OperatorEvaluatorBoolXNilAnd, bool, nil, _operate_and)
-
-OP_EVALUATOR(OperatorEvaluatorNilXIntAnd, nil, int, _operate_and)
-OP_EVALUATOR(OperatorEvaluatorIntXNilAnd, int, nil, _operate_and)
-
-OP_EVALUATOR(OperatorEvaluatorNilXFloatAnd, nil, float, _operate_and)
-OP_EVALUATOR(OperatorEvaluatorFloatXNilAnd, float, nil, _operate_and)
-
-OP_EVALUATOR(OperatorEvaluatorObjectXNilAnd, object, nil, _operate_and)
-OP_EVALUATOR(OperatorEvaluatorNilXObjectAnd, nil, object, _operate_and)
-
-// bool
-OP_EVALUATOR(OperatorEvaluatorBoolXBoolAnd, bool, bool, _operate_and)
-
-OP_EVALUATOR(OperatorEvaluatorBoolXIntAnd, bool, int, _operate_and)
-OP_EVALUATOR(OperatorEvaluatorIntXBoolAnd, int, bool, _operate_and)
-
-OP_EVALUATOR(OperatorEvaluatorBoolXFloatAnd, bool, float, _operate_and)
-OP_EVALUATOR(OperatorEvaluatorFloatXBoolAnd, float, bool, _operate_and)
-
-OP_EVALUATOR(OperatorEvaluatorBoolXObjectAnd, bool, object, _operate_and)
-OP_EVALUATOR(OperatorEvaluatorObjectXBoolAnd, object, bool, _operate_and)
-
-// int
-OP_EVALUATOR(OperatorEvaluatorIntXIntAnd, int, int, _operate_and)
-
-OP_EVALUATOR(OperatorEvaluatorIntXFloatAnd, int, float, _operate_and)
-OP_EVALUATOR(OperatorEvaluatorFloatXIntAnd, float, int, _operate_and)
-
-OP_EVALUATOR(OperatorEvaluatorIntXObjectAnd, int, object, _operate_and)
-OP_EVALUATOR(OperatorEvaluatorObjectXIntAnd, object, int, _operate_and)
-
-// float
-OP_EVALUATOR(OperatorEvaluatorFloatXFloatAnd, float, float, _operate_and)
-
-OP_EVALUATOR(OperatorEvaluatorFloatXObjectAnd, float, object, _operate_and)
-OP_EVALUATOR(OperatorEvaluatorObjectXFloatAnd, object, float, _operate_and)
-
-// object
-OP_EVALUATOR(OperatorEvaluatorObjectXObjectAnd, object, object, _operate_and)
-
-// XOR
-
-// nil
-OP_EVALUATOR(OperatorEvaluatorNilXBoolXor, nil, bool, _operate_xor)
-OP_EVALUATOR(OperatorEvaluatorBoolXNilXor, bool, nil, _operate_xor)
-
-OP_EVALUATOR(OperatorEvaluatorNilXIntXor, nil, int, _operate_xor)
-OP_EVALUATOR(OperatorEvaluatorIntXNilXor, int, nil, _operate_xor)
-
-OP_EVALUATOR(OperatorEvaluatorNilXFloatXor, nil, float, _operate_xor)
-OP_EVALUATOR(OperatorEvaluatorFloatXNilXor, float, nil, _operate_xor)
-
-OP_EVALUATOR(OperatorEvaluatorObjectXNilXor, object, nil, _operate_xor)
-OP_EVALUATOR(OperatorEvaluatorNilXObjectXor, nil, object, _operate_xor)
-
-// bool
-OP_EVALUATOR(OperatorEvaluatorBoolXBoolXor, bool, bool, _operate_xor)
-
-OP_EVALUATOR(OperatorEvaluatorBoolXIntXor, bool, int, _operate_xor)
-OP_EVALUATOR(OperatorEvaluatorIntXBoolXor, int, bool, _operate_xor)
-
-OP_EVALUATOR(OperatorEvaluatorBoolXFloatXor, bool, float, _operate_xor)
-OP_EVALUATOR(OperatorEvaluatorFloatXBoolXor, float, bool, _operate_xor)
-
-OP_EVALUATOR(OperatorEvaluatorBoolXObjectXor, bool, object, _operate_xor)
-OP_EVALUATOR(OperatorEvaluatorObjectXBoolXor, object, bool, _operate_xor)
-
-// int
-OP_EVALUATOR(OperatorEvaluatorIntXIntXor, int, int, _operate_xor)
-
-OP_EVALUATOR(OperatorEvaluatorIntXFloatXor, int, float, _operate_xor)
-OP_EVALUATOR(OperatorEvaluatorFloatXIntXor, float, int, _operate_xor)
-
-OP_EVALUATOR(OperatorEvaluatorIntXObjectXor, int, object, _operate_xor)
-OP_EVALUATOR(OperatorEvaluatorObjectXIntXor, object, int, _operate_xor)
-
-// float
-OP_EVALUATOR(OperatorEvaluatorFloatXFloatXor, float, float, _operate_xor)
-
-OP_EVALUATOR(OperatorEvaluatorFloatXObjectXor, float, object, _operate_xor)
-OP_EVALUATOR(OperatorEvaluatorObjectXFloatXor, object, float, _operate_xor)
-
-// object
-OP_EVALUATOR(OperatorEvaluatorObjectXObjectXor, object, object, _operate_xor)
-
-class OperatorEvaluatorNotBool {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- *r_ret = !*VariantGetInternalPtr<bool>::get_ptr(&p_left);
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = !*VariantGetInternalPtr<bool>::get_ptr(left);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(!PtrToArg<bool>::convert(left), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorNotInt {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- *r_ret = !*VariantGetInternalPtr<int64_t>::get_ptr(&p_left);
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = !*VariantGetInternalPtr<int64_t>::get_ptr(left);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(!PtrToArg<int64_t>::convert(left), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorNotFloat {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- *r_ret = !*VariantGetInternalPtr<double>::get_ptr(&p_left);
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = !*VariantGetInternalPtr<double>::get_ptr(left);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(!PtrToArg<double>::convert(left), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorNotObject {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- *r_ret = p_left.get_validated_object() == nullptr;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = left->get_validated_object() == nullptr;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) == nullptr, r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-////
-
-class OperatorEvaluatorInStringFind {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const String &str_a = *VariantGetInternalPtr<String>::get_ptr(&p_left);
- const String &str_b = *VariantGetInternalPtr<String>::get_ptr(&p_right);
-
- *r_ret = str_b.find(str_a) != -1;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const String &str_a = *VariantGetInternalPtr<String>::get_ptr(left);
- const String &str_b = *VariantGetInternalPtr<String>::get_ptr(right);
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = str_b.find(str_a) != -1;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<String>::convert(right).find(PtrToArg<String>::convert(left)) != -1, r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-template <class A, class B>
-class OperatorEvaluatorInArrayFind {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
-
- *r_ret = b.find(a) != -1;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const A &a = *VariantGetInternalPtr<A>::get_ptr(left);
- const B &b = *VariantGetInternalPtr<B>::get_ptr(right);
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.find(a) != -1;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<B>::convert(right).find(PtrToArg<A>::convert(left)) != -1, r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorInArrayFindNil {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Array &b = *VariantGetInternalPtr<Array>::get_ptr(&p_right);
- *r_ret = b.find(Variant()) != -1;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Array &b = *VariantGetInternalPtr<Array>::get_ptr(right);
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.find(Variant()) != -1;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<Array>::convert(right).find(Variant()) != -1, r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorInArrayFindObject {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Array &b = *VariantGetInternalPtr<Array>::get_ptr(&p_right);
- *r_ret = b.find(p_left) != -1;
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Array &b = *VariantGetInternalPtr<Array>::get_ptr(right);
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.find(*left) != -1;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<Array>::convert(right).find(PtrToArg<Object *>::convert(left)) != -1, r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-template <class A>
-class OperatorEvaluatorInDictionaryHas {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(&p_right);
- const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
-
- *r_ret = b.has(a);
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(right);
- const A &a = *VariantGetInternalPtr<A>::get_ptr(left);
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.has(a);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<Dictionary>::convert(right).has(PtrToArg<A>::convert(left)), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorInDictionaryHasNil {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(&p_right);
-
- *r_ret = b.has(Variant());
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(right);
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.has(Variant());
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<Dictionary>::convert(right).has(Variant()), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorInDictionaryHasObject {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(&p_right);
-
- *r_ret = b.has(p_left);
- r_valid = true;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(right);
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.has(*left);
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- PtrToArg<bool>::encode(PtrToArg<Dictionary>::convert(right).has(PtrToArg<Object *>::convert(left)), r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorObjectHasPropertyString {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- Object *b = p_right.get_validated_object();
- if (!b) {
- *r_ret = "Invalid base object for 'in'";
- r_valid = false;
- return;
- }
-
- const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left);
-
- b->get(a, &r_valid);
- *r_ret = r_valid;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- Object *l = right->get_validated_object();
- ERR_FAIL_COND(l == nullptr);
- const String &a = *VariantGetInternalPtr<String>::get_ptr(left);
-
- bool valid;
- l->get(a, &valid);
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = valid;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- bool valid;
- PtrToArg<Object *>::convert(right)->get(PtrToArg<String>::convert(left), &valid);
- PtrToArg<bool>::encode(valid, r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
-
-class OperatorEvaluatorObjectHasPropertyStringName {
-public:
- static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- Object *b = p_right.get_validated_object();
- if (!b) {
- *r_ret = "Invalid base object for 'in'";
- r_valid = false;
- return;
- }
-
- const StringName &a = *VariantGetInternalPtr<StringName>::get_ptr(&p_left);
-
- b->get(a, &r_valid);
- *r_ret = r_valid;
- }
- static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- Object *l = right->get_validated_object();
- ERR_FAIL_COND(l == nullptr);
- const StringName &a = *VariantGetInternalPtr<StringName>::get_ptr(left);
-
- bool valid;
- l->get(a, &valid);
- VariantTypeChanger<bool>::change(r_ret);
- *VariantGetInternalPtr<bool>::get_ptr(r_ret) = valid;
- }
- static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
- bool valid;
- PtrToArg<Object *>::convert(right)->get(PtrToArg<StringName>::convert(left), &valid);
- PtrToArg<bool>::encode(valid, r_ret);
- }
- static Variant::Type get_return_type() { return Variant::BOOL; }
-};
+#include "variant_op.h"
typedef void (*VariantEvaluatorFunction)(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid);
@@ -1395,7 +60,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorAdd<Vector2i, Vector2i, Vector2i>>(Variant::OP_ADD, Variant::VECTOR2I, Variant::VECTOR2I);
register_op<OperatorEvaluatorAdd<Vector3, Vector3, Vector3>>(Variant::OP_ADD, Variant::VECTOR3, Variant::VECTOR3);
register_op<OperatorEvaluatorAdd<Vector3i, Vector3i, Vector3i>>(Variant::OP_ADD, Variant::VECTOR3I, Variant::VECTOR3I);
- register_op<OperatorEvaluatorAdd<Quat, Quat, Quat>>(Variant::OP_ADD, Variant::QUAT, Variant::QUAT);
+ register_op<OperatorEvaluatorAdd<Quaternion, Quaternion, Quaternion>>(Variant::OP_ADD, Variant::QUATERNION, Variant::QUATERNION);
register_op<OperatorEvaluatorAdd<Color, Color, Color>>(Variant::OP_ADD, Variant::COLOR, Variant::COLOR);
register_op<OperatorEvaluatorAddArray>(Variant::OP_ADD, Variant::ARRAY, Variant::ARRAY);
register_op<OperatorEvaluatorAppendArray<uint8_t>>(Variant::OP_ADD, Variant::PACKED_BYTE_ARRAY, Variant::PACKED_BYTE_ARRAY);
@@ -1416,7 +81,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorSub<Vector2i, Vector2i, Vector2i>>(Variant::OP_SUBTRACT, Variant::VECTOR2I, Variant::VECTOR2I);
register_op<OperatorEvaluatorSub<Vector3, Vector3, Vector3>>(Variant::OP_SUBTRACT, Variant::VECTOR3, Variant::VECTOR3);
register_op<OperatorEvaluatorSub<Vector3i, Vector3i, Vector3i>>(Variant::OP_SUBTRACT, Variant::VECTOR3I, Variant::VECTOR3I);
- register_op<OperatorEvaluatorSub<Quat, Quat, Quat>>(Variant::OP_SUBTRACT, Variant::QUAT, Variant::QUAT);
+ register_op<OperatorEvaluatorSub<Quaternion, Quaternion, Quaternion>>(Variant::OP_SUBTRACT, Variant::QUATERNION, Variant::QUATERNION);
register_op<OperatorEvaluatorSub<Color, Color, Color>>(Variant::OP_SUBTRACT, Variant::COLOR, Variant::COLOR);
register_op<OperatorEvaluatorMul<int64_t, int64_t, int64_t>>(Variant::OP_MULTIPLY, Variant::INT, Variant::INT);
@@ -1449,15 +114,17 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorMul<Vector3i, Vector3i, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::INT);
register_op<OperatorEvaluatorMul<Vector3i, Vector3i, double>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::FLOAT);
- register_op<OperatorEvaluatorMul<Quat, Quat, Quat>>(Variant::OP_MULTIPLY, Variant::QUAT, Variant::QUAT);
- register_op<OperatorEvaluatorMul<Quat, Quat, int64_t>>(Variant::OP_MULTIPLY, Variant::QUAT, Variant::INT);
- register_op<OperatorEvaluatorMul<Quat, Quat, double>>(Variant::OP_MULTIPLY, Variant::QUAT, Variant::FLOAT);
+ register_op<OperatorEvaluatorMul<Quaternion, Quaternion, Quaternion>>(Variant::OP_MULTIPLY, Variant::QUATERNION, Variant::QUATERNION);
+ register_op<OperatorEvaluatorMul<Quaternion, Quaternion, int64_t>>(Variant::OP_MULTIPLY, Variant::QUATERNION, Variant::INT);
+ register_op<OperatorEvaluatorMul<Quaternion, Quaternion, double>>(Variant::OP_MULTIPLY, Variant::QUATERNION, Variant::FLOAT);
register_op<OperatorEvaluatorMul<Color, Color, Color>>(Variant::OP_MULTIPLY, Variant::COLOR, Variant::COLOR);
register_op<OperatorEvaluatorMul<Color, Color, int64_t>>(Variant::OP_MULTIPLY, Variant::COLOR, Variant::INT);
register_op<OperatorEvaluatorMul<Color, Color, double>>(Variant::OP_MULTIPLY, Variant::COLOR, Variant::FLOAT);
register_op<OperatorEvaluatorMul<Transform2D, Transform2D, Transform2D>>(Variant::OP_MULTIPLY, Variant::TRANSFORM2D, Variant::TRANSFORM2D);
+ register_op<OperatorEvaluatorMul<Transform2D, Transform2D, int64_t>>(Variant::OP_MULTIPLY, Variant::TRANSFORM2D, Variant::INT);
+ register_op<OperatorEvaluatorMul<Transform2D, Transform2D, double>>(Variant::OP_MULTIPLY, Variant::TRANSFORM2D, Variant::FLOAT);
register_op<OperatorEvaluatorXForm<Vector2, Transform2D, Vector2>>(Variant::OP_MULTIPLY, Variant::TRANSFORM2D, Variant::VECTOR2);
register_op<OperatorEvaluatorXFormInv<Vector2, Vector2, Transform2D>>(Variant::OP_MULTIPLY, Variant::VECTOR2, Variant::TRANSFORM2D);
register_op<OperatorEvaluatorXForm<Rect2, Transform2D, Rect2>>(Variant::OP_MULTIPLY, Variant::TRANSFORM2D, Variant::RECT2);
@@ -1465,25 +132,29 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorXForm<Vector<Vector2>, Transform2D, Vector<Vector2>>>(Variant::OP_MULTIPLY, Variant::TRANSFORM2D, Variant::PACKED_VECTOR2_ARRAY);
register_op<OperatorEvaluatorXFormInv<Vector<Vector2>, Vector<Vector2>, Transform2D>>(Variant::OP_MULTIPLY, Variant::PACKED_VECTOR2_ARRAY, Variant::TRANSFORM2D);
- register_op<OperatorEvaluatorMul<Transform, Transform, Transform>>(Variant::OP_MULTIPLY, Variant::TRANSFORM, Variant::TRANSFORM);
- register_op<OperatorEvaluatorXForm<Vector3, Transform, Vector3>>(Variant::OP_MULTIPLY, Variant::TRANSFORM, Variant::VECTOR3);
- register_op<OperatorEvaluatorXFormInv<Vector3, Vector3, Transform>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::TRANSFORM);
- register_op<OperatorEvaluatorXForm<::AABB, Transform, ::AABB>>(Variant::OP_MULTIPLY, Variant::TRANSFORM, Variant::AABB);
- register_op<OperatorEvaluatorXFormInv<::AABB, ::AABB, Transform>>(Variant::OP_MULTIPLY, Variant::AABB, Variant::TRANSFORM);
- register_op<OperatorEvaluatorXForm<Vector<Vector3>, Transform, Vector<Vector3>>>(Variant::OP_MULTIPLY, Variant::TRANSFORM, Variant::PACKED_VECTOR3_ARRAY);
- register_op<OperatorEvaluatorXFormInv<Vector<Vector3>, Vector<Vector3>, Transform>>(Variant::OP_MULTIPLY, Variant::PACKED_VECTOR3_ARRAY, Variant::TRANSFORM);
+ register_op<OperatorEvaluatorMul<Transform3D, Transform3D, Transform3D>>(Variant::OP_MULTIPLY, Variant::TRANSFORM3D, Variant::TRANSFORM3D);
+ register_op<OperatorEvaluatorMul<Transform3D, Transform3D, int64_t>>(Variant::OP_MULTIPLY, Variant::TRANSFORM3D, Variant::INT);
+ register_op<OperatorEvaluatorMul<Transform3D, Transform3D, double>>(Variant::OP_MULTIPLY, Variant::TRANSFORM3D, Variant::FLOAT);
+ register_op<OperatorEvaluatorXForm<Vector3, Transform3D, Vector3>>(Variant::OP_MULTIPLY, Variant::TRANSFORM3D, Variant::VECTOR3);
+ register_op<OperatorEvaluatorXFormInv<Vector3, Vector3, Transform3D>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::TRANSFORM3D);
+ register_op<OperatorEvaluatorXForm<::AABB, Transform3D, ::AABB>>(Variant::OP_MULTIPLY, Variant::TRANSFORM3D, Variant::AABB);
+ register_op<OperatorEvaluatorXFormInv<::AABB, ::AABB, Transform3D>>(Variant::OP_MULTIPLY, Variant::AABB, Variant::TRANSFORM3D);
+ register_op<OperatorEvaluatorXForm<Vector<Vector3>, Transform3D, Vector<Vector3>>>(Variant::OP_MULTIPLY, Variant::TRANSFORM3D, Variant::PACKED_VECTOR3_ARRAY);
+ register_op<OperatorEvaluatorXFormInv<Vector<Vector3>, Vector<Vector3>, Transform3D>>(Variant::OP_MULTIPLY, Variant::PACKED_VECTOR3_ARRAY, Variant::TRANSFORM3D);
register_op<OperatorEvaluatorMul<Basis, Basis, Basis>>(Variant::OP_MULTIPLY, Variant::BASIS, Variant::BASIS);
+ register_op<OperatorEvaluatorMul<Basis, Basis, int64_t>>(Variant::OP_MULTIPLY, Variant::BASIS, Variant::INT);
+ register_op<OperatorEvaluatorMul<Basis, Basis, double>>(Variant::OP_MULTIPLY, Variant::BASIS, Variant::FLOAT);
register_op<OperatorEvaluatorXForm<Vector3, Basis, Vector3>>(Variant::OP_MULTIPLY, Variant::BASIS, Variant::VECTOR3);
register_op<OperatorEvaluatorXFormInv<Vector3, Vector3, Basis>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::BASIS);
- register_op<OperatorEvaluatorMul<Quat, Quat, Quat>>(Variant::OP_MULTIPLY, Variant::QUAT, Variant::QUAT);
- register_op<OperatorEvaluatorMul<Quat, Quat, int64_t>>(Variant::OP_MULTIPLY, Variant::QUAT, Variant::INT);
- register_op<OperatorEvaluatorMul<Quat, int64_t, Quat>>(Variant::OP_MULTIPLY, Variant::INT, Variant::QUAT);
- register_op<OperatorEvaluatorMul<Quat, Quat, double>>(Variant::OP_MULTIPLY, Variant::QUAT, Variant::FLOAT);
- register_op<OperatorEvaluatorMul<Quat, double, Quat>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::QUAT);
- register_op<OperatorEvaluatorXForm<Vector3, Quat, Vector3>>(Variant::OP_MULTIPLY, Variant::QUAT, Variant::VECTOR3);
- register_op<OperatorEvaluatorXFormInv<Vector3, Vector3, Quat>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::QUAT);
+ register_op<OperatorEvaluatorMul<Quaternion, Quaternion, Quaternion>>(Variant::OP_MULTIPLY, Variant::QUATERNION, Variant::QUATERNION);
+ register_op<OperatorEvaluatorMul<Quaternion, Quaternion, int64_t>>(Variant::OP_MULTIPLY, Variant::QUATERNION, Variant::INT);
+ register_op<OperatorEvaluatorMul<Quaternion, int64_t, Quaternion>>(Variant::OP_MULTIPLY, Variant::INT, Variant::QUATERNION);
+ register_op<OperatorEvaluatorMul<Quaternion, Quaternion, double>>(Variant::OP_MULTIPLY, Variant::QUATERNION, Variant::FLOAT);
+ register_op<OperatorEvaluatorMul<Quaternion, double, Quaternion>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::QUATERNION);
+ register_op<OperatorEvaluatorXForm<Vector3, Quaternion, Vector3>>(Variant::OP_MULTIPLY, Variant::QUATERNION, Variant::VECTOR3);
+ register_op<OperatorEvaluatorXFormInv<Vector3, Vector3, Quaternion>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::QUATERNION);
register_op<OperatorEvaluatorMul<Color, Color, Color>>(Variant::OP_MULTIPLY, Variant::COLOR, Variant::COLOR);
register_op<OperatorEvaluatorMul<Color, Color, int64_t>>(Variant::OP_MULTIPLY, Variant::COLOR, Variant::INT);
@@ -1500,7 +171,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorDiv<Vector2, Vector2, double>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::FLOAT);
register_op<OperatorEvaluatorDiv<Vector2, Vector2, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::INT);
- register_op<OperatorEvaluatorDiv<Vector2i, Vector2i, Vector2i>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::VECTOR2I);
+ register_op<OperatorEvaluatorDivNZ<Vector2i, Vector2i, Vector2i>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::VECTOR2I);
register_op<OperatorEvaluatorDivNZ<Vector2i, Vector2i, double>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::FLOAT);
register_op<OperatorEvaluatorDivNZ<Vector2i, Vector2i, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::INT);
@@ -1512,22 +183,22 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorDiv<Vector3, Vector3, double>>(Variant::OP_DIVIDE, Variant::VECTOR3, Variant::FLOAT);
register_op<OperatorEvaluatorDiv<Vector3, Vector3, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR3, Variant::INT);
- register_op<OperatorEvaluatorDiv<Vector3i, Vector3i, Vector3i>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::VECTOR3I);
+ register_op<OperatorEvaluatorDivNZ<Vector3i, Vector3i, Vector3i>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::VECTOR3I);
register_op<OperatorEvaluatorDivNZ<Vector3i, Vector3i, double>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::FLOAT);
register_op<OperatorEvaluatorDivNZ<Vector3i, Vector3i, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::INT);
- register_op<OperatorEvaluatorDiv<Quat, Quat, double>>(Variant::OP_DIVIDE, Variant::QUAT, Variant::FLOAT);
- register_op<OperatorEvaluatorDiv<Quat, Quat, int64_t>>(Variant::OP_DIVIDE, Variant::QUAT, Variant::INT);
+ register_op<OperatorEvaluatorDiv<Quaternion, Quaternion, double>>(Variant::OP_DIVIDE, Variant::QUATERNION, Variant::FLOAT);
+ register_op<OperatorEvaluatorDiv<Quaternion, Quaternion, int64_t>>(Variant::OP_DIVIDE, Variant::QUATERNION, Variant::INT);
register_op<OperatorEvaluatorDiv<Color, Color, Color>>(Variant::OP_DIVIDE, Variant::COLOR, Variant::COLOR);
register_op<OperatorEvaluatorDiv<Color, Color, double>>(Variant::OP_DIVIDE, Variant::COLOR, Variant::FLOAT);
register_op<OperatorEvaluatorDiv<Color, Color, int64_t>>(Variant::OP_DIVIDE, Variant::COLOR, Variant::INT);
register_op<OperatorEvaluatorModNZ<int64_t, int64_t, int64_t>>(Variant::OP_MODULE, Variant::INT, Variant::INT);
- register_op<OperatorEvaluatorMod<Vector2i, Vector2i, Vector2i>>(Variant::OP_MODULE, Variant::VECTOR2I, Variant::VECTOR2I);
+ register_op<OperatorEvaluatorModNZ<Vector2i, Vector2i, Vector2i>>(Variant::OP_MODULE, Variant::VECTOR2I, Variant::VECTOR2I);
register_op<OperatorEvaluatorModNZ<Vector2i, Vector2i, int64_t>>(Variant::OP_MODULE, Variant::VECTOR2I, Variant::INT);
- register_op<OperatorEvaluatorMod<Vector3i, Vector3i, Vector3i>>(Variant::OP_MODULE, Variant::VECTOR3I, Variant::VECTOR3I);
+ register_op<OperatorEvaluatorModNZ<Vector3i, Vector3i, Vector3i>>(Variant::OP_MODULE, Variant::VECTOR3I, Variant::VECTOR3I);
register_op<OperatorEvaluatorModNZ<Vector3i, Vector3i, int64_t>>(Variant::OP_MODULE, Variant::VECTOR3I, Variant::INT);
register_op<OperatorEvaluatorStringModNil>(Variant::OP_MODULE, Variant::STRING, Variant::NIL);
@@ -1544,10 +215,10 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorStringModT<Vector3i>>(Variant::OP_MODULE, Variant::STRING, Variant::VECTOR3I);
register_op<OperatorEvaluatorStringModT<Transform2D>>(Variant::OP_MODULE, Variant::STRING, Variant::TRANSFORM2D);
register_op<OperatorEvaluatorStringModT<Plane>>(Variant::OP_MODULE, Variant::STRING, Variant::PLANE);
- register_op<OperatorEvaluatorStringModT<Quat>>(Variant::OP_MODULE, Variant::STRING, Variant::QUAT);
+ register_op<OperatorEvaluatorStringModT<Quaternion>>(Variant::OP_MODULE, Variant::STRING, Variant::QUATERNION);
register_op<OperatorEvaluatorStringModT<::AABB>>(Variant::OP_MODULE, Variant::STRING, Variant::AABB);
register_op<OperatorEvaluatorStringModT<Basis>>(Variant::OP_MODULE, Variant::STRING, Variant::BASIS);
- register_op<OperatorEvaluatorStringModT<Transform>>(Variant::OP_MODULE, Variant::STRING, Variant::TRANSFORM);
+ register_op<OperatorEvaluatorStringModT<Transform3D>>(Variant::OP_MODULE, Variant::STRING, Variant::TRANSFORM3D);
register_op<OperatorEvaluatorStringModT<Color>>(Variant::OP_MODULE, Variant::STRING, Variant::COLOR);
register_op<OperatorEvaluatorStringModT<StringName>>(Variant::OP_MODULE, Variant::STRING, Variant::STRING_NAME);
@@ -1574,7 +245,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorNeg<Vector2i, Vector2i>>(Variant::OP_NEGATE, Variant::VECTOR2I, Variant::NIL);
register_op<OperatorEvaluatorNeg<Vector3, Vector3>>(Variant::OP_NEGATE, Variant::VECTOR3, Variant::NIL);
register_op<OperatorEvaluatorNeg<Vector3i, Vector3i>>(Variant::OP_NEGATE, Variant::VECTOR3I, Variant::NIL);
- register_op<OperatorEvaluatorNeg<Quat, Quat>>(Variant::OP_NEGATE, Variant::QUAT, Variant::NIL);
+ register_op<OperatorEvaluatorNeg<Quaternion, Quaternion>>(Variant::OP_NEGATE, Variant::QUATERNION, Variant::NIL);
register_op<OperatorEvaluatorNeg<Plane, Plane>>(Variant::OP_NEGATE, Variant::PLANE, Variant::NIL);
register_op<OperatorEvaluatorNeg<Color, Color>>(Variant::OP_NEGATE, Variant::COLOR, Variant::NIL);
@@ -1584,7 +255,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorPos<Vector2i, Vector2i>>(Variant::OP_POSITIVE, Variant::VECTOR2I, Variant::NIL);
register_op<OperatorEvaluatorPos<Vector3, Vector3>>(Variant::OP_POSITIVE, Variant::VECTOR3, Variant::NIL);
register_op<OperatorEvaluatorPos<Vector3i, Vector3i>>(Variant::OP_POSITIVE, Variant::VECTOR3I, Variant::NIL);
- register_op<OperatorEvaluatorPos<Quat, Quat>>(Variant::OP_POSITIVE, Variant::QUAT, Variant::NIL);
+ register_op<OperatorEvaluatorPos<Quaternion, Quaternion>>(Variant::OP_POSITIVE, Variant::QUATERNION, Variant::NIL);
register_op<OperatorEvaluatorPos<Plane, Plane>>(Variant::OP_POSITIVE, Variant::PLANE, Variant::NIL);
register_op<OperatorEvaluatorPos<Color, Color>>(Variant::OP_POSITIVE, Variant::COLOR, Variant::NIL);
@@ -1612,10 +283,10 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorEqual<Vector3i, Vector3i>>(Variant::OP_EQUAL, Variant::VECTOR3I, Variant::VECTOR3I);
register_op<OperatorEvaluatorEqual<Transform2D, Transform2D>>(Variant::OP_EQUAL, Variant::TRANSFORM2D, Variant::TRANSFORM2D);
register_op<OperatorEvaluatorEqual<Plane, Plane>>(Variant::OP_EQUAL, Variant::PLANE, Variant::PLANE);
- register_op<OperatorEvaluatorEqual<Quat, Quat>>(Variant::OP_EQUAL, Variant::QUAT, Variant::QUAT);
+ register_op<OperatorEvaluatorEqual<Quaternion, Quaternion>>(Variant::OP_EQUAL, Variant::QUATERNION, Variant::QUATERNION);
register_op<OperatorEvaluatorEqual<::AABB, ::AABB>>(Variant::OP_EQUAL, Variant::AABB, Variant::AABB);
register_op<OperatorEvaluatorEqual<Basis, Basis>>(Variant::OP_EQUAL, Variant::BASIS, Variant::BASIS);
- register_op<OperatorEvaluatorEqual<Transform, Transform>>(Variant::OP_EQUAL, Variant::TRANSFORM, Variant::TRANSFORM);
+ register_op<OperatorEvaluatorEqual<Transform3D, Transform3D>>(Variant::OP_EQUAL, Variant::TRANSFORM3D, Variant::TRANSFORM3D);
register_op<OperatorEvaluatorEqual<Color, Color>>(Variant::OP_EQUAL, Variant::COLOR, Variant::COLOR);
register_op<OperatorEvaluatorEqual<StringName, String>>(Variant::OP_EQUAL, Variant::STRING_NAME, Variant::STRING);
@@ -1658,10 +329,10 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorNotEqual<Vector3i, Vector3i>>(Variant::OP_NOT_EQUAL, Variant::VECTOR3I, Variant::VECTOR3I);
register_op<OperatorEvaluatorNotEqual<Transform2D, Transform2D>>(Variant::OP_NOT_EQUAL, Variant::TRANSFORM2D, Variant::TRANSFORM2D);
register_op<OperatorEvaluatorNotEqual<Plane, Plane>>(Variant::OP_NOT_EQUAL, Variant::PLANE, Variant::PLANE);
- register_op<OperatorEvaluatorNotEqual<Quat, Quat>>(Variant::OP_NOT_EQUAL, Variant::QUAT, Variant::QUAT);
+ register_op<OperatorEvaluatorNotEqual<Quaternion, Quaternion>>(Variant::OP_NOT_EQUAL, Variant::QUATERNION, Variant::QUATERNION);
register_op<OperatorEvaluatorNotEqual<::AABB, ::AABB>>(Variant::OP_NOT_EQUAL, Variant::AABB, Variant::AABB);
register_op<OperatorEvaluatorNotEqual<Basis, Basis>>(Variant::OP_NOT_EQUAL, Variant::BASIS, Variant::BASIS);
- register_op<OperatorEvaluatorNotEqual<Transform, Transform>>(Variant::OP_NOT_EQUAL, Variant::TRANSFORM, Variant::TRANSFORM);
+ register_op<OperatorEvaluatorNotEqual<Transform3D, Transform3D>>(Variant::OP_NOT_EQUAL, Variant::TRANSFORM3D, Variant::TRANSFORM3D);
register_op<OperatorEvaluatorNotEqual<Color, Color>>(Variant::OP_NOT_EQUAL, Variant::COLOR, Variant::COLOR);
register_op<OperatorEvaluatorNotEqual<StringName, String>>(Variant::OP_NOT_EQUAL, Variant::STRING_NAME, Variant::STRING);
@@ -1849,10 +520,10 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorInDictionaryHas<Vector3i>>(Variant::OP_IN, Variant::VECTOR3I, Variant::DICTIONARY);
register_op<OperatorEvaluatorInDictionaryHas<Transform2D>>(Variant::OP_IN, Variant::TRANSFORM2D, Variant::DICTIONARY);
register_op<OperatorEvaluatorInDictionaryHas<Plane>>(Variant::OP_IN, Variant::PLANE, Variant::DICTIONARY);
- register_op<OperatorEvaluatorInDictionaryHas<Quat>>(Variant::OP_IN, Variant::QUAT, Variant::DICTIONARY);
+ register_op<OperatorEvaluatorInDictionaryHas<Quaternion>>(Variant::OP_IN, Variant::QUATERNION, Variant::DICTIONARY);
register_op<OperatorEvaluatorInDictionaryHas<::AABB>>(Variant::OP_IN, Variant::AABB, Variant::DICTIONARY);
register_op<OperatorEvaluatorInDictionaryHas<Basis>>(Variant::OP_IN, Variant::BASIS, Variant::DICTIONARY);
- register_op<OperatorEvaluatorInDictionaryHas<Transform>>(Variant::OP_IN, Variant::TRANSFORM, Variant::DICTIONARY);
+ register_op<OperatorEvaluatorInDictionaryHas<Transform3D>>(Variant::OP_IN, Variant::TRANSFORM3D, Variant::DICTIONARY);
register_op<OperatorEvaluatorInDictionaryHas<Color>>(Variant::OP_IN, Variant::COLOR, Variant::DICTIONARY);
register_op<OperatorEvaluatorInDictionaryHas<StringName>>(Variant::OP_IN, Variant::STRING_NAME, Variant::DICTIONARY);
@@ -1886,10 +557,10 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorInArrayFind<Vector3i, Array>>(Variant::OP_IN, Variant::VECTOR3I, Variant::ARRAY);
register_op<OperatorEvaluatorInArrayFind<Transform2D, Array>>(Variant::OP_IN, Variant::TRANSFORM2D, Variant::ARRAY);
register_op<OperatorEvaluatorInArrayFind<Plane, Array>>(Variant::OP_IN, Variant::PLANE, Variant::ARRAY);
- register_op<OperatorEvaluatorInArrayFind<Quat, Array>>(Variant::OP_IN, Variant::QUAT, Variant::ARRAY);
+ register_op<OperatorEvaluatorInArrayFind<Quaternion, Array>>(Variant::OP_IN, Variant::QUATERNION, Variant::ARRAY);
register_op<OperatorEvaluatorInArrayFind<::AABB, Array>>(Variant::OP_IN, Variant::AABB, Variant::ARRAY);
register_op<OperatorEvaluatorInArrayFind<Basis, Array>>(Variant::OP_IN, Variant::BASIS, Variant::ARRAY);
- register_op<OperatorEvaluatorInArrayFind<Transform, Array>>(Variant::OP_IN, Variant::TRANSFORM, Variant::ARRAY);
+ register_op<OperatorEvaluatorInArrayFind<Transform3D, Array>>(Variant::OP_IN, Variant::TRANSFORM3D, Variant::ARRAY);
register_op<OperatorEvaluatorInArrayFind<Color, Array>>(Variant::OP_IN, Variant::COLOR, Variant::ARRAY);
register_op<OperatorEvaluatorInArrayFind<StringName, Array>>(Variant::OP_IN, Variant::STRING_NAME, Variant::ARRAY);
@@ -1990,8 +661,8 @@ static const char *_op_names[Variant::OP_MAX] = {
"-",
"*",
"/",
- "-",
- "+",
+ "unary-",
+ "unary+",
"%",
"<<",
">>",
diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h
new file mode 100644
index 0000000000..3c9f849a4f
--- /dev/null
+++ b/core/variant/variant_op.h
@@ -0,0 +1,1416 @@
+/*************************************************************************/
+/* variant_op.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef VARIANT_OP_H
+#define VARIANT_OP_H
+
+#include "variant.h"
+
+#include "core/core_string_names.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/object/class_db.h"
+
+template <class R, class A, class B>
+class OperatorEvaluatorAdd {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a + b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) + *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<A>::convert(left) + PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A, class B>
+class OperatorEvaluatorSub {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a - b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) - *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<A>::convert(left) - PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A, class B>
+class OperatorEvaluatorMul {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a * b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) * *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<A>::convert(left) * PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A, class B>
+class OperatorEvaluatorXForm {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a.xform(b);
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = VariantGetInternalPtr<A>::get_ptr(left)->xform(*VariantGetInternalPtr<B>::get_ptr(right));
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<A>::convert(left).xform(PtrToArg<B>::convert(right)), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A, class B>
+class OperatorEvaluatorXFormInv {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = b.xform_inv(a);
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = VariantGetInternalPtr<B>::get_ptr(right)->xform_inv(*VariantGetInternalPtr<A>::get_ptr(left));
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<B>::convert(right).xform_inv(PtrToArg<A>::convert(left)), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A, class B>
+class OperatorEvaluatorDiv {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a / b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) / *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<A>::convert(left) / PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A, class B>
+class OperatorEvaluatorDivNZ {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ if (b == 0) {
+ r_valid = false;
+ *r_ret = "Division by zero error";
+ return;
+ }
+ *r_ret = a / b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) / *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<A>::convert(left) / PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <>
+class OperatorEvaluatorDivNZ<Vector2i, Vector2i, Vector2i> {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Vector2i &a = *VariantGetInternalPtr<Vector2i>::get_ptr(&p_left);
+ const Vector2i &b = *VariantGetInternalPtr<Vector2i>::get_ptr(&p_right);
+ if (unlikely(b.x == 0 || b.y == 0)) {
+ r_valid = false;
+ *r_ret = "Division by zero error";
+ return;
+ }
+ *r_ret = a / b;
+ r_valid = true;
+ }
+ static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ VariantTypeChanger<Vector2i>::change(r_ret);
+ *VariantGetInternalPtr<Vector2i>::get_ptr(r_ret) = *VariantGetInternalPtr<Vector2i>::get_ptr(left) / *VariantGetInternalPtr<Vector2i>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<Vector2i>::encode(PtrToArg<Vector2i>::convert(left) / PtrToArg<Vector2i>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<Vector2i>::VARIANT_TYPE; }
+};
+
+template <>
+class OperatorEvaluatorDivNZ<Vector3i, Vector3i, Vector3i> {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Vector3i &a = *VariantGetInternalPtr<Vector3i>::get_ptr(&p_left);
+ 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 = "Division by zero error";
+ return;
+ }
+ *r_ret = a / b;
+ r_valid = true;
+ }
+ static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ VariantTypeChanger<Vector3i>::change(r_ret);
+ *VariantGetInternalPtr<Vector3i>::get_ptr(r_ret) = *VariantGetInternalPtr<Vector3i>::get_ptr(left) / *VariantGetInternalPtr<Vector3i>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<Vector3i>::encode(PtrToArg<Vector3i>::convert(left) / PtrToArg<Vector3i>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<Vector3i>::VARIANT_TYPE; }
+};
+
+template <class R, class A, class B>
+class OperatorEvaluatorMod {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a % b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) % *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<A>::convert(left) % PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A, class B>
+class OperatorEvaluatorModNZ {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ if (b == 0) {
+ r_valid = false;
+ *r_ret = "Module by zero error";
+ return;
+ }
+ *r_ret = a % b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) % *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<A>::convert(left) % PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <>
+class OperatorEvaluatorModNZ<Vector2i, Vector2i, Vector2i> {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Vector2i &a = *VariantGetInternalPtr<Vector2i>::get_ptr(&p_left);
+ 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";
+ return;
+ }
+ *r_ret = a % b;
+ r_valid = true;
+ }
+ static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ VariantTypeChanger<Vector2i>::change(r_ret);
+ *VariantGetInternalPtr<Vector2i>::get_ptr(r_ret) = *VariantGetInternalPtr<Vector2i>::get_ptr(left) % *VariantGetInternalPtr<Vector2i>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<Vector2i>::encode(PtrToArg<Vector2i>::convert(left) / PtrToArg<Vector2i>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<Vector2i>::VARIANT_TYPE; }
+};
+
+template <>
+class OperatorEvaluatorModNZ<Vector3i, Vector3i, Vector3i> {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Vector3i &a = *VariantGetInternalPtr<Vector3i>::get_ptr(&p_left);
+ 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";
+ return;
+ }
+ *r_ret = a % b;
+ r_valid = true;
+ }
+ static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ VariantTypeChanger<Vector3i>::change(r_ret);
+ *VariantGetInternalPtr<Vector3i>::get_ptr(r_ret) = *VariantGetInternalPtr<Vector3i>::get_ptr(left) % *VariantGetInternalPtr<Vector3i>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<Vector3i>::encode(PtrToArg<Vector3i>::convert(left) % PtrToArg<Vector3i>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<Vector3i>::VARIANT_TYPE; }
+};
+
+template <class R, class A>
+class OperatorEvaluatorNeg {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ *r_ret = -a;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = -*VariantGetInternalPtr<A>::get_ptr(left);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(-PtrToArg<A>::convert(left), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A>
+class OperatorEvaluatorPos {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ *r_ret = a;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<A>::convert(left), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A, class B>
+class OperatorEvaluatorShiftLeft {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+
+#if defined(DEBUG_ENABLED)
+ if (b < 0 || a < 0) {
+ *r_ret = "Invalid operands for bit shifting. Only positive operands are supported.";
+ r_valid = false;
+ return;
+ }
+#endif
+ *r_ret = a << b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) << *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<A>::convert(left) << PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A, class B>
+class OperatorEvaluatorShiftRight {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+
+#if defined(DEBUG_ENABLED)
+ if (b < 0 || a < 0) {
+ *r_ret = "Invalid operands for bit shifting. Only positive operands are supported.";
+ r_valid = false;
+ return;
+ }
+#endif
+ *r_ret = a >> b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) >> *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<A>::convert(left) >> PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A, class B>
+class OperatorEvaluatorBitOr {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a | b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) | *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<A>::convert(left) | PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A, class B>
+class OperatorEvaluatorBitAnd {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a & b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) & *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<A>::convert(left) & PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A, class B>
+class OperatorEvaluatorBitXor {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a ^ b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) ^ *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(PtrToArg<A>::convert(left) ^ PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class R, class A>
+class OperatorEvaluatorBitNeg {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ *r_ret = ~a;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<R>::get_ptr(r_ret) = ~*VariantGetInternalPtr<A>::get_ptr(left);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<R>::encode(~PtrToArg<A>::convert(left), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
+};
+
+template <class A, class B>
+class OperatorEvaluatorEqual {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a == b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) == *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<A>::convert(left) == PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorEqualObject {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Object *a = p_left.get_validated_object();
+ const Object *b = p_right.get_validated_object();
+ *r_ret = a == b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ const Object *a = left->get_validated_object();
+ const Object *b = right->get_validated_object();
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == b;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) == PtrToArg<Object *>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorEqualObjectNil {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Object *a = p_left.get_validated_object();
+ *r_ret = a == nullptr;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ const Object *a = left->get_validated_object();
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == nullptr;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) == nullptr, r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorEqualNilObject {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Object *b = p_right.get_validated_object();
+ *r_ret = nullptr == b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ const Object *b = right->get_validated_object();
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr == b;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(nullptr == PtrToArg<Object *>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+template <class A, class B>
+class OperatorEvaluatorNotEqual {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a != b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) != *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<A>::convert(left) != PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorNotEqualObject {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ Object *a = p_left.get_validated_object();
+ Object *b = p_right.get_validated_object();
+ *r_ret = a != b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ Object *a = left->get_validated_object();
+ Object *b = right->get_validated_object();
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != b;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) != PtrToArg<Object *>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorNotEqualObjectNil {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ Object *a = p_left.get_validated_object();
+ *r_ret = a != nullptr;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ Object *a = left->get_validated_object();
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != nullptr;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) != nullptr, r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorNotEqualNilObject {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ Object *b = p_right.get_validated_object();
+ *r_ret = nullptr != b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ Object *b = right->get_validated_object();
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr != b;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(nullptr != PtrToArg<Object *>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+template <class A, class B>
+class OperatorEvaluatorLess {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a < b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) < *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<A>::convert(left) < PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+template <class A, class B>
+class OperatorEvaluatorLessEqual {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a <= b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) <= *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<A>::convert(left) <= PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+template <class A, class B>
+class OperatorEvaluatorGreater {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a > b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) > *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<A>::convert(left) > PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+template <class A, class B>
+class OperatorEvaluatorGreaterEqual {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a >= b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) >= *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<A>::convert(left) >= PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+template <class A, class B>
+class OperatorEvaluatorAnd {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a && b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) && *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<A>::convert(left) && PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+template <class A, class B>
+class OperatorEvaluatorOr {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = a || b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) || *VariantGetInternalPtr<B>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<A>::convert(left) || PtrToArg<B>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+#define XOR_OP(m_a, m_b) (((m_a) || (m_b)) && !((m_a) && (m_b)))
+template <class A, class B>
+class OperatorEvaluatorXor {
+public:
+ _FORCE_INLINE_ static bool xor_op(const A &a, const B &b) {
+ return ((a) || (b)) && !((a) && (b));
+ }
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+ *r_ret = xor_op(a, b);
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = xor_op(*VariantGetInternalPtr<A>::get_ptr(left), *VariantGetInternalPtr<B>::get_ptr(right));
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(xor_op(PtrToArg<A>::convert(left), PtrToArg<B>::convert(right)), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+template <class A>
+class OperatorEvaluatorNot {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ *r_ret = !a;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = !*VariantGetInternalPtr<A>::get_ptr(left);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(!PtrToArg<A>::convert(left));
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+//// CUSTOM ////
+
+class OperatorEvaluatorAddArray {
+public:
+ _FORCE_INLINE_ static void _add_arrays(Array &sum, const Array &array_a, const Array &array_b) {
+ int asize = array_a.size();
+ int bsize = array_b.size();
+ sum.resize(asize + bsize);
+ for (int i = 0; i < asize; i++) {
+ sum[i] = array_a[i];
+ }
+ for (int i = 0; i < bsize; i++) {
+ sum[i + asize] = array_b[i];
+ }
+ }
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Array &array_a = *VariantGetInternalPtr<Array>::get_ptr(&p_left);
+ const Array &array_b = *VariantGetInternalPtr<Array>::get_ptr(&p_right);
+ Array sum;
+ _add_arrays(sum, array_a, array_b);
+ *r_ret = sum;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ _add_arrays(*VariantGetInternalPtr<Array>::get_ptr(r_ret), *VariantGetInternalPtr<Array>::get_ptr(left), *VariantGetInternalPtr<Array>::get_ptr(right));
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ Array ret;
+ _add_arrays(ret, PtrToArg<Array>::convert(left), PtrToArg<Array>::convert(right));
+ PtrToArg<Array>::encode(ret, r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::ARRAY; }
+};
+
+template <class T>
+class OperatorEvaluatorAppendArray {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Vector<T> &array_a = *VariantGetInternalPtr<Vector<T>>::get_ptr(&p_left);
+ const Vector<T> &array_b = *VariantGetInternalPtr<Vector<T>>::get_ptr(&p_right);
+ Vector<T> sum = array_a;
+ sum.append_array(array_b);
+ *r_ret = sum;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<Vector<T>>::get_ptr(r_ret) = *VariantGetInternalPtr<Vector<T>>::get_ptr(left);
+ VariantGetInternalPtr<Vector<T>>::get_ptr(r_ret)->append_array(*VariantGetInternalPtr<Vector<T>>::get_ptr(right));
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ Vector<T> sum = PtrToArg<Vector<T>>::convert(left);
+ sum.append_array(PtrToArg<Vector<T>>::convert(right));
+ PtrToArg<Vector<T>>::encode(sum, r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<Vector<T>>::VARIANT_TYPE; }
+};
+
+class OperatorEvaluatorStringModNil {
+public:
+ _FORCE_INLINE_ static String do_mod(const String &s, bool *r_valid) {
+ Array values;
+ values.push_back(Variant());
+
+ String a = s.sprintf(values, r_valid);
+ if (r_valid) {
+ *r_valid = !*r_valid;
+ }
+ return a;
+ }
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left);
+ *r_ret = do_mod(a, &r_valid);
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<String>::get_ptr(r_ret) = do_mod(*VariantGetInternalPtr<String>::get_ptr(left), nullptr);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<String>::encode(do_mod(PtrToArg<String>::convert(left), nullptr), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::STRING; }
+};
+
+class OperatorEvaluatorStringModArray {
+public:
+ _FORCE_INLINE_ static String do_mod(const String &s, const Array &p_values, bool *r_valid) {
+ String a = s.sprintf(p_values, r_valid);
+ if (r_valid) {
+ *r_valid = !*r_valid;
+ }
+ return a;
+ }
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left);
+ *r_ret = do_mod(a, *VariantGetInternalPtr<Array>::get_ptr(&p_right), &r_valid);
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<String>::get_ptr(r_ret) = do_mod(*VariantGetInternalPtr<String>::get_ptr(left), *VariantGetInternalPtr<Array>::get_ptr(right), nullptr);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<String>::encode(do_mod(PtrToArg<String>::convert(left), PtrToArg<Array>::convert(right), nullptr), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::STRING; }
+};
+
+class OperatorEvaluatorStringModObject {
+public:
+ _FORCE_INLINE_ static String do_mod(const String &s, const Object *p_object, bool *r_valid) {
+ Array values;
+ values.push_back(p_object);
+ String a = s.sprintf(values, r_valid);
+ if (r_valid) {
+ *r_valid = !*r_valid;
+ }
+
+ return a;
+ }
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left);
+ *r_ret = do_mod(a, p_right.get_validated_object(), &r_valid);
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<String>::get_ptr(r_ret) = do_mod(*VariantGetInternalPtr<String>::get_ptr(left), right->get_validated_object(), nullptr);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<String>::encode(do_mod(PtrToArg<String>::convert(left), PtrToArg<Object *>::convert(right), nullptr), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::STRING; }
+};
+
+template <class T>
+class OperatorEvaluatorStringModT {
+public:
+ _FORCE_INLINE_ static String do_mod(const String &s, const T &p_value, bool *r_valid) {
+ Array values;
+ values.push_back(p_value);
+ String a = s.sprintf(values, r_valid);
+ if (r_valid) {
+ *r_valid = !*r_valid;
+ }
+ return a;
+ }
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left);
+ *r_ret = do_mod(a, *VariantGetInternalPtr<T>::get_ptr(&p_right), &r_valid);
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<String>::get_ptr(r_ret) = do_mod(*VariantGetInternalPtr<String>::get_ptr(left), *VariantGetInternalPtr<T>::get_ptr(right), nullptr);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<String>::encode(do_mod(PtrToArg<String>::convert(left), PtrToArg<T>::convert(right), nullptr), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::STRING; }
+};
+
+template <Variant::Operator op, Variant::Type type_left, Variant::Type type_right>
+class OperatorEvaluatorAlwaysTrue {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ *r_ret = true;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = true;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(true, r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+template <Variant::Operator op, Variant::Type type_left, Variant::Type type_right>
+class OperatorEvaluatorAlwaysFalse {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ *r_ret = false;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = false;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(false, r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+///// OR ///////
+
+_FORCE_INLINE_ static bool _operate_or(bool p_left, bool p_right) {
+ return p_left || p_right;
+}
+
+_FORCE_INLINE_ static bool _operate_and(bool p_left, bool p_right) {
+ return p_left && p_right;
+}
+
+_FORCE_INLINE_ static bool _operate_xor(bool p_left, bool p_right) {
+ return (p_left || p_right) && !(p_left && p_right);
+}
+
+_FORCE_INLINE_ static bool _operate_get_nil(const Variant *p_ptr) {
+ return p_ptr->get_validated_object() != nullptr;
+}
+
+_FORCE_INLINE_ static bool _operate_get_bool(const Variant *p_ptr) {
+ return *VariantGetInternalPtr<bool>::get_ptr(p_ptr);
+}
+
+_FORCE_INLINE_ static bool _operate_get_int(const Variant *p_ptr) {
+ return *VariantGetInternalPtr<int64_t>::get_ptr(p_ptr) != 0;
+}
+
+_FORCE_INLINE_ static bool _operate_get_float(const Variant *p_ptr) {
+ return *VariantGetInternalPtr<double>::get_ptr(p_ptr) != 0.0;
+}
+
+_FORCE_INLINE_ static bool _operate_get_object(const Variant *p_ptr) {
+ return p_ptr->get_validated_object() != nullptr;
+}
+
+_FORCE_INLINE_ static bool _operate_get_ptr_nil(const void *p_ptr) {
+ return false;
+}
+
+_FORCE_INLINE_ static bool _operate_get_ptr_bool(const void *p_ptr) {
+ return PtrToArg<bool>::convert(p_ptr);
+}
+
+_FORCE_INLINE_ static bool _operate_get_ptr_int(const void *p_ptr) {
+ return PtrToArg<int64_t>::convert(p_ptr) != 0;
+}
+
+_FORCE_INLINE_ static bool _operate_get_ptr_float(const void *p_ptr) {
+ return PtrToArg<double>::convert(p_ptr) != 0.0;
+}
+
+_FORCE_INLINE_ static bool _operate_get_ptr_object(const void *p_ptr) {
+ return PtrToArg<Object *>::convert(p_ptr) != nullptr;
+}
+
+#define OP_EVALUATOR(m_class_name, m_left, m_right, m_op) \
+ class m_class_name { \
+ public: \
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { \
+ *r_ret = m_op(_operate_get_##m_left(&p_left), _operate_get_##m_right(&p_right)); \
+ r_valid = true; \
+ } \
+ \
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { \
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = m_op(_operate_get_##m_left(left), _operate_get_##m_right(right)); \
+ } \
+ \
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) { \
+ PtrToArg<bool>::encode(m_op(_operate_get_ptr_##m_left(left), _operate_get_ptr_##m_right(right)), r_ret); \
+ } \
+ \
+ static Variant::Type get_return_type() { \
+ return Variant::BOOL; \
+ } \
+ };
+
+// OR
+
+// nil
+OP_EVALUATOR(OperatorEvaluatorNilXBoolOr, nil, bool, _operate_or)
+OP_EVALUATOR(OperatorEvaluatorBoolXNilOr, bool, nil, _operate_or)
+
+OP_EVALUATOR(OperatorEvaluatorNilXIntOr, nil, int, _operate_or)
+OP_EVALUATOR(OperatorEvaluatorIntXNilOr, int, nil, _operate_or)
+
+OP_EVALUATOR(OperatorEvaluatorNilXFloatOr, nil, float, _operate_or)
+OP_EVALUATOR(OperatorEvaluatorFloatXNilOr, float, nil, _operate_or)
+
+OP_EVALUATOR(OperatorEvaluatorObjectXNilOr, object, nil, _operate_or)
+OP_EVALUATOR(OperatorEvaluatorNilXObjectOr, nil, object, _operate_or)
+
+// bool
+OP_EVALUATOR(OperatorEvaluatorBoolXBoolOr, bool, bool, _operate_or)
+
+OP_EVALUATOR(OperatorEvaluatorBoolXIntOr, bool, int, _operate_or)
+OP_EVALUATOR(OperatorEvaluatorIntXBoolOr, int, bool, _operate_or)
+
+OP_EVALUATOR(OperatorEvaluatorBoolXFloatOr, bool, float, _operate_or)
+OP_EVALUATOR(OperatorEvaluatorFloatXBoolOr, float, bool, _operate_or)
+
+OP_EVALUATOR(OperatorEvaluatorBoolXObjectOr, bool, object, _operate_or)
+OP_EVALUATOR(OperatorEvaluatorObjectXBoolOr, object, bool, _operate_or)
+
+// int
+OP_EVALUATOR(OperatorEvaluatorIntXIntOr, int, int, _operate_or)
+
+OP_EVALUATOR(OperatorEvaluatorIntXFloatOr, int, float, _operate_or)
+OP_EVALUATOR(OperatorEvaluatorFloatXIntOr, float, int, _operate_or)
+
+OP_EVALUATOR(OperatorEvaluatorIntXObjectOr, int, object, _operate_or)
+OP_EVALUATOR(OperatorEvaluatorObjectXIntOr, object, int, _operate_or)
+
+// float
+OP_EVALUATOR(OperatorEvaluatorFloatXFloatOr, float, float, _operate_or)
+
+OP_EVALUATOR(OperatorEvaluatorFloatXObjectOr, float, object, _operate_or)
+OP_EVALUATOR(OperatorEvaluatorObjectXFloatOr, object, float, _operate_or)
+
+// object
+OP_EVALUATOR(OperatorEvaluatorObjectXObjectOr, object, object, _operate_or)
+
+// AND
+
+// nil
+OP_EVALUATOR(OperatorEvaluatorNilXBoolAnd, nil, bool, _operate_and)
+OP_EVALUATOR(OperatorEvaluatorBoolXNilAnd, bool, nil, _operate_and)
+
+OP_EVALUATOR(OperatorEvaluatorNilXIntAnd, nil, int, _operate_and)
+OP_EVALUATOR(OperatorEvaluatorIntXNilAnd, int, nil, _operate_and)
+
+OP_EVALUATOR(OperatorEvaluatorNilXFloatAnd, nil, float, _operate_and)
+OP_EVALUATOR(OperatorEvaluatorFloatXNilAnd, float, nil, _operate_and)
+
+OP_EVALUATOR(OperatorEvaluatorObjectXNilAnd, object, nil, _operate_and)
+OP_EVALUATOR(OperatorEvaluatorNilXObjectAnd, nil, object, _operate_and)
+
+// bool
+OP_EVALUATOR(OperatorEvaluatorBoolXBoolAnd, bool, bool, _operate_and)
+
+OP_EVALUATOR(OperatorEvaluatorBoolXIntAnd, bool, int, _operate_and)
+OP_EVALUATOR(OperatorEvaluatorIntXBoolAnd, int, bool, _operate_and)
+
+OP_EVALUATOR(OperatorEvaluatorBoolXFloatAnd, bool, float, _operate_and)
+OP_EVALUATOR(OperatorEvaluatorFloatXBoolAnd, float, bool, _operate_and)
+
+OP_EVALUATOR(OperatorEvaluatorBoolXObjectAnd, bool, object, _operate_and)
+OP_EVALUATOR(OperatorEvaluatorObjectXBoolAnd, object, bool, _operate_and)
+
+// int
+OP_EVALUATOR(OperatorEvaluatorIntXIntAnd, int, int, _operate_and)
+
+OP_EVALUATOR(OperatorEvaluatorIntXFloatAnd, int, float, _operate_and)
+OP_EVALUATOR(OperatorEvaluatorFloatXIntAnd, float, int, _operate_and)
+
+OP_EVALUATOR(OperatorEvaluatorIntXObjectAnd, int, object, _operate_and)
+OP_EVALUATOR(OperatorEvaluatorObjectXIntAnd, object, int, _operate_and)
+
+// float
+OP_EVALUATOR(OperatorEvaluatorFloatXFloatAnd, float, float, _operate_and)
+
+OP_EVALUATOR(OperatorEvaluatorFloatXObjectAnd, float, object, _operate_and)
+OP_EVALUATOR(OperatorEvaluatorObjectXFloatAnd, object, float, _operate_and)
+
+// object
+OP_EVALUATOR(OperatorEvaluatorObjectXObjectAnd, object, object, _operate_and)
+
+// XOR
+
+// nil
+OP_EVALUATOR(OperatorEvaluatorNilXBoolXor, nil, bool, _operate_xor)
+OP_EVALUATOR(OperatorEvaluatorBoolXNilXor, bool, nil, _operate_xor)
+
+OP_EVALUATOR(OperatorEvaluatorNilXIntXor, nil, int, _operate_xor)
+OP_EVALUATOR(OperatorEvaluatorIntXNilXor, int, nil, _operate_xor)
+
+OP_EVALUATOR(OperatorEvaluatorNilXFloatXor, nil, float, _operate_xor)
+OP_EVALUATOR(OperatorEvaluatorFloatXNilXor, float, nil, _operate_xor)
+
+OP_EVALUATOR(OperatorEvaluatorObjectXNilXor, object, nil, _operate_xor)
+OP_EVALUATOR(OperatorEvaluatorNilXObjectXor, nil, object, _operate_xor)
+
+// bool
+OP_EVALUATOR(OperatorEvaluatorBoolXBoolXor, bool, bool, _operate_xor)
+
+OP_EVALUATOR(OperatorEvaluatorBoolXIntXor, bool, int, _operate_xor)
+OP_EVALUATOR(OperatorEvaluatorIntXBoolXor, int, bool, _operate_xor)
+
+OP_EVALUATOR(OperatorEvaluatorBoolXFloatXor, bool, float, _operate_xor)
+OP_EVALUATOR(OperatorEvaluatorFloatXBoolXor, float, bool, _operate_xor)
+
+OP_EVALUATOR(OperatorEvaluatorBoolXObjectXor, bool, object, _operate_xor)
+OP_EVALUATOR(OperatorEvaluatorObjectXBoolXor, object, bool, _operate_xor)
+
+// int
+OP_EVALUATOR(OperatorEvaluatorIntXIntXor, int, int, _operate_xor)
+
+OP_EVALUATOR(OperatorEvaluatorIntXFloatXor, int, float, _operate_xor)
+OP_EVALUATOR(OperatorEvaluatorFloatXIntXor, float, int, _operate_xor)
+
+OP_EVALUATOR(OperatorEvaluatorIntXObjectXor, int, object, _operate_xor)
+OP_EVALUATOR(OperatorEvaluatorObjectXIntXor, object, int, _operate_xor)
+
+// float
+OP_EVALUATOR(OperatorEvaluatorFloatXFloatXor, float, float, _operate_xor)
+
+OP_EVALUATOR(OperatorEvaluatorFloatXObjectXor, float, object, _operate_xor)
+OP_EVALUATOR(OperatorEvaluatorObjectXFloatXor, object, float, _operate_xor)
+
+// object
+OP_EVALUATOR(OperatorEvaluatorObjectXObjectXor, object, object, _operate_xor)
+
+class OperatorEvaluatorNotBool {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ *r_ret = !*VariantGetInternalPtr<bool>::get_ptr(&p_left);
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = !*VariantGetInternalPtr<bool>::get_ptr(left);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(!PtrToArg<bool>::convert(left), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorNotInt {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ *r_ret = !*VariantGetInternalPtr<int64_t>::get_ptr(&p_left);
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = !*VariantGetInternalPtr<int64_t>::get_ptr(left);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(!PtrToArg<int64_t>::convert(left), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorNotFloat {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ *r_ret = !*VariantGetInternalPtr<double>::get_ptr(&p_left);
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = !*VariantGetInternalPtr<double>::get_ptr(left);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(!PtrToArg<double>::convert(left), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorNotObject {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ *r_ret = p_left.get_validated_object() == nullptr;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = left->get_validated_object() == nullptr;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<Object *>::convert(left) == nullptr, r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+////
+
+class OperatorEvaluatorInStringFind {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const String &str_a = *VariantGetInternalPtr<String>::get_ptr(&p_left);
+ const String &str_b = *VariantGetInternalPtr<String>::get_ptr(&p_right);
+
+ *r_ret = str_b.find(str_a) != -1;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ const String &str_a = *VariantGetInternalPtr<String>::get_ptr(left);
+ const String &str_b = *VariantGetInternalPtr<String>::get_ptr(right);
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = str_b.find(str_a) != -1;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<String>::convert(right).find(PtrToArg<String>::convert(left)) != -1, r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+template <class A, class B>
+class OperatorEvaluatorInArrayFind {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
+
+ *r_ret = b.find(a) != -1;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(left);
+ const B &b = *VariantGetInternalPtr<B>::get_ptr(right);
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.find(a) != -1;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<B>::convert(right).find(PtrToArg<A>::convert(left)) != -1, r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorInArrayFindNil {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Array &b = *VariantGetInternalPtr<Array>::get_ptr(&p_right);
+ *r_ret = b.find(Variant()) != -1;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ const Array &b = *VariantGetInternalPtr<Array>::get_ptr(right);
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.find(Variant()) != -1;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<Array>::convert(right).find(Variant()) != -1, r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorInArrayFindObject {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Array &b = *VariantGetInternalPtr<Array>::get_ptr(&p_right);
+ *r_ret = b.find(p_left) != -1;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ const Array &b = *VariantGetInternalPtr<Array>::get_ptr(right);
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.find(*left) != -1;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<Array>::convert(right).find(PtrToArg<Object *>::convert(left)) != -1, r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+template <class A>
+class OperatorEvaluatorInDictionaryHas {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(&p_right);
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
+
+ *r_ret = b.has(a);
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(right);
+ const A &a = *VariantGetInternalPtr<A>::get_ptr(left);
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.has(a);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<Dictionary>::convert(right).has(PtrToArg<A>::convert(left)), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorInDictionaryHasNil {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(&p_right);
+
+ *r_ret = b.has(Variant());
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(right);
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.has(Variant());
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<Dictionary>::convert(right).has(Variant()), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorInDictionaryHasObject {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(&p_right);
+
+ *r_ret = b.has(p_left);
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ const Dictionary &b = *VariantGetInternalPtr<Dictionary>::get_ptr(right);
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = b.has(*left);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<bool>::encode(PtrToArg<Dictionary>::convert(right).has(PtrToArg<Object *>::convert(left)), r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorObjectHasPropertyString {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ Object *b = p_right.get_validated_object();
+ if (!b) {
+ *r_ret = "Invalid base object for 'in'";
+ r_valid = false;
+ return;
+ }
+
+ const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left);
+
+ bool exist;
+ b->get(a, &exist);
+ *r_ret = exist;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ Object *l = right->get_validated_object();
+ ERR_FAIL_COND(l == nullptr);
+ const String &a = *VariantGetInternalPtr<String>::get_ptr(left);
+
+ bool valid;
+ l->get(a, &valid);
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = valid;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ bool valid;
+ PtrToArg<Object *>::convert(right)->get(PtrToArg<String>::convert(left), &valid);
+ PtrToArg<bool>::encode(valid, r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+class OperatorEvaluatorObjectHasPropertyStringName {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ Object *b = p_right.get_validated_object();
+ if (!b) {
+ *r_ret = "Invalid base object for 'in'";
+ r_valid = false;
+ return;
+ }
+
+ const StringName &a = *VariantGetInternalPtr<StringName>::get_ptr(&p_left);
+
+ bool exist;
+ b->get(a, &exist);
+ *r_ret = exist;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ Object *l = right->get_validated_object();
+ ERR_FAIL_COND(l == nullptr);
+ const StringName &a = *VariantGetInternalPtr<StringName>::get_ptr(left);
+
+ bool valid;
+ l->get(a, &valid);
+ *VariantGetInternalPtr<bool>::get_ptr(r_ret) = valid;
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ bool valid;
+ PtrToArg<Object *>::convert(right)->get(PtrToArg<StringName>::convert(left), &valid);
+ PtrToArg<bool>::encode(valid, r_ret);
+ }
+ static Variant::Type get_return_type() { return Variant::BOOL; }
+};
+
+#endif // VARIANT_OP_H
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index edaeddbf27..a214a238a6 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -190,10 +190,13 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
r_token.type = TK_COLOR;
return OK;
}
- case '@': {
+#ifndef DISABLE_DEPRECATED
+ case '@': // Compatibility with 3.x StringNames.
+#endif
+ case '&': { // StringName.
cchar = p_stream->get_char();
if (cchar != '"') {
- r_err_str = "Expected '\"' after '@'";
+ r_err_str = "Expected '\"' after '&'";
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
@@ -503,9 +506,9 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
} else if (id == "null" || id == "nil") {
value = Variant();
} else if (id == "inf") {
- value = Math_INF;
+ value = INFINITY;
} else if (id == "nan") {
- value = Math_NAN;
+ value = NAN;
} else if (id == "Vector2") {
Vector<real_t> args;
Error err = _parse_construct<real_t>(p_stream, args, line, r_err_str);
@@ -614,7 +617,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
}
value = Plane(args[0], args[1], args[2], args[3]);
- } else if (id == "Quat") {
+ } else if (id == "Quaternion" || id == "Quat") { // "Quat" kept for compatibility
Vector<real_t> args;
Error err = _parse_construct<real_t>(p_stream, args, line, r_err_str);
if (err) {
@@ -626,7 +629,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
return ERR_PARSE_ERROR;
}
- value = Quat(args[0], args[1], args[2], args[3]);
+ value = Quaternion(args[0], args[1], args[2], args[3]);
} else if (id == "AABB" || id == "Rect3") {
Vector<real_t> args;
Error err = _parse_construct<real_t>(p_stream, args, line, r_err_str);
@@ -653,7 +656,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
}
value = Basis(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
- } else if (id == "Transform") {
+ } else if (id == "Transform3D" || id == "Transform") { // "Transform" kept for compatibility with Godot <4.
Vector<real_t> args;
Error err = _parse_construct<real_t>(p_stream, args, line, r_err_str);
if (err) {
@@ -665,7 +668,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
return ERR_PARSE_ERROR;
}
- value = Transform(Basis(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]), Vector3(args[9], args[10], args[11]));
+ value = Transform3D(Basis(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]), Vector3(args[9], args[10], args[11]));
} else if (id == "Color") {
Vector<float> args;
Error err = _parse_construct<float>(p_stream, args, line, r_err_str);
@@ -735,14 +738,14 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
String type = token.value;
- Object *obj = ClassDB::instance(type);
+ Object *obj = ClassDB::instantiate(type);
if (!obj) {
- r_err_str = "Can't instance Object() of type: " + type;
+ r_err_str = "Can't instantiate Object() of type: " + type;
return ERR_PARSE_ERROR;
}
- REF ref = REF(Object::cast_to<Reference>(obj));
+ REF ref = REF(Object::cast_to<RefCounted>(obj));
get_token(p_stream, token, line, r_err_str);
if (token.type != TK_COMMA) {
@@ -1201,16 +1204,32 @@ Error VariantParser::_parse_tag(Token &token, Stream *p_stream, int &line, Strin
r_tag.name = "";
r_tag.fields.clear();
- while (true) {
- char32_t c = p_stream->get_char();
- if (p_stream->is_eof()) {
- r_err_str = "Unexpected EOF while parsing simple tag";
- return ERR_PARSE_ERROR;
+ if (p_stream->is_utf8()) {
+ CharString cs;
+ while (true) {
+ char c = p_stream->get_char();
+ if (p_stream->is_eof()) {
+ r_err_str = "Unexpected EOF while parsing simple tag";
+ return ERR_PARSE_ERROR;
+ }
+ if (c == ']') {
+ break;
+ }
+ cs += c;
}
- if (c == ']') {
- break;
+ r_tag.name.parse_utf8(cs.get_data(), cs.length());
+ } else {
+ while (true) {
+ char32_t c = p_stream->get_char();
+ if (p_stream->is_eof()) {
+ r_err_str = "Unexpected EOF while parsing simple tag";
+ return ERR_PARSE_ERROR;
+ }
+ if (c == ']') {
+ break;
+ }
+ r_tag.name += String::chr(c);
}
- r_tag.name += String::chr(c);
}
r_tag.name = r_tag.name.strip_edges();
@@ -1404,7 +1423,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, itos(p_variant.operator int64_t()));
} break;
case Variant::FLOAT: {
- String s = rtosfix(p_variant.operator real_t());
+ String s = rtosfix(p_variant.operator double());
if (s != "inf" && s != "nan") {
if (s.find(".") == -1 && s.find("e") == -1) {
s += ".0";
@@ -1420,47 +1439,47 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
} break;
case Variant::VECTOR2: {
Vector2 v = p_variant;
- p_store_string_func(p_store_string_ud, "Vector2( " + rtosfix(v.x) + ", " + rtosfix(v.y) + " )");
+ p_store_string_func(p_store_string_ud, "Vector2(" + rtosfix(v.x) + ", " + rtosfix(v.y) + ")");
} break;
case Variant::VECTOR2I: {
Vector2i v = p_variant;
- p_store_string_func(p_store_string_ud, "Vector2i( " + itos(v.x) + ", " + itos(v.y) + " )");
+ p_store_string_func(p_store_string_ud, "Vector2i(" + itos(v.x) + ", " + itos(v.y) + ")");
} break;
case Variant::RECT2: {
Rect2 aabb = p_variant;
- p_store_string_func(p_store_string_ud, "Rect2( " + rtosfix(aabb.position.x) + ", " + rtosfix(aabb.position.y) + ", " + rtosfix(aabb.size.x) + ", " + rtosfix(aabb.size.y) + " )");
+ p_store_string_func(p_store_string_ud, "Rect2(" + rtosfix(aabb.position.x) + ", " + rtosfix(aabb.position.y) + ", " + rtosfix(aabb.size.x) + ", " + rtosfix(aabb.size.y) + ")");
} break;
case Variant::RECT2I: {
Rect2i aabb = p_variant;
- p_store_string_func(p_store_string_ud, "Rect2i( " + itos(aabb.position.x) + ", " + itos(aabb.position.y) + ", " + itos(aabb.size.x) + ", " + itos(aabb.size.y) + " )");
+ p_store_string_func(p_store_string_ud, "Rect2i(" + itos(aabb.position.x) + ", " + itos(aabb.position.y) + ", " + itos(aabb.size.x) + ", " + itos(aabb.size.y) + ")");
} break;
case Variant::VECTOR3: {
Vector3 v = p_variant;
- p_store_string_func(p_store_string_ud, "Vector3( " + rtosfix(v.x) + ", " + rtosfix(v.y) + ", " + rtosfix(v.z) + " )");
+ p_store_string_func(p_store_string_ud, "Vector3(" + rtosfix(v.x) + ", " + rtosfix(v.y) + ", " + rtosfix(v.z) + ")");
} break;
case Variant::VECTOR3I: {
Vector3i v = p_variant;
- p_store_string_func(p_store_string_ud, "Vector3i( " + itos(v.x) + ", " + itos(v.y) + ", " + itos(v.z) + " )");
+ p_store_string_func(p_store_string_ud, "Vector3i(" + itos(v.x) + ", " + itos(v.y) + ", " + itos(v.z) + ")");
} break;
case Variant::PLANE: {
Plane p = p_variant;
- p_store_string_func(p_store_string_ud, "Plane( " + rtosfix(p.normal.x) + ", " + rtosfix(p.normal.y) + ", " + rtosfix(p.normal.z) + ", " + rtosfix(p.d) + " )");
+ p_store_string_func(p_store_string_ud, "Plane(" + rtosfix(p.normal.x) + ", " + rtosfix(p.normal.y) + ", " + rtosfix(p.normal.z) + ", " + rtosfix(p.d) + ")");
} break;
case Variant::AABB: {
AABB aabb = p_variant;
- p_store_string_func(p_store_string_ud, "AABB( " + rtosfix(aabb.position.x) + ", " + rtosfix(aabb.position.y) + ", " + rtosfix(aabb.position.z) + ", " + rtosfix(aabb.size.x) + ", " + rtosfix(aabb.size.y) + ", " + rtosfix(aabb.size.z) + " )");
+ p_store_string_func(p_store_string_ud, "AABB(" + rtosfix(aabb.position.x) + ", " + rtosfix(aabb.position.y) + ", " + rtosfix(aabb.position.z) + ", " + rtosfix(aabb.size.x) + ", " + rtosfix(aabb.size.y) + ", " + rtosfix(aabb.size.z) + ")");
} break;
- case Variant::QUAT: {
- Quat quat = p_variant;
- p_store_string_func(p_store_string_ud, "Quat( " + rtosfix(quat.x) + ", " + rtosfix(quat.y) + ", " + rtosfix(quat.z) + ", " + rtosfix(quat.w) + " )");
+ case Variant::QUATERNION: {
+ Quaternion quaternion = p_variant;
+ p_store_string_func(p_store_string_ud, "Quaternion(" + rtosfix(quaternion.x) + ", " + rtosfix(quaternion.y) + ", " + rtosfix(quaternion.z) + ", " + rtosfix(quaternion.w) + ")");
} break;
case Variant::TRANSFORM2D: {
- String s = "Transform2D( ";
+ String s = "Transform2D(";
Transform2D m3 = p_variant;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
@@ -1471,11 +1490,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
}
- p_store_string_func(p_store_string_ud, s + " )");
+ p_store_string_func(p_store_string_ud, s + ")");
} break;
case Variant::BASIS: {
- String s = "Basis( ";
+ String s = "Basis(";
Basis m3 = p_variant;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
@@ -1486,12 +1505,12 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
}
- p_store_string_func(p_store_string_ud, s + " )");
+ p_store_string_func(p_store_string_ud, s + ")");
} break;
- case Variant::TRANSFORM: {
- String s = "Transform( ";
- Transform t = p_variant;
+ case Variant::TRANSFORM3D: {
+ String s = "Transform3D(";
+ Transform3D t = p_variant;
Basis &m3 = t.basis;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
@@ -1504,19 +1523,19 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
s = s + ", " + rtosfix(t.origin.x) + ", " + rtosfix(t.origin.y) + ", " + rtosfix(t.origin.z);
- p_store_string_func(p_store_string_ud, s + " )");
+ p_store_string_func(p_store_string_ud, s + ")");
} break;
// misc types
case Variant::COLOR: {
Color c = p_variant;
- p_store_string_func(p_store_string_ud, "Color( " + rtosfix(c.r) + ", " + rtosfix(c.g) + ", " + rtosfix(c.b) + ", " + rtosfix(c.a) + " )");
+ p_store_string_func(p_store_string_ud, "Color(" + rtosfix(c.r) + ", " + rtosfix(c.g) + ", " + rtosfix(c.b) + ", " + rtosfix(c.a) + ")");
} break;
case Variant::STRING_NAME: {
String str = p_variant;
- str = "@\"" + str.c_escape() + "\"";
+ str = "&\"" + str.c_escape() + "\"";
p_store_string_func(p_store_string_ud, str);
} break;
@@ -1550,7 +1569,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
if (res_text == String() && res->get_path().is_resource_file()) {
//external resource
String path = res->get_path();
- res_text = "Resource( \"" + path + "\")";
+ res_text = "Resource(\"" + path + "\")";
}
//could come up with some sort of text
@@ -1567,8 +1586,8 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
List<PropertyInfo> props;
obj->get_property_list(&props);
bool first = true;
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- if (E->get().usage & PROPERTY_USAGE_STORAGE || E->get().usage & PROPERTY_USAGE_SCRIPT_VARIABLE) {
+ for (const PropertyInfo &E : props) {
+ if (E.usage & PROPERTY_USAGE_STORAGE || E.usage & PROPERTY_USAGE_SCRIPT_VARIABLE) {
//must be serialized
if (first) {
@@ -1577,8 +1596,8 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, ",");
}
- p_store_string_func(p_store_string_ud, "\"" + E->get().name + "\":");
- write(obj->get(E->get().name), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud);
+ p_store_string_func(p_store_string_ud, "\"" + E.name + "\":");
+ write(obj->get(E.name), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud);
}
}
@@ -1596,7 +1615,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, "{\n");
for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
/*
- if (!_check_type(dict[E->get()]))
+ if (!_check_type(dict[E]))
continue;
*/
write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud);
@@ -1613,7 +1632,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
} break;
case Variant::ARRAY: {
- p_store_string_func(p_store_string_ud, "[ ");
+ p_store_string_func(p_store_string_ud, "[");
Array array = p_variant;
int len = array.size();
for (int i = 0; i < len; i++) {
@@ -1622,12 +1641,12 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
write(array[i], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud);
}
- p_store_string_func(p_store_string_ud, " ]");
+ p_store_string_func(p_store_string_ud, "]");
} break;
case Variant::PACKED_BYTE_ARRAY: {
- p_store_string_func(p_store_string_ud, "PackedByteArray( ");
+ p_store_string_func(p_store_string_ud, "PackedByteArray(");
String s;
Vector<uint8_t> data = p_variant;
int len = data.size();
@@ -1641,11 +1660,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, itos(ptr[i]));
}
- p_store_string_func(p_store_string_ud, " )");
+ p_store_string_func(p_store_string_ud, ")");
} break;
case Variant::PACKED_INT32_ARRAY: {
- p_store_string_func(p_store_string_ud, "PackedInt32Array( ");
+ p_store_string_func(p_store_string_ud, "PackedInt32Array(");
Vector<int32_t> data = p_variant;
int32_t len = data.size();
const int32_t *ptr = data.ptr();
@@ -1658,11 +1677,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, itos(ptr[i]));
}
- p_store_string_func(p_store_string_ud, " )");
+ p_store_string_func(p_store_string_ud, ")");
} break;
case Variant::PACKED_INT64_ARRAY: {
- p_store_string_func(p_store_string_ud, "PackedInt64Array( ");
+ p_store_string_func(p_store_string_ud, "PackedInt64Array(");
Vector<int64_t> data = p_variant;
int64_t len = data.size();
const int64_t *ptr = data.ptr();
@@ -1675,11 +1694,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, itos(ptr[i]));
}
- p_store_string_func(p_store_string_ud, " )");
+ p_store_string_func(p_store_string_ud, ")");
} break;
case Variant::PACKED_FLOAT32_ARRAY: {
- p_store_string_func(p_store_string_ud, "PackedFloat32Array( ");
+ p_store_string_func(p_store_string_ud, "PackedFloat32Array(");
Vector<float> data = p_variant;
int len = data.size();
const float *ptr = data.ptr();
@@ -1691,11 +1710,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, rtosfix(ptr[i]));
}
- p_store_string_func(p_store_string_ud, " )");
+ p_store_string_func(p_store_string_ud, ")");
} break;
case Variant::PACKED_FLOAT64_ARRAY: {
- p_store_string_func(p_store_string_ud, "PackedFloat64Array( ");
+ p_store_string_func(p_store_string_ud, "PackedFloat64Array(");
Vector<double> data = p_variant;
int len = data.size();
const double *ptr = data.ptr();
@@ -1707,11 +1726,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, rtosfix(ptr[i]));
}
- p_store_string_func(p_store_string_ud, " )");
+ p_store_string_func(p_store_string_ud, ")");
} break;
case Variant::PACKED_STRING_ARRAY: {
- p_store_string_func(p_store_string_ud, "PackedStringArray( ");
+ p_store_string_func(p_store_string_ud, "PackedStringArray(");
Vector<String> data = p_variant;
int len = data.size();
const String *ptr = data.ptr();
@@ -1727,11 +1746,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, "\"" + str.c_escape() + "\"");
}
- p_store_string_func(p_store_string_ud, " )");
+ p_store_string_func(p_store_string_ud, ")");
} break;
case Variant::PACKED_VECTOR2_ARRAY: {
- p_store_string_func(p_store_string_ud, "PackedVector2Array( ");
+ p_store_string_func(p_store_string_ud, "PackedVector2Array(");
Vector<Vector2> data = p_variant;
int len = data.size();
const Vector2 *ptr = data.ptr();
@@ -1743,11 +1762,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, rtosfix(ptr[i].x) + ", " + rtosfix(ptr[i].y));
}
- p_store_string_func(p_store_string_ud, " )");
+ p_store_string_func(p_store_string_ud, ")");
} break;
case Variant::PACKED_VECTOR3_ARRAY: {
- p_store_string_func(p_store_string_ud, "PackedVector3Array( ");
+ p_store_string_func(p_store_string_ud, "PackedVector3Array(");
Vector<Vector3> data = p_variant;
int len = data.size();
const Vector3 *ptr = data.ptr();
@@ -1759,12 +1778,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, rtosfix(ptr[i].x) + ", " + rtosfix(ptr[i].y) + ", " + rtosfix(ptr[i].z));
}
- p_store_string_func(p_store_string_ud, " )");
+ p_store_string_func(p_store_string_ud, ")");
} break;
case Variant::PACKED_COLOR_ARRAY: {
- p_store_string_func(p_store_string_ud, "PackedColorArray( ");
-
+ p_store_string_func(p_store_string_ud, "PackedColorArray(");
Vector<Color> data = p_variant;
int len = data.size();
const Color *ptr = data.ptr();
@@ -1776,7 +1794,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, rtosfix(ptr[i].r) + ", " + rtosfix(ptr[i].g) + ", " + rtosfix(ptr[i].b) + ", " + rtosfix(ptr[i].a));
}
- p_store_string_func(p_store_string_ud, " )");
+ p_store_string_func(p_store_string_ud, ")");
} break;
default: {
diff --git a/core/variant/variant_parser.h b/core/variant/variant_parser.h
index 5703f0200c..1ba26db6ed 100644
--- a/core/variant/variant_parser.h
+++ b/core/variant/variant_parser.h
@@ -31,8 +31,8 @@
#ifndef VARIANT_PARSER_H
#define VARIANT_PARSER_H
+#include "core/io/file_access.h"
#include "core/io/resource.h"
-#include "core/os/file_access.h"
#include "core/variant/variant.h"
class VariantParser {
@@ -73,9 +73,9 @@ public:
struct ResourceParser {
void *userdata = nullptr;
- ParseResourceFunc func;
- ParseResourceFunc ext_func;
- ParseResourceFunc sub_func;
+ ParseResourceFunc func = nullptr;
+ ParseResourceFunc ext_func = nullptr;
+ ParseResourceFunc sub_func = nullptr;
};
enum TokenType {
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index 9ab8602782..ae3c7685fd 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -28,282 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "variant.h"
-
-#include "core/core_string_names.h"
-#include "core/debugger/engine_debugger.h"
-#include "core/object/class_db.h"
-#include "core/templates/local_vector.h"
-#include "core/variant/variant_internal.h"
-
-/**** NAMED SETTERS AND GETTERS ****/
-
-#define SETGET_STRUCT(m_base_type, m_member_type, m_member) \
- struct VariantSetGet_##m_base_type##_##m_member { \
- static void get(const Variant *base, Variant *member) { \
- VariantTypeAdjust<m_member_type>::adjust(member); \
- *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member; \
- } \
- static void ptr_get(const void *base, void *member) { \
- PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_member, member); \
- } \
- static void set(Variant *base, const Variant *value, bool &valid) { \
- if (value->get_type() == GetTypeInfo<m_member_type>::VARIANT_TYPE) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \
- valid = true; \
- } else { \
- valid = false; \
- } \
- } \
- static void validated_set(Variant *base, const Variant *value) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \
- } \
- static void ptr_set(void *base, const void *member) { \
- m_base_type b = PtrToArg<m_base_type>::convert(base); \
- b.m_member = PtrToArg<m_member_type>::convert(member); \
- PtrToArg<m_base_type>::encode(b, base); \
- } \
- static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \
- };
-
-#define SETGET_NUMBER_STRUCT(m_base_type, m_member_type, m_member) \
- struct VariantSetGet_##m_base_type##_##m_member { \
- static void get(const Variant *base, Variant *member) { \
- VariantTypeAdjust<m_member_type>::adjust(member); \
- *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member; \
- } \
- static void ptr_get(const void *base, void *member) { \
- PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_member, member); \
- } \
- static void set(Variant *base, const Variant *value, bool &valid) { \
- if (value->get_type() == Variant::FLOAT) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<double>::get_ptr(value); \
- valid = true; \
- } else if (value->get_type() == Variant::INT) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<int64_t>::get_ptr(value); \
- valid = true; \
- } else { \
- valid = false; \
- } \
- } \
- static void validated_set(Variant *base, const Variant *value) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \
- } \
- static void ptr_set(void *base, const void *member) { \
- m_base_type b = PtrToArg<m_base_type>::convert(base); \
- b.m_member = PtrToArg<m_member_type>::convert(member); \
- PtrToArg<m_base_type>::encode(b, base); \
- } \
- static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \
- };
-
-#define SETGET_STRUCT_CUSTOM(m_base_type, m_member_type, m_member, m_custom) \
- struct VariantSetGet_##m_base_type##_##m_member { \
- static void get(const Variant *base, Variant *member) { \
- VariantTypeAdjust<m_member_type>::adjust(member); \
- *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom; \
- } \
- static void ptr_get(const void *base, void *member) { \
- PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_custom, member); \
- } \
- static void set(Variant *base, const Variant *value, bool &valid) { \
- if (value->get_type() == GetTypeInfo<m_member_type>::VARIANT_TYPE) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \
- valid = true; \
- } else { \
- valid = false; \
- } \
- } \
- static void validated_set(Variant *base, const Variant *value) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \
- } \
- static void ptr_set(void *base, const void *member) { \
- m_base_type b = PtrToArg<m_base_type>::convert(base); \
- b.m_custom = PtrToArg<m_member_type>::convert(member); \
- PtrToArg<m_base_type>::encode(b, base); \
- } \
- static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \
- };
-
-#define SETGET_NUMBER_STRUCT_CUSTOM(m_base_type, m_member_type, m_member, m_custom) \
- struct VariantSetGet_##m_base_type##_##m_member { \
- static void get(const Variant *base, Variant *member) { \
- VariantTypeAdjust<m_member_type>::adjust(member); \
- *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom; \
- } \
- static void ptr_get(const void *base, void *member) { \
- PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_custom, member); \
- } \
- static void set(Variant *base, const Variant *value, bool &valid) { \
- if (value->get_type() == Variant::FLOAT) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<double>::get_ptr(value); \
- valid = true; \
- } else if (value->get_type() == Variant::INT) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<int64_t>::get_ptr(value); \
- valid = true; \
- } else { \
- valid = false; \
- } \
- } \
- static void validated_set(Variant *base, const Variant *value) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \
- } \
- static void ptr_set(void *base, const void *member) { \
- m_base_type b = PtrToArg<m_base_type>::convert(base); \
- b.m_custom = PtrToArg<m_member_type>::convert(member); \
- PtrToArg<m_base_type>::encode(b, base); \
- } \
- static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \
- };
-
-#define SETGET_STRUCT_FUNC(m_base_type, m_member_type, m_member, m_setter, m_getter) \
- struct VariantSetGet_##m_base_type##_##m_member { \
- static void get(const Variant *base, Variant *member) { \
- VariantTypeAdjust<m_member_type>::adjust(member); \
- *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_getter(); \
- } \
- static void ptr_get(const void *base, void *member) { \
- PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_getter(), member); \
- } \
- static void set(Variant *base, const Variant *value, bool &valid) { \
- if (value->get_type() == GetTypeInfo<m_member_type>::VARIANT_TYPE) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<m_member_type>::get_ptr(value)); \
- valid = true; \
- } else { \
- valid = false; \
- } \
- } \
- static void validated_set(Variant *base, const Variant *value) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<m_member_type>::get_ptr(value)); \
- } \
- static void ptr_set(void *base, const void *member) { \
- m_base_type b = PtrToArg<m_base_type>::convert(base); \
- b.m_setter(PtrToArg<m_member_type>::convert(member)); \
- PtrToArg<m_base_type>::encode(b, base); \
- } \
- static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \
- };
-
-#define SETGET_NUMBER_STRUCT_FUNC(m_base_type, m_member_type, m_member, m_setter, m_getter) \
- struct VariantSetGet_##m_base_type##_##m_member { \
- static void get(const Variant *base, Variant *member) { \
- VariantTypeAdjust<m_member_type>::adjust(member); \
- *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_getter(); \
- } \
- static void ptr_get(const void *base, void *member) { \
- PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_getter(), member); \
- } \
- static void set(Variant *base, const Variant *value, bool &valid) { \
- if (value->get_type() == Variant::FLOAT) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<double>::get_ptr(value)); \
- valid = true; \
- } else if (value->get_type() == Variant::INT) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<int64_t>::get_ptr(value)); \
- valid = true; \
- } else { \
- valid = false; \
- } \
- } \
- static void validated_set(Variant *base, const Variant *value) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<m_member_type>::get_ptr(value)); \
- } \
- static void ptr_set(void *base, const void *member) { \
- m_base_type b = PtrToArg<m_base_type>::convert(base); \
- b.m_setter(PtrToArg<m_member_type>::convert(member)); \
- PtrToArg<m_base_type>::encode(b, base); \
- } \
- static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \
- };
-
-#define SETGET_STRUCT_FUNC_INDEX(m_base_type, m_member_type, m_member, m_setter, m_getter, m_index) \
- struct VariantSetGet_##m_base_type##_##m_member { \
- static void get(const Variant *base, Variant *member) { \
- VariantTypeAdjust<m_member_type>::adjust(member); \
- *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_getter(m_index); \
- } \
- static void ptr_get(const void *base, void *member) { \
- PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_getter(m_index), member); \
- } \
- static void set(Variant *base, const Variant *value, bool &valid) { \
- if (value->get_type() == GetTypeInfo<m_member_type>::VARIANT_TYPE) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(m_index, *VariantGetInternalPtr<m_member_type>::get_ptr(value)); \
- valid = true; \
- } else { \
- valid = false; \
- } \
- } \
- static void validated_set(Variant *base, const Variant *value) { \
- VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(m_index, *VariantGetInternalPtr<m_member_type>::get_ptr(value)); \
- } \
- static void ptr_set(void *base, const void *member) { \
- m_base_type b = PtrToArg<m_base_type>::convert(base); \
- b.m_setter(m_index, PtrToArg<m_member_type>::convert(member)); \
- PtrToArg<m_base_type>::encode(b, base); \
- } \
- static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \
- };
-
-SETGET_NUMBER_STRUCT(Vector2, double, x)
-SETGET_NUMBER_STRUCT(Vector2, double, y)
-
-SETGET_NUMBER_STRUCT(Vector2i, int64_t, x)
-SETGET_NUMBER_STRUCT(Vector2i, int64_t, y)
-
-SETGET_NUMBER_STRUCT(Vector3, double, x)
-SETGET_NUMBER_STRUCT(Vector3, double, y)
-SETGET_NUMBER_STRUCT(Vector3, double, z)
-
-SETGET_NUMBER_STRUCT(Vector3i, int64_t, x)
-SETGET_NUMBER_STRUCT(Vector3i, int64_t, y)
-SETGET_NUMBER_STRUCT(Vector3i, int64_t, z)
-
-SETGET_STRUCT(Rect2, Vector2, position)
-SETGET_STRUCT(Rect2, Vector2, size)
-SETGET_STRUCT_FUNC(Rect2, Vector2, end, set_end, get_end)
-
-SETGET_STRUCT(Rect2i, Vector2i, position)
-SETGET_STRUCT(Rect2i, Vector2i, size)
-SETGET_STRUCT_FUNC(Rect2i, Vector2i, end, set_end, get_end)
-
-SETGET_STRUCT(AABB, Vector3, position)
-SETGET_STRUCT(AABB, Vector3, size)
-SETGET_STRUCT_FUNC(AABB, Vector3, end, set_end, get_end)
-
-SETGET_STRUCT_CUSTOM(Transform2D, Vector2, x, elements[0])
-SETGET_STRUCT_CUSTOM(Transform2D, Vector2, y, elements[1])
-SETGET_STRUCT_CUSTOM(Transform2D, Vector2, origin, elements[2])
-
-SETGET_NUMBER_STRUCT_CUSTOM(Plane, double, x, normal.x)
-SETGET_NUMBER_STRUCT_CUSTOM(Plane, double, y, normal.y)
-SETGET_NUMBER_STRUCT_CUSTOM(Plane, double, z, normal.z)
-SETGET_STRUCT(Plane, Vector3, normal)
-SETGET_NUMBER_STRUCT(Plane, double, d)
-
-SETGET_NUMBER_STRUCT(Quat, double, x)
-SETGET_NUMBER_STRUCT(Quat, double, y)
-SETGET_NUMBER_STRUCT(Quat, double, z)
-SETGET_NUMBER_STRUCT(Quat, double, w)
-
-SETGET_STRUCT_FUNC_INDEX(Basis, Vector3, x, set_axis, get_axis, 0)
-SETGET_STRUCT_FUNC_INDEX(Basis, Vector3, y, set_axis, get_axis, 1)
-SETGET_STRUCT_FUNC_INDEX(Basis, Vector3, z, set_axis, get_axis, 2)
-
-SETGET_STRUCT(Transform, Basis, basis)
-SETGET_STRUCT(Transform, Vector3, origin)
-
-SETGET_NUMBER_STRUCT(Color, double, r)
-SETGET_NUMBER_STRUCT(Color, double, g)
-SETGET_NUMBER_STRUCT(Color, double, b)
-SETGET_NUMBER_STRUCT(Color, double, a)
-
-SETGET_NUMBER_STRUCT_FUNC(Color, int64_t, r8, set_r8, get_r8)
-SETGET_NUMBER_STRUCT_FUNC(Color, int64_t, g8, set_g8, get_g8)
-SETGET_NUMBER_STRUCT_FUNC(Color, int64_t, b8, set_b8, get_b8)
-SETGET_NUMBER_STRUCT_FUNC(Color, int64_t, a8, set_a8, get_a8)
-
-SETGET_NUMBER_STRUCT_FUNC(Color, double, h, set_h, get_h)
-SETGET_NUMBER_STRUCT_FUNC(Color, double, s, set_s, get_s)
-SETGET_NUMBER_STRUCT_FUNC(Color, double, v, set_v, get_v)
+#include "variant_setget.h"
struct VariantSetterGetterInfo {
void (*setter)(Variant *base, const Variant *value, bool &valid);
@@ -326,7 +51,7 @@ static void register_member(Variant::Type p_type, const StringName &p_member) {
sgi.ptr_setter = T::ptr_set;
sgi.getter = T::get;
- sgi.validated_getter = T::get;
+ sgi.validated_getter = T::validated_get;
sgi.ptr_getter = T::ptr_get;
sgi.member_type = T::get_type();
@@ -374,17 +99,17 @@ void register_named_setters_getters() {
REGISTER_MEMBER(Plane, d);
REGISTER_MEMBER(Plane, normal);
- REGISTER_MEMBER(Quat, x);
- REGISTER_MEMBER(Quat, y);
- REGISTER_MEMBER(Quat, z);
- REGISTER_MEMBER(Quat, w);
+ REGISTER_MEMBER(Quaternion, x);
+ REGISTER_MEMBER(Quaternion, y);
+ REGISTER_MEMBER(Quaternion, z);
+ REGISTER_MEMBER(Quaternion, w);
REGISTER_MEMBER(Basis, x);
REGISTER_MEMBER(Basis, y);
REGISTER_MEMBER(Basis, z);
- REGISTER_MEMBER(Transform, basis);
- REGISTER_MEMBER(Transform, origin);
+ REGISTER_MEMBER(Transform3D, basis);
+ REGISTER_MEMBER(Transform3D, origin);
REGISTER_MEMBER(Color, r);
REGISTER_MEMBER(Color, g);
@@ -975,7 +700,7 @@ INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector2, double, real_t, 2)
INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector2i, int64_t, int32_t, 2)
INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector3, double, real_t, 3)
INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector3i, int64_t, int32_t, 3)
-INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Quat, double, real_t, 4)
+INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Quaternion, double, real_t, 4)
INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Color, double, float, 4)
INDEXED_SETGET_STRUCT_BULTIN_ACCESSOR(Transform2D, Vector2, .elements, 3)
@@ -1037,7 +762,7 @@ void register_indexed_setters_getters() {
REGISTER_INDEXED_MEMBER(Vector2i);
REGISTER_INDEXED_MEMBER(Vector3);
REGISTER_INDEXED_MEMBER(Vector3i);
- REGISTER_INDEXED_MEMBER(Quat);
+ REGISTER_INDEXED_MEMBER(Quaternion);
REGISTER_INDEXED_MEMBER(Color);
REGISTER_INDEXED_MEMBER(Transform2D);
REGISTER_INDEXED_MEMBER(Basis);
@@ -1146,7 +871,7 @@ struct VariantKeyedSetGetDictionary {
*r_valid = true;
return VariantGetInternalPtr<Dictionary>::get_ptr(base)->has(*key);
}
- static bool ptr_has(const void *base, const void *key) {
+ static uint32_t ptr_has(const void *base, const void *key) {
/* avoid ptrconvert for performance*/
const Dictionary &v = *reinterpret_cast<const Dictionary *>(base);
return v.has(PtrToArg<Variant>::convert(key));
@@ -1196,7 +921,7 @@ struct VariantKeyedSetGetObject {
obj->getvar(*key, &exists);
return exists;
}
- static bool ptr_has(const void *base, const void *key) {
+ static uint32_t ptr_has(const void *base, const void *key) {
const Object *obj = PtrToArg<Object *>::convert(base);
ERR_FAIL_COND_V(!obj, false);
bool valid;
@@ -1368,9 +1093,9 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const {
const Dictionary *dic = reinterpret_cast<const Dictionary *>(_data._mem);
List<Variant> keys;
dic->get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- if (E->get().get_type() == Variant::STRING) {
- p_list->push_back(PropertyInfo(Variant::STRING, E->get()));
+ for (const Variant &E : keys) {
+ if (E.get_type() == Variant::STRING) {
+ p_list->push_back(PropertyInfo(Variant::STRING, E));
}
}
} else if (type == OBJECT) {
@@ -1381,10 +1106,10 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const {
} else {
List<StringName> members;
get_member_list(type, &members);
- for (List<StringName>::Element *E = members.front(); E; E = E->next()) {
+ for (const StringName &E : members) {
PropertyInfo pi;
- pi.name = E->get();
- pi.type = get_member_type(type, E->get());
+ pi.name = E;
+ pi.type = get_member_type(type, E);
p_list->push_back(pi);
}
}
@@ -1453,7 +1178,7 @@ bool Variant::iter_init(Variant &r_iter, bool &valid) const {
#ifdef DEBUG_ENABLED
- if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (EngineDebugger::is_active() && !_get_obj().id.is_ref_counted() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
valid = false;
return false;
}
@@ -1680,7 +1405,7 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const {
#ifdef DEBUG_ENABLED
- if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (EngineDebugger::is_active() && !_get_obj().id.is_ref_counted() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
valid = false;
return false;
}
@@ -1865,7 +1590,7 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const {
return Variant();
}
#ifdef DEBUG_ENABLED
- if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (EngineDebugger::is_active() && !_get_obj().id.is_ref_counted() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
r_valid = false;
return Variant();
}
@@ -2135,10 +1860,10 @@ void Variant::blend(const Variant &a, const Variant &b, float c, Variant &r_dst)
r_dst = ::AABB(ra->position + rb->position * c, ra->size + rb->size * c);
}
return;
- case QUAT: {
- Quat empty_rot;
- const Quat *qa = reinterpret_cast<const Quat *>(a._data._mem);
- const Quat *qb = reinterpret_cast<const Quat *>(b._data._mem);
+ case QUATERNION: {
+ Quaternion empty_rot;
+ const Quaternion *qa = reinterpret_cast<const Quaternion *>(a._data._mem);
+ const Quaternion *qb = reinterpret_cast<const Quaternion *>(b._data._mem);
r_dst = *qa * empty_rot.slerp(*qb, c);
}
return;
@@ -2295,8 +2020,8 @@ void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant &
r_dst = a;
}
return;
- case QUAT: {
- r_dst = reinterpret_cast<const Quat *>(a._data._mem)->slerp(*reinterpret_cast<const Quat *>(b._data._mem), c);
+ case QUATERNION: {
+ r_dst = reinterpret_cast<const Quaternion *>(a._data._mem)->slerp(*reinterpret_cast<const Quaternion *>(b._data._mem), c);
}
return;
case AABB: {
@@ -2304,11 +2029,11 @@ void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant &
}
return;
case BASIS: {
- r_dst = Transform(*a._data._basis).interpolate_with(Transform(*b._data._basis), c).basis;
+ r_dst = Transform3D(*a._data._basis).interpolate_with(Transform3D(*b._data._basis), c).basis;
}
return;
- case TRANSFORM: {
- r_dst = a._data._transform->interpolate_with(*b._data._transform, c);
+ case TRANSFORM3D: {
+ r_dst = a._data._transform3d->interpolate_with(*b._data._transform3d, c);
}
return;
case COLOR: {
diff --git a/core/variant/variant_setget.h b/core/variant/variant_setget.h
new file mode 100644
index 0000000000..dbf24ab3e3
--- /dev/null
+++ b/core/variant/variant_setget.h
@@ -0,0 +1,332 @@
+/*************************************************************************/
+/* variant_setget.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef VARIANT_SETGET_H
+#define VARIANT_SETGET_H
+
+#include "variant.h"
+
+#include "core/core_string_names.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/object/class_db.h"
+#include "core/templates/local_vector.h"
+#include "core/variant/variant_internal.h"
+
+/**** NAMED SETTERS AND GETTERS ****/
+
+#define SETGET_STRUCT(m_base_type, m_member_type, m_member) \
+ struct VariantSetGet_##m_base_type##_##m_member { \
+ static void get(const Variant *base, Variant *member) { \
+ VariantTypeAdjust<m_member_type>::adjust(member); \
+ *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member; \
+ } \
+ static inline void validated_get(const Variant *base, Variant *member) { \
+ *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member; \
+ } \
+ static void ptr_get(const void *base, void *member) { \
+ PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_member, member); \
+ } \
+ static void set(Variant *base, const Variant *value, bool &valid) { \
+ if (value->get_type() == GetTypeInfo<m_member_type>::VARIANT_TYPE) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \
+ valid = true; \
+ } else { \
+ valid = false; \
+ } \
+ } \
+ static inline void validated_set(Variant *base, const Variant *value) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \
+ } \
+ static void ptr_set(void *base, const void *member) { \
+ m_base_type b = PtrToArg<m_base_type>::convert(base); \
+ b.m_member = PtrToArg<m_member_type>::convert(member); \
+ PtrToArg<m_base_type>::encode(b, base); \
+ } \
+ static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \
+ };
+
+#define SETGET_NUMBER_STRUCT(m_base_type, m_member_type, m_member) \
+ struct VariantSetGet_##m_base_type##_##m_member { \
+ static void get(const Variant *base, Variant *member) { \
+ VariantTypeAdjust<m_member_type>::adjust(member); \
+ *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member; \
+ } \
+ static inline void validated_get(const Variant *base, Variant *member) { \
+ *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member; \
+ } \
+ static void ptr_get(const void *base, void *member) { \
+ PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_member, member); \
+ } \
+ static void set(Variant *base, const Variant *value, bool &valid) { \
+ if (value->get_type() == Variant::FLOAT) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<double>::get_ptr(value); \
+ valid = true; \
+ } else if (value->get_type() == Variant::INT) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<int64_t>::get_ptr(value); \
+ valid = true; \
+ } else { \
+ valid = false; \
+ } \
+ } \
+ static inline void validated_set(Variant *base, const Variant *value) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_member = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \
+ } \
+ static void ptr_set(void *base, const void *member) { \
+ m_base_type b = PtrToArg<m_base_type>::convert(base); \
+ b.m_member = PtrToArg<m_member_type>::convert(member); \
+ PtrToArg<m_base_type>::encode(b, base); \
+ } \
+ static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \
+ };
+
+#define SETGET_STRUCT_CUSTOM(m_base_type, m_member_type, m_member, m_custom) \
+ struct VariantSetGet_##m_base_type##_##m_member { \
+ static void get(const Variant *base, Variant *member) { \
+ VariantTypeAdjust<m_member_type>::adjust(member); \
+ *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom; \
+ } \
+ static inline void validated_get(const Variant *base, Variant *member) { \
+ *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom; \
+ } \
+ static void ptr_get(const void *base, void *member) { \
+ PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_custom, member); \
+ } \
+ static void set(Variant *base, const Variant *value, bool &valid) { \
+ if (value->get_type() == GetTypeInfo<m_member_type>::VARIANT_TYPE) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \
+ valid = true; \
+ } else { \
+ valid = false; \
+ } \
+ } \
+ static inline void validated_set(Variant *base, const Variant *value) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \
+ } \
+ static void ptr_set(void *base, const void *member) { \
+ m_base_type b = PtrToArg<m_base_type>::convert(base); \
+ b.m_custom = PtrToArg<m_member_type>::convert(member); \
+ PtrToArg<m_base_type>::encode(b, base); \
+ } \
+ static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \
+ };
+
+#define SETGET_NUMBER_STRUCT_CUSTOM(m_base_type, m_member_type, m_member, m_custom) \
+ struct VariantSetGet_##m_base_type##_##m_member { \
+ static void get(const Variant *base, Variant *member) { \
+ VariantTypeAdjust<m_member_type>::adjust(member); \
+ *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom; \
+ } \
+ static inline void validated_get(const Variant *base, Variant *member) { \
+ *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom; \
+ } \
+ static void ptr_get(const void *base, void *member) { \
+ PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_custom, member); \
+ } \
+ static void set(Variant *base, const Variant *value, bool &valid) { \
+ if (value->get_type() == Variant::FLOAT) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<double>::get_ptr(value); \
+ valid = true; \
+ } else if (value->get_type() == Variant::INT) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<int64_t>::get_ptr(value); \
+ valid = true; \
+ } else { \
+ valid = false; \
+ } \
+ } \
+ static inline void validated_set(Variant *base, const Variant *value) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_custom = *VariantGetInternalPtr<m_member_type>::get_ptr(value); \
+ } \
+ static void ptr_set(void *base, const void *member) { \
+ m_base_type b = PtrToArg<m_base_type>::convert(base); \
+ b.m_custom = PtrToArg<m_member_type>::convert(member); \
+ PtrToArg<m_base_type>::encode(b, base); \
+ } \
+ static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \
+ };
+
+#define SETGET_STRUCT_FUNC(m_base_type, m_member_type, m_member, m_setter, m_getter) \
+ struct VariantSetGet_##m_base_type##_##m_member { \
+ static void get(const Variant *base, Variant *member) { \
+ VariantTypeAdjust<m_member_type>::adjust(member); \
+ *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_getter(); \
+ } \
+ static inline void validated_get(const Variant *base, Variant *member) { \
+ *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_getter(); \
+ } \
+ static void ptr_get(const void *base, void *member) { \
+ PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_getter(), member); \
+ } \
+ static void set(Variant *base, const Variant *value, bool &valid) { \
+ if (value->get_type() == GetTypeInfo<m_member_type>::VARIANT_TYPE) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<m_member_type>::get_ptr(value)); \
+ valid = true; \
+ } else { \
+ valid = false; \
+ } \
+ } \
+ static inline void validated_set(Variant *base, const Variant *value) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<m_member_type>::get_ptr(value)); \
+ } \
+ static void ptr_set(void *base, const void *member) { \
+ m_base_type b = PtrToArg<m_base_type>::convert(base); \
+ b.m_setter(PtrToArg<m_member_type>::convert(member)); \
+ PtrToArg<m_base_type>::encode(b, base); \
+ } \
+ static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \
+ };
+
+#define SETGET_NUMBER_STRUCT_FUNC(m_base_type, m_member_type, m_member, m_setter, m_getter) \
+ struct VariantSetGet_##m_base_type##_##m_member { \
+ static void get(const Variant *base, Variant *member) { \
+ VariantTypeAdjust<m_member_type>::adjust(member); \
+ *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_getter(); \
+ } \
+ static inline void validated_get(const Variant *base, Variant *member) { \
+ *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_getter(); \
+ } \
+ static void ptr_get(const void *base, void *member) { \
+ PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_getter(), member); \
+ } \
+ static void set(Variant *base, const Variant *value, bool &valid) { \
+ if (value->get_type() == Variant::FLOAT) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<double>::get_ptr(value)); \
+ valid = true; \
+ } else if (value->get_type() == Variant::INT) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<int64_t>::get_ptr(value)); \
+ valid = true; \
+ } else { \
+ valid = false; \
+ } \
+ } \
+ static inline void validated_set(Variant *base, const Variant *value) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(*VariantGetInternalPtr<m_member_type>::get_ptr(value)); \
+ } \
+ static void ptr_set(void *base, const void *member) { \
+ m_base_type b = PtrToArg<m_base_type>::convert(base); \
+ b.m_setter(PtrToArg<m_member_type>::convert(member)); \
+ PtrToArg<m_base_type>::encode(b, base); \
+ } \
+ static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \
+ };
+
+#define SETGET_STRUCT_FUNC_INDEX(m_base_type, m_member_type, m_member, m_setter, m_getter, m_index) \
+ struct VariantSetGet_##m_base_type##_##m_member { \
+ static void get(const Variant *base, Variant *member) { \
+ VariantTypeAdjust<m_member_type>::adjust(member); \
+ *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_getter(m_index); \
+ } \
+ static inline void validated_get(const Variant *base, Variant *member) { \
+ *VariantGetInternalPtr<m_member_type>::get_ptr(member) = VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_getter(m_index); \
+ } \
+ static void ptr_get(const void *base, void *member) { \
+ PtrToArg<m_member_type>::encode(PtrToArg<m_base_type>::convert(base).m_getter(m_index), member); \
+ } \
+ static void set(Variant *base, const Variant *value, bool &valid) { \
+ if (value->get_type() == GetTypeInfo<m_member_type>::VARIANT_TYPE) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(m_index, *VariantGetInternalPtr<m_member_type>::get_ptr(value)); \
+ valid = true; \
+ } else { \
+ valid = false; \
+ } \
+ } \
+ static inline void validated_set(Variant *base, const Variant *value) { \
+ VariantGetInternalPtr<m_base_type>::get_ptr(base)->m_setter(m_index, *VariantGetInternalPtr<m_member_type>::get_ptr(value)); \
+ } \
+ static void ptr_set(void *base, const void *member) { \
+ m_base_type b = PtrToArg<m_base_type>::convert(base); \
+ b.m_setter(m_index, PtrToArg<m_member_type>::convert(member)); \
+ PtrToArg<m_base_type>::encode(b, base); \
+ } \
+ static Variant::Type get_type() { return GetTypeInfo<m_member_type>::VARIANT_TYPE; } \
+ };
+
+SETGET_NUMBER_STRUCT(Vector2, double, x)
+SETGET_NUMBER_STRUCT(Vector2, double, y)
+
+SETGET_NUMBER_STRUCT(Vector2i, int64_t, x)
+SETGET_NUMBER_STRUCT(Vector2i, int64_t, y)
+
+SETGET_NUMBER_STRUCT(Vector3, double, x)
+SETGET_NUMBER_STRUCT(Vector3, double, y)
+SETGET_NUMBER_STRUCT(Vector3, double, z)
+
+SETGET_NUMBER_STRUCT(Vector3i, int64_t, x)
+SETGET_NUMBER_STRUCT(Vector3i, int64_t, y)
+SETGET_NUMBER_STRUCT(Vector3i, int64_t, z)
+
+SETGET_STRUCT(Rect2, Vector2, position)
+SETGET_STRUCT(Rect2, Vector2, size)
+SETGET_STRUCT_FUNC(Rect2, Vector2, end, set_end, get_end)
+
+SETGET_STRUCT(Rect2i, Vector2i, position)
+SETGET_STRUCT(Rect2i, Vector2i, size)
+SETGET_STRUCT_FUNC(Rect2i, Vector2i, end, set_end, get_end)
+
+SETGET_STRUCT(AABB, Vector3, position)
+SETGET_STRUCT(AABB, Vector3, size)
+SETGET_STRUCT_FUNC(AABB, Vector3, end, set_end, get_end)
+
+SETGET_STRUCT_CUSTOM(Transform2D, Vector2, x, elements[0])
+SETGET_STRUCT_CUSTOM(Transform2D, Vector2, y, elements[1])
+SETGET_STRUCT_CUSTOM(Transform2D, Vector2, origin, elements[2])
+
+SETGET_NUMBER_STRUCT_CUSTOM(Plane, double, x, normal.x)
+SETGET_NUMBER_STRUCT_CUSTOM(Plane, double, y, normal.y)
+SETGET_NUMBER_STRUCT_CUSTOM(Plane, double, z, normal.z)
+SETGET_STRUCT(Plane, Vector3, normal)
+SETGET_NUMBER_STRUCT(Plane, double, d)
+
+SETGET_NUMBER_STRUCT(Quaternion, double, x)
+SETGET_NUMBER_STRUCT(Quaternion, double, y)
+SETGET_NUMBER_STRUCT(Quaternion, double, z)
+SETGET_NUMBER_STRUCT(Quaternion, double, w)
+
+SETGET_STRUCT_FUNC_INDEX(Basis, Vector3, x, set_axis, get_axis, 0)
+SETGET_STRUCT_FUNC_INDEX(Basis, Vector3, y, set_axis, get_axis, 1)
+SETGET_STRUCT_FUNC_INDEX(Basis, Vector3, z, set_axis, get_axis, 2)
+
+SETGET_STRUCT(Transform3D, Basis, basis)
+SETGET_STRUCT(Transform3D, Vector3, origin)
+
+SETGET_NUMBER_STRUCT(Color, double, r)
+SETGET_NUMBER_STRUCT(Color, double, g)
+SETGET_NUMBER_STRUCT(Color, double, b)
+SETGET_NUMBER_STRUCT(Color, double, a)
+
+SETGET_NUMBER_STRUCT_FUNC(Color, int64_t, r8, set_r8, get_r8)
+SETGET_NUMBER_STRUCT_FUNC(Color, int64_t, g8, set_g8, get_g8)
+SETGET_NUMBER_STRUCT_FUNC(Color, int64_t, b8, set_b8, get_b8)
+SETGET_NUMBER_STRUCT_FUNC(Color, int64_t, a8, set_a8, get_a8)
+
+SETGET_NUMBER_STRUCT_FUNC(Color, double, h, set_h, get_h)
+SETGET_NUMBER_STRUCT_FUNC(Color, double, s, set_s, get_s)
+SETGET_NUMBER_STRUCT_FUNC(Color, double, v, set_v, get_v)
+
+#endif // VARIANT_SETGET_H
diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index 553f2b23a2..232054d0ca 100644
--- a/core/variant/variant_utility.cpp
+++ b/core/variant/variant_utility.cpp
@@ -32,9 +32,11 @@
#include "core/core_string_names.h"
#include "core/io/marshalls.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
#include "core/os/os.h"
#include "core/templates/oa_hash_map.h"
+#include "core/templates/rid.h"
+#include "core/templates/rid_owner.h"
#include "core/variant/binder_common.h"
#include "core/variant/variant_parser.h"
@@ -249,10 +251,6 @@ struct VariantUtilityFunctions {
return Math::move_toward(from, to, delta);
}
- static inline double dectime(double value, double amount, double step) {
- return Math::dectime(value, amount, step);
- }
-
static inline double deg2rad(double angle_deg) {
return Math::deg2rad(angle_deg);
}
@@ -269,14 +267,6 @@ struct VariantUtilityFunctions {
return Math::db2linear(db);
}
- static inline Vector2 polar2cartesian(double r, double th) {
- return Vector2(r * Math::cos(th), r * Math::sin(th));
- }
-
- static inline Vector2 cartesian2polar(double x, double y) {
- return Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x));
- }
-
static inline int64_t wrapi(int64_t value, int64_t min, int64_t max) {
return Math::wrapi(value, min, max);
}
@@ -488,6 +478,14 @@ struct VariantUtilityFunctions {
return str;
}
+ static inline String error_string(Error error) {
+ if (error < 0 || error >= ERR_MAX) {
+ return String("(invalid error code)");
+ }
+
+ return String(error_names[error]);
+ }
+
static inline void print(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;
@@ -724,6 +722,13 @@ struct VariantUtilityFunctions {
}
return p_instance.get_validated_object() != nullptr;
}
+
+ static inline uint64_t rid_allocate_id() {
+ return RID_AllocBase::_gen_id();
+ }
+ static inline RID rid_from_int64(uint64_t p_base) {
+ return RID::from_uint64(p_base);
+ }
};
#ifdef DEBUG_METHODS_ENABLED
@@ -1195,16 +1200,12 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(smoothstep, sarray("from", "to", "x"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(move_toward, sarray("from", "to", "delta"), Variant::UTILITY_FUNC_TYPE_MATH);
- FUNCBINDR(dectime, sarray("value", "amount", "step"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(deg2rad, sarray("deg"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(rad2deg, sarray("rad"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(linear2db, sarray("lin"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(db2linear, sarray("db"), Variant::UTILITY_FUNC_TYPE_MATH);
- FUNCBINDR(polar2cartesian, sarray("r", "th"), Variant::UTILITY_FUNC_TYPE_MATH);
- FUNCBINDR(cartesian2polar, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH);
-
FUNCBINDR(wrapi, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(wrapf, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH);
@@ -1239,6 +1240,7 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDVR(weakref, sarray("obj"), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDR(_typeof, sarray("variable"), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDVARARGS(str, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);
+ FUNCBINDR(error_string, sarray("error"), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDVARARGV(print, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDVARARGV(printerr, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDVARARGV(printt, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);
@@ -1261,6 +1263,9 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(instance_from_id, sarray("instance_id"), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDR(is_instance_id_valid, sarray("id"), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDR(is_instance_valid, sarray("instance"), Variant::UTILITY_FUNC_TYPE_GENERAL);
+
+ FUNCBINDR(rid_allocate_id, Vector<String>(), Variant::UTILITY_FUNC_TYPE_GENERAL);
+ FUNCBINDR(rid_from_int64, sarray("base"), Variant::UTILITY_FUNC_TYPE_GENERAL);
}
void Variant::_unregister_variant_utility_functions() {
@@ -1348,8 +1353,8 @@ String Variant::get_utility_function_argument_name(const StringName &p_name, int
if (!bfi) {
return String();
}
- ERR_FAIL_COND_V(bfi->is_vararg, String());
ERR_FAIL_INDEX_V(p_arg, bfi->argnames.size(), String());
+ ERR_FAIL_COND_V(bfi->is_vararg, String());
return bfi->argnames[p_arg];
}
@@ -1379,9 +1384,26 @@ bool Variant::is_utility_function_vararg(const StringName &p_name) {
return bfi->is_vararg;
}
+uint32_t Variant::get_utility_function_hash(const StringName &p_name) {
+ const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name);
+ ERR_FAIL_COND_V(!bfi, 0);
+
+ uint32_t hash = hash_djb2_one_32(bfi->is_vararg);
+ hash = hash_djb2_one_32(bfi->returns_value, hash);
+ if (bfi->returns_value) {
+ hash = hash_djb2_one_32(bfi->return_type, hash);
+ }
+ hash = hash_djb2_one_32(bfi->argcount, hash);
+ for (int i = 0; i < bfi->argcount; i++) {
+ hash = hash_djb2_one_32(bfi->get_arg_type(i), hash);
+ }
+
+ return hash;
+}
+
void Variant::get_utility_function_list(List<StringName> *r_functions) {
- for (List<StringName>::Element *E = utility_function_name_table.front(); E; E = E->next()) {
- r_functions->push_back(E->get());
+ for (const StringName &E : utility_function_name_table) {
+ r_functions->push_back(E);
}
}