summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/SCsub1
-rw-r--r--core/config/engine.cpp42
-rw-r--r--core/config/engine.h35
-rw-r--r--core/config/project_settings.cpp57
-rw-r--r--core/config/project_settings.h9
-rw-r--r--core/core_bind.cpp1647
-rw-r--r--core/core_bind.h221
-rw-r--r--core/core_constants.cpp63
-rw-r--r--core/debugger/debugger_marshalls.h16
-rw-r--r--core/debugger/engine_debugger.cpp12
-rw-r--r--core/debugger/engine_debugger.h12
-rw-r--r--core/debugger/local_debugger.cpp22
-rw-r--r--core/debugger/remote_debugger.cpp29
-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/error/error_macros.h183
-rw-r--r--core/extension/extension_api_dump.cpp195
-rw-r--r--core/extension/gdnative_interface.cpp382
-rw-r--r--core/extension/gdnative_interface.h74
-rw-r--r--core/extension/native_extension.cpp55
-rw-r--r--core/extension/native_extension.h11
-rw-r--r--core/extension/native_extension_manager.cpp31
-rw-r--r--core/extension/native_extension_manager.h3
-rw-r--r--core/input/input.cpp91
-rw-r--r--core/input/input.h11
-rw-r--r--core/input/input_event.cpp83
-rw-r--r--core/input/input_event.h36
-rw-r--r--core/input/input_map.cpp133
-rw-r--r--core/input/input_map.h6
-rw-r--r--core/input/shortcut.cpp132
-rw-r--r--core/input/shortcut.h (renamed from core/io/multiplayer_peer.h)59
-rw-r--r--core/io/config_file.cpp4
-rw-r--r--core/io/dir_access.cpp4
-rw-r--r--core/io/file_access.cpp37
-rw-r--r--core/io/file_access_pack.cpp8
-rw-r--r--core/io/http_client_tcp.cpp26
-rw-r--r--core/io/http_client_tcp.h1
-rw-r--r--core/io/image.cpp8
-rw-r--r--core/io/ip.cpp71
-rw-r--r--core/io/marshalls.cpp7
-rw-r--r--core/io/multiplayer_api.cpp1110
-rw-r--r--core/io/multiplayer_peer.cpp91
-rw-r--r--core/io/packet_peer.cpp46
-rw-r--r--core/io/packet_peer.h23
-rw-r--r--core/io/resource.cpp15
-rw-r--r--core/io/resource.h2
-rw-r--r--core/io/resource_format_binary.cpp19
-rw-r--r--core/io/resource_format_binary.h2
-rw-r--r--core/io/resource_importer.cpp2
-rw-r--r--core/io/resource_loader.cpp90
-rw-r--r--core/io/resource_loader.h12
-rw-r--r--core/io/resource_saver.cpp35
-rw-r--r--core/io/resource_saver.h6
-rw-r--r--core/io/resource_uid.cpp20
-rw-r--r--core/io/resource_uid.h2
-rw-r--r--core/io/stream_peer.cpp57
-rw-r--r--core/io/stream_peer.h25
-rw-r--r--core/math/a_star.cpp106
-rw-r--r--core/math/a_star.h8
-rw-r--r--core/math/aabb.cpp4
-rw-r--r--core/math/aabb.h4
-rw-r--r--core/math/basis.cpp51
-rw-r--r--core/math/basis.h10
-rw-r--r--core/math/bvh_cull.inc2
-rw-r--r--core/math/bvh_debug.inc27
-rw-r--r--core/math/bvh_split.inc16
-rw-r--r--core/math/camera_matrix.cpp4
-rw-r--r--core/math/convex_hull.cpp28
-rw-r--r--core/math/convex_hull.h2
-rw-r--r--core/math/delaunay_2d.h2
-rw-r--r--core/math/face3.cpp4
-rw-r--r--core/math/geometry_2d.h23
-rw-r--r--core/math/math_defs.h20
-rw-r--r--core/math/quick_hull.cpp24
-rw-r--r--core/math/rect2.h6
-rw-r--r--core/math/static_raycaster.cpp40
-rw-r--r--core/math/static_raycaster.h111
-rw-r--r--core/math/transform_2d.cpp24
-rw-r--r--core/math/transform_2d.h27
-rw-r--r--core/math/transform_3d.cpp32
-rw-r--r--core/math/transform_3d.h83
-rw-r--r--core/math/triangle_mesh.cpp8
-rw-r--r--core/math/triangle_mesh.h6
-rw-r--r--core/math/triangulate.h2
-rw-r--r--core/math/vector2.cpp10
-rw-r--r--core/math/vector2.h31
-rw-r--r--core/math/vector3.cpp20
-rw-r--r--core/math/vector3.h61
-rw-r--r--core/math/vector3i.cpp4
-rw-r--r--core/math/vector3i.h50
-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)123
-rw-r--r--core/multiplayer/multiplayer_peer.cpp271
-rw-r--r--core/multiplayer/multiplayer_peer.h144
-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/class_db.cpp84
-rw-r--r--core/object/class_db.h61
-rw-r--r--core/object/make_virtuals.py19
-rw-r--r--core/object/message_queue.cpp12
-rw-r--r--core/object/method_bind.h1
-rw-r--r--core/object/object.cpp36
-rw-r--r--core/object/object.h45
-rw-r--r--core/object/ref_counted.h4
-rw-r--r--core/object/script_language.cpp4
-rw-r--r--core/object/script_language.h9
-rw-r--r--core/object/undo_redo.cpp4
-rw-r--r--core/object/undo_redo.h4
-rw-r--r--core/os/keyboard.h47
-rw-r--r--core/os/main_loop.cpp29
-rw-r--r--core/os/main_loop.h6
-rw-r--r--core/os/memory.h14
-rw-r--r--core/os/os.cpp43
-rw-r--r--core/os/os.h12
-rw-r--r--core/os/thread.cpp3
-rw-r--r--core/os/thread.h7
-rw-r--r--core/register_core_types.cpp108
-rw-r--r--core/string/optimized_translation.cpp10
-rw-r--r--core/string/translation.cpp295
-rw-r--r--core/string/translation.h32
-rw-r--r--core/string/ustring.cpp283
-rw-r--r--core/string/ustring.h26
-rw-r--r--core/templates/cowdata.h18
-rw-r--r--core/templates/local_vector.h2
-rw-r--r--core/templates/map.h2
-rw-r--r--core/templates/paged_allocator.h5
-rw-r--r--core/templates/pooled_list.h14
-rw-r--r--core/templates/rid_owner.h26
-rw-r--r--core/templates/safe_list.h375
-rw-r--r--core/templates/search_array.h67
-rw-r--r--core/templates/set.h2
-rw-r--r--core/templates/vector.h10
-rw-r--r--core/variant/array.cpp73
-rw-r--r--core/variant/array.h3
-rw-r--r--core/variant/binder_common.h4
-rw-r--r--core/variant/callable.cpp4
-rw-r--r--core/variant/callable.h2
-rw-r--r--core/variant/callable_bind.cpp3
-rw-r--r--core/variant/method_ptrcall.h7
-rw-r--r--core/variant/native_ptr.h131
-rw-r--r--core/variant/type_info.h29
-rw-r--r--core/variant/typed_array.h10
-rw-r--r--core/variant/variant.cpp27
-rw-r--r--core/variant/variant.h12
-rw-r--r--core/variant/variant_call.cpp36
-rw-r--r--core/variant/variant_construct.cpp1
-rw-r--r--core/variant/variant_destruct.cpp78
-rw-r--r--core/variant/variant_destruct.h76
-rw-r--r--core/variant/variant_internal.h44
-rw-r--r--core/variant/variant_op.cpp144
-rw-r--r--core/variant/variant_op.h96
-rw-r--r--core/variant/variant_parser.cpp76
-rw-r--r--core/variant/variant_setget.cpp86
-rw-r--r--core/variant/variant_utility.cpp75
160 files changed, 8365 insertions, 3554 deletions
diff --git a/core/SCsub b/core/SCsub
index d9167b8f83..14dfa3487f 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -183,6 +183,7 @@ SConscript("os/SCsub")
SConscript("math/SCsub")
SConscript("crypto/SCsub")
SConscript("io/SCsub")
+SConscript("multiplayer/SCsub")
SConscript("debugger/SCsub")
SConscript("input/SCsub")
SConscript("variant/SCsub")
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 0b5b5627af..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;
}
@@ -199,17 +199,41 @@ bool Engine::is_printing_error_messages() const {
}
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);
}
diff --git a/core/config/engine.h b/core/config/engine.h
index 970cfb03e8..ae33acede2 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -42,6 +42,7 @@ public:
StringName name;
Object *ptr;
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());
};
@@ -51,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;
@@ -78,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();
@@ -95,11 +96,11 @@ 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;
@@ -109,8 +110,10 @@ public:
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 c5e6c6d685..57833fe42f 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -48,18 +48,25 @@ ProjectSettings *ProjectSettings::get_singleton() {
return singleton;
}
+String ProjectSettings::get_project_data_dir_name() const {
+ return ".godot";
+}
+
+String ProjectSettings::get_project_data_path() const {
+ String project_data_dir_name = get_project_data_dir_name();
+ return "res://" + project_data_dir_name;
+}
+
String ProjectSettings::get_resource_path() const {
return resource_path;
}
-const String ProjectSettings::IMPORTED_FILES_PATH("res://.godot/imported");
+String ProjectSettings::get_imported_files_path() const {
+ return get_project_data_path().plus_file("imported");
+}
String ProjectSettings::localize_path(const String &p_path) const {
- if (resource_path == "") {
- return p_path; //not initialized yet
- }
-
- if (p_path.begins_with("res://") || p_path.begins_with("user://") ||
+ if (resource_path.is_empty() || p_path.begins_with("res://") || p_path.begins_with("user://") ||
(p_path.is_absolute_path() && !p_path.begins_with(resource_path))) {
return p_path.simplify_path();
}
@@ -256,15 +263,15 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const {
Set<_VCSort> vclist;
- for (Map<StringName, VariantContainer>::Element *E = props.front(); E; E = E->next()) {
- const VariantContainer *v = &E->get();
+ for (const KeyValue<StringName, VariantContainer> &E : props) {
+ const VariantContainer *v = &E.value;
if (v->hide_from_editor) {
continue;
}
_VCSort vc;
- vc.name = E->key();
+ vc.name = E.key;
vc.order = v->order;
vc.type = v->variant.get_type();
if (vc.name.begins_with("input/") || vc.name.begins_with("import/") || vc.name.begins_with("export/") || vc.name.begins_with("/remap") || vc.name.begins_with("/locale") || vc.name.begins_with("/autoload")) {
@@ -322,14 +329,14 @@ bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_f
void ProjectSettings::_convert_to_last_version(int p_from_version) {
if (p_from_version <= 3) {
// Converts the actions from array to dictionary (array of events to dictionary with deadzone + events)
- for (Map<StringName, ProjectSettings::VariantContainer>::Element *E = props.front(); E; E = E->next()) {
- Variant value = E->get().variant;
- if (String(E->key()).begins_with("input/") && value.get_type() == Variant::ARRAY) {
+ for (KeyValue<StringName, ProjectSettings::VariantContainer> &E : props) {
+ Variant value = E.value.variant;
+ if (String(E.key).begins_with("input/") && value.get_type() == Variant::ARRAY) {
Array array = value;
Dictionary action;
action["deadzone"] = Variant(0.5f);
action["events"] = array;
- E->get().variant = action;
+ E.value.variant = action;
}
}
}
@@ -699,8 +706,8 @@ 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()) {
- count += E->get().size();
+ for (const KeyValue<String, List<String>> &E : props) {
+ count += E.value.size();
}
if (p_custom_features != String()) {
@@ -792,7 +799,7 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const Map<Strin
}
file->store_string("\n");
- for (Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) {
+ for (const Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) {
if (E != props.front()) {
file->store_string("\n");
}
@@ -835,19 +842,19 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
Set<_VCSort> vclist;
if (p_merge_with_current) {
- for (Map<StringName, VariantContainer>::Element *G = props.front(); G; G = G->next()) {
- const VariantContainer *v = &G->get();
+ for (const KeyValue<StringName, VariantContainer> &G : props) {
+ const VariantContainer *v = &G.value;
if (v->hide_from_editor) {
continue;
}
- if (p_custom.has(G->key())) {
+ if (p_custom.has(G.key)) {
continue;
}
_VCSort vc;
- vc.name = G->key(); //*k;
+ vc.name = G.key; //*k;
vc.order = v->order;
vc.type = v->variant.get_type();
vc.flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE;
@@ -859,14 +866,14 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
}
}
- for (const Map<String, Variant>::Element *E = p_custom.front(); E; E = E->next()) {
+ for (const KeyValue<String, Variant> &E : p_custom) {
// Lookup global prop to store in the same order
- Map<StringName, VariantContainer>::Element *global_prop = props.find(E->key());
+ Map<StringName, VariantContainer>::Element *global_prop = props.find(E.key);
_VCSort vc;
- vc.name = E->key();
+ vc.name = E.key;
vc.order = global_prop ? global_prop->get().order : 0xFFFFFFF;
- vc.type = E->get().get_type();
+ vc.type = E.value.get_type();
vc.flags = PROPERTY_USAGE_STORAGE;
vclist.insert(vc);
}
@@ -1006,7 +1013,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;
}
diff --git a/core/config/project_settings.h b/core/config/project_settings.h
index ed8fb19fa0..b642051402 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 {
@@ -41,7 +42,6 @@ class ProjectSettings : public Object {
public:
typedef Map<String, Variant> CustomMap;
- static const String IMPORTED_FILES_PATH;
enum {
//properties that are not for built in values begin from this value, so builtin ones are displayed first
@@ -91,7 +91,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;
@@ -140,7 +140,10 @@ public:
bool property_can_revert(const String &p_name);
Variant property_get_revert(const String &p_name);
+ String get_project_data_dir_name() const;
+ String get_project_data_path() const;
String get_resource_path() const;
+ String get_imported_files_path() const;
static ProjectSettings *get_singleton();
@@ -168,7 +171,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 9e96d4b079..e11d9ab9c1 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -41,39 +41,41 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
-////// _ResourceLoader //////
+namespace core_bind {
-_ResourceLoader *_ResourceLoader::singleton = nullptr;
+////// ResourceLoader //////
-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 *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);
}
-_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 (const String &E : exts) {
ret.push_back(E);
@@ -82,13 +84,13 @@ Vector<String> _ResourceLoader::get_recognized_extensions_for_type(const String
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 (const String &E : deps) {
@@ -98,26 +100,31 @@ PackedStringArray _ResourceLoader::get_dependencies(const String &p_path) {
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);
@@ -129,17 +136,17 @@ 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 (const String &E : exts) {
ret.push_back(E);
@@ -147,11 +154,11 @@ Vector<String> _ResourceSaver::get_recognized_extensions(const RES &p_resource)
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);
@@ -162,65 +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();
}
-void _OS::alert(const String &p_alert, const String &p_title) {
- OS::get_singleton()->alert(p_alert, p_title);
+void OS::alert(const String &p_alert, const String &p_title) {
+ ::OS::get_singleton()->alert(p_alert, p_title);
}
-String _OS::get_executable_path() const {
- return OS::get_singleton()->get_executable_path();
+String OS::get_executable_path() const {
+ return ::OS::get_singleton()->get_executable_path();
}
-Error _OS::shell_open(String p_uri) {
+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;
@@ -228,45 +235,45 @@ 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 (const String &E : cmdline) {
cmdlinev.push_back(E);
@@ -275,95 +282,99 @@ Vector<String> _OS::get_cmdline_args() {
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_locale_language() const {
+ return ::OS::get_singleton()->get_locale_language();
}
-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);
+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();
+uint64_t OS::get_static_memory_usage() const {
+ return ::OS::get_singleton()->get_static_memory_usage();
}
-uint64_t _OS::get_static_memory_peak_usage() const {
- return OS::get_singleton()->get_static_memory_peak_usage();
+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);
+ ::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 (Ref<Resource> &res : rsrc) {
- if (!res->is_class("ImageTexture")) {
+ if (!res->is_class("Texture")) {
continue;
}
Size2 size = res->call("get_size");
int fmt = res->call("get_format");
- _OSCoreBindImg img;
+ OSCoreBindImg img;
img.size = size;
img.fmt = fmt;
img.path = res->get_path();
@@ -376,14 +387,30 @@ void _OS::print_all_textures_by_size() {
imgs.sort();
- for (_OSCoreBindImg &E : imgs) {
- total -= E.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);
@@ -404,45 +431,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)));
+ }
}
-}
-void _OS::print_all_resources(const String &p_to_file) {
- OS::get_singleton()->print_all_resources(p_to_file);
+ for (const KeyValue<String, int> &E : type_count) {
+ print_line(vformat("%s count: %d", E.key, E.value));
+ }
}
-void _OS::print_resources_in_use(bool p_short) {
- OS::get_singleton()->print_resources_in_use(p_short);
+void OS::print_all_resources(const String &p_to_file) {
+ ::OS::get_singleton()->print_all_resources(p_to_file);
}
-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_external_data_dir() const {
- return OS::get_singleton()->get_external_data_dir();
+String OS::get_user_data_dir() const {
+ return ::OS::get_singleton()->get_user_data_dir();
}
-String _OS::get_config_dir() const {
+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();
+ return ::OS::get_singleton()->get_config_path();
}
-String _OS::get_data_dir() const {
+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();
+ return ::OS::get_singleton()->get_data_path();
}
-String _OS::get_cache_dir() 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();
+ return ::OS::get_singleton()->get_cache_path();
}
-bool _OS::is_debug_build() const {
+bool OS::is_debug_build() const {
#ifdef DEBUG_ENABLED
return true;
#else
@@ -450,114 +485,114 @@ 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("alert", "text", "title"), &_OS::alert, DEFVAL("Alert!"));
+ ClassDB::bind_method(D_METHOD("alert", "text", "title"), &OS::alert, DEFVAL("Alert!"));
- 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("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("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_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_processor_count"), &_OS::get_processor_count);
+ ClassDB::bind_method(D_METHOD("get_processor_count"), &OS::get_processor_count);
- 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_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_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_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_name"), &_OS::get_name);
- ClassDB::bind_method(D_METHOD("get_cmdline_args"), &_OS::get_cmdline_args);
+ 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_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_locale_language"), &OS::get_locale_language);
+ 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_external_data_dir"), &_OS::get_external_data_dir);
- ClassDB::bind_method(D_METHOD("get_system_dir", "dir"), &_OS::get_system_dir);
- 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("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");
@@ -602,43 +637,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);
@@ -646,42 +681,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;
@@ -691,8 +726,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;
@@ -702,8 +737,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;
@@ -713,8 +748,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;
@@ -724,8 +759,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;
@@ -735,8 +770,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;
@@ -746,8 +781,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;
@@ -757,8 +792,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;
@@ -768,7 +803,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;
@@ -779,7 +814,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;
@@ -793,37 +828,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);
@@ -841,29 +876,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);
@@ -871,38 +906,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;
}
@@ -912,10 +947,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;
}
@@ -925,10 +960,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;
}
@@ -938,33 +973,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;
@@ -981,7 +1016,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;
@@ -999,7 +1034,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);
@@ -1015,7 +1050,7 @@ 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);
@@ -1025,94 +1060,94 @@ Error _File::open(const String &p_path, ModeFlags p_mode_flags) {
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.");
@@ -1134,7 +1169,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;
@@ -1153,20 +1188,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);
}
@@ -1176,95 +1211,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_big_endian(bool p_big_endian) {
+void File::set_big_endian(bool p_big_endian) {
big_endian = p_big_endian;
if (f) {
f->set_big_endian(p_big_endian);
}
}
-bool _File::is_big_endian() {
+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();
@@ -1277,11 +1312,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);
@@ -1298,7 +1333,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);
@@ -1313,62 +1348,62 @@ 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("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);
+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");
@@ -1383,15 +1418,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);
@@ -1407,11 +1442,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;
@@ -1420,7 +1455,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();
@@ -1430,32 +1465,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);
@@ -1467,14 +1502,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);
@@ -1483,9 +1518,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);
@@ -1494,18 +1529,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);
@@ -1515,33 +1550,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);
@@ -1551,47 +1588,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.");
@@ -1609,7 +1646,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();
@@ -1627,13 +1664,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();
@@ -1650,14 +1687,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();
@@ -1674,150 +1711,176 @@ 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 = (int)(arg[0]->get_type() != Variant::NIL);
- Thread::set_name(t->target_method);
+ Object *target_instance = t->target_callable.get_object();
+ if (!target_instance) {
+ t->running.clear();
+ ERR_FAIL_MSG(vformat("Could not call function '%s' on previously freed instance to start thread %s.", t->target_callable.get_method(), t->get_id()));
+ }
- t->ret = t->target_instance->call(t->target_method, arg, argc, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- String reason;
- switch (ce.error) {
- case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
- reason = "Invalid Argument #" + itos(ce.argument);
- } break;
- case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: {
- reason = "Too Many Arguments";
- } break;
- case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: {
- reason = "Too Few Arguments";
- } break;
- case Callable::CallError::CALL_ERROR_INVALID_METHOD: {
- reason = "Method Not Found";
- } break;
- default: {
+ 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 = target_instance->get_script();
+ if (script.is_valid()) {
+ MethodInfo mi = script->get_method_info(t->target_callable.get_method());
+ target_param_count = mi.arguments.size();
+ target_default_arg_count = mi.default_arguments.size();
+ } else {
+ MethodBind *method = ClassDB::get_method(target_instance->get_class_name(), t->target_callable.get_method());
+ if (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_callable.get_method());
- ERR_FAIL_MSG("Could not call function '" + t->target_method.operator String() + "' to start thread " + t->get_id() + ": " + reason + ".");
+ t->target_callable.call(arg, argc, t->ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ t->running.clear();
+ ERR_FAIL_MSG("Could not call function '" + t->target_callable.get_method().operator String() + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, arg, argc, ce) + ".");
}
+
+ t->running.clear();
}
-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);
+Error Thread::start(const Callable &p_callable, const Variant &p_userdata, Priority p_priority) {
+ ERR_FAIL_COND_V_MSG(is_started(), ERR_ALREADY_IN_USE, "Thread already started.");
+ ERR_FAIL_COND_V(p_callable.is_null(), ERR_INVALID_PARAMETER);
ERR_FAIL_INDEX_V(p_priority, PRIORITY_MAX, ERR_INVALID_PARAMETER);
ret = Variant();
- target_method = p_method;
- target_instance = p_instance;
+ target_callable = p_callable;
userdata = p_userdata;
- active.set();
+ running.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 {
- return active.is_set();
+bool Thread::is_started() const {
+ return thread.is_started();
}
-Variant _Thread::wait_to_finish() {
- ERR_FAIL_COND_V_MSG(!active.is_set(), Variant(), "Thread must be active to wait for its completion.");
+bool Thread::is_alive() const {
+ return running.is_set();
+}
+
+Variant Thread::wait_to_finish() {
+ ERR_FAIL_COND_V_MSG(!is_started(), Variant(), "Thread must have been started to wait for its completion.");
thread.wait_to_finish();
Variant r = ret;
- active.clear();
- target_method = StringName();
- target_instance = nullptr;
+ target_callable = Callable();
userdata = Variant();
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", "callable", "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_started"), &Thread::is_started);
+ ClassDB::bind_method(D_METHOD("is_alive"), &Thread::is_alive);
+ 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 {
+
+////// ClassDB //////
-PackedStringArray _ClassDB::get_class_list() const {
+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());
@@ -1829,9 +1892,9 @@ PackedStringArray _ClassDB::get_class_list() const {
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());
@@ -1843,24 +1906,24 @@ PackedStringArray _ClassDB::get_inheriters_from_class(const StringName &p_class)
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_instantiate(const StringName &p_class) const {
- return ClassDB::can_instantiate(p_class);
+bool ClassDB::can_instantiate(const StringName &p_class) const {
+ return ::ClassDB::can_instantiate(p_class);
}
-Variant _ClassDB::instantiate(const StringName &p_class) const {
- Object *obj = ClassDB::instantiate(p_class);
+Variant ClassDB::instantiate(const StringName &p_class) const {
+ Object *obj = ::ClassDB::instantiate(p_class);
if (!obj) {
return Variant();
}
@@ -1873,22 +1936,22 @@ Variant _ClassDB::instantiate(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 (const MethodInfo &E : signals) {
@@ -1898,9 +1961,9 @@ Array _ClassDB::get_signal_list(StringName p_class, bool p_no_inheritance) const
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 (const PropertyInfo &E : plist) {
ret.push_back(E.operator Dictionary());
@@ -1909,16 +1972,16 @@ Array _ClassDB::get_property_list(StringName p_class, bool p_no_inheritance) con
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;
@@ -1926,13 +1989,13 @@ 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 (const MethodInfo &E : methods) {
@@ -1948,9 +2011,9 @@ 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());
@@ -1962,299 +2025,353 @@ PackedStringArray _ClassDB::get_integer_constant_list(const StringName &p_class,
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::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);
+}
+
+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);
+
+ PackedStringArray ret;
+ ret.resize(enums.size());
+ int idx = 0;
+ for (const StringName &E : enums) {
+ ret.set(idx++, E);
+ }
+
+ return ret;
+}
+
+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);
+
+ PackedStringArray ret;
+ ret.resize(constants.size());
+ int idx = 0;
+ for (const StringName &E : constants) {
+ ret.set(idx++, E);
+ }
+
+ return ret;
+}
+
+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);
}
-bool _ClassDB::is_class_enabled(StringName p_class) const {
- return ClassDB::is_class_enabled(p_class);
+bool ClassDB::is_class_enabled(StringName p_class) const {
+ return ::ClassDB::is_class_enabled(p_class);
}
-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 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);
+
+ ::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));
+
+ ::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);
+
+ ::ClassDB::bind_method(D_METHOD("class_has_method", "class", "method", "no_inheritance"), &ClassDB::has_method, DEFVAL(false));
+
+ ::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::get_method_list, DEFVAL(false));
- 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));
+ ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &ClassDB::get_integer_constant_list, DEFVAL(false));
- 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);
+ ::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);
- ClassDB::bind_method(D_METHOD("class_has_method", "class", "method", "no_inheritance"), &_ClassDB::has_method, DEFVAL(false));
+ ::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));
- ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &_ClassDB::get_method_list, DEFVAL(false));
+ ::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);
+}
- ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &_ClassDB::get_integer_constant_list, DEFVAL(false));
+} // namespace special
- 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);
+////// Engine //////
- 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);
+void Engine::set_physics_ticks_per_second(int p_ips) {
+ ::Engine::get_singleton()->set_physics_ticks_per_second(p_ips);
}
-////// _Engine //////
+int Engine::get_physics_ticks_per_second() const {
+ return ::Engine::get_singleton()->get_physics_ticks_per_second();
+}
-void _Engine::set_iterations_per_second(int p_ips) {
- Engine::get_singleton()->set_iterations_per_second(p_ips);
+void Engine::set_physics_jitter_fix(double p_threshold) {
+ ::Engine::get_singleton()->set_physics_jitter_fix(p_threshold);
}
-int _Engine::get_iterations_per_second() const {
- return Engine::get_singleton()->get_iterations_per_second();
+double Engine::get_physics_jitter_fix() const {
+ return ::Engine::get_singleton()->get_physics_jitter_fix();
}
-void _Engine::set_physics_jitter_fix(float p_threshold) {
- Engine::get_singleton()->set_physics_jitter_fix(p_threshold);
+double Engine::get_physics_interpolation_fraction() const {
+ return ::Engine::get_singleton()->get_physics_interpolation_fraction();
}
-float _Engine::get_physics_jitter_fix() const {
- return Engine::get_singleton()->get_physics_jitter_fix();
+void Engine::set_target_fps(int p_fps) {
+ ::Engine::get_singleton()->set_target_fps(p_fps);
}
-float _Engine::get_physics_interpolation_fraction() const {
- return Engine::get_singleton()->get_physics_interpolation_fraction();
+int Engine::get_target_fps() const {
+ return ::Engine::get_singleton()->get_target_fps();
}
-void _Engine::set_target_fps(int p_fps) {
- Engine::get_singleton()->set_target_fps(p_fps);
+double Engine::get_frames_per_second() const {
+ return ::Engine::get_singleton()->get_frames_per_second();
}
-int _Engine::get_target_fps() const {
- return Engine::get_singleton()->get_target_fps();
+uint64_t Engine::get_physics_frames() const {
+ return ::Engine::get_singleton()->get_physics_frames();
}
-float _Engine::get_frames_per_second() const {
- return Engine::get_singleton()->get_frames_per_second();
+uint64_t Engine::get_process_frames() const {
+ return ::Engine::get_singleton()->get_process_frames();
}
-uint64_t _Engine::get_physics_frames() const {
- return Engine::get_singleton()->get_physics_frames();
+void Engine::set_time_scale(double p_scale) {
+ ::Engine::get_singleton()->set_time_scale(p_scale);
}
-uint64_t _Engine::get_process_frames() const {
- return Engine::get_singleton()->get_process_frames();
+double Engine::get_time_scale() {
+ return ::Engine::get_singleton()->get_time_scale();
}
-void _Engine::set_time_scale(float p_scale) {
- Engine::get_singleton()->set_time_scale(p_scale);
+int Engine::get_frames_drawn() {
+ return ::Engine::get_singleton()->get_frames_drawn();
}
-float _Engine::get_time_scale() {
- return Engine::get_singleton()->get_time_scale();
+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();
}
-int _Engine::get_frames_drawn() {
- return Engine::get_singleton()->get_frames_drawn();
+Dictionary Engine::get_version_info() const {
+ return ::Engine::get_singleton()->get_version_info();
}
-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();
+Dictionary Engine::get_author_info() const {
+ return ::Engine::get_singleton()->get_author_info();
}
-Dictionary _Engine::get_version_info() const {
- return Engine::get_singleton()->get_version_info();
+Array Engine::get_copyright_info() const {
+ return ::Engine::get_singleton()->get_copyright_info();
}
-Dictionary _Engine::get_author_info() const {
- return Engine::get_singleton()->get_author_info();
+Dictionary Engine::get_donor_info() const {
+ return ::Engine::get_singleton()->get_donor_info();
}
-Array _Engine::get_copyright_info() const {
- return Engine::get_singleton()->get_copyright_info();
+Dictionary Engine::get_license_info() const {
+ return ::Engine::get_singleton()->get_license_info();
}
-Dictionary _Engine::get_donor_info() const {
- return Engine::get_singleton()->get_donor_info();
+String Engine::get_license_text() const {
+ return ::Engine::get_singleton()->get_license_text();
}
-Dictionary _Engine::get_license_info() const {
- return Engine::get_singleton()->get_license_info();
+bool Engine::is_in_physics_frame() const {
+ return ::Engine::get_singleton()->is_in_physics_frame();
}
-String _Engine::get_license_text() const {
- return Engine::get_singleton()->get_license_text();
+bool Engine::has_singleton(const StringName &p_name) const {
+ return ::Engine::get_singleton()->has_singleton(p_name);
}
-bool _Engine::is_in_physics_frame() const {
- return Engine::get_singleton()->is_in_physics_frame();
+Object *Engine::get_singleton_object(const StringName &p_name) const {
+ return ::Engine::get_singleton()->get_singleton_object(p_name);
}
-bool _Engine::has_singleton(const String &p_name) const {
- return Engine::get_singleton()->has_singleton(p_name);
+void Engine::register_singleton(const StringName &p_name, Object *p_object) {
+ ERR_FAIL_COND_MSG(has_singleton(p_name), "Singleton already registered: " + String(p_name));
+ ERR_FAIL_COND_MSG(!String(p_name).is_valid_identifier(), "Singleton name is not a valid identifier: " + 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);
}
-Object *_Engine::get_singleton_object(const String &p_name) const {
- return Engine::get_singleton()->get_singleton_object(p_name);
+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 _Engine::set_editor_hint(bool p_enabled) {
- Engine::get_singleton()->set_editor_hint(p_enabled);
+void Engine::set_editor_hint(bool p_enabled) {
+ ::Engine::get_singleton()->set_editor_hint(p_enabled);
}
-bool _Engine::is_editor_hint() const {
- return Engine::get_singleton()->is_editor_hint();
+bool Engine::is_editor_hint() const {
+ return ::Engine::get_singleton()->is_editor_hint();
}
-void _Engine::set_print_error_messages(bool p_enabled) {
- Engine::get_singleton()->set_print_error_messages(p_enabled);
+void Engine::set_print_error_messages(bool p_enabled) {
+ ::Engine::get_singleton()->set_print_error_messages(p_enabled);
}
-bool _Engine::is_printing_error_messages() const {
- return Engine::get_singleton()->is_printing_error_messages();
+bool Engine::is_printing_error_messages() const {
+ return ::Engine::get_singleton()->is_printing_error_messages();
}
-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);
+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);
+
+ 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("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_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_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("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("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("has_singleton", "name"), &_Engine::has_singleton);
- ClassDB::bind_method(D_METHOD("get_singleton", "name"), &_Engine::get_singleton_object);
+ 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("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("is_editor_hint"), &Engine::is_editor_hint);
- 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);
+ 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, "iterations_per_second"), "set_iterations_per_second", "get_iterations_per_second");
+ 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");
}
-_Engine *_Engine::singleton = nullptr;
-
-////// _EngineDebugger //////
-
-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);
+Engine *Engine::singleton = nullptr;
- 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()));
+////// EngineDebugger //////
- 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);
-}
-
-bool _EngineDebugger::is_active() {
- return EngineDebugger::is_active();
+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;
@@ -2267,7 +2384,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;
@@ -2280,7 +2397,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;
@@ -2293,7 +2410,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;
@@ -2309,15 +2426,35 @@ Error _EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Arr
return OK;
}
-_EngineDebugger::~_EngineDebugger() {
- for (Map<StringName, Callable>::Element *E = captures.front(); E; E = E->next()) {
- EngineDebugger::unregister_message_capture(E->key());
+EngineDebugger::~EngineDebugger() {
+ for (const KeyValue<StringName, Callable> &E : captures) {
+ ::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());
+ for (const KeyValue<StringName, ProfilerCallable> &E : profilers) {
+ ::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 1574c36d3c..4eab085dda 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -42,12 +42,16 @@
#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 {
@@ -63,7 +67,7 @@ public:
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 {
@@ -180,6 +178,7 @@ public:
Vector<String> get_cmdline_args();
String get_locale() const;
+ String get_locale_language() const;
String get_model_name() const;
@@ -228,10 +227,9 @@ 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_external_data_dir() const;
String get_config_dir() const;
String get_data_dir() const;
String get_cache_dir() const;
@@ -245,26 +243,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);
@@ -315,23 +308,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);
@@ -347,11 +336,11 @@ public:
Vector<Vector3> clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane);
- _Geometry3D() { singleton = this; }
+ Geometry3D() { singleton = this; }
};
-class _File : public RefCounted {
- GDCLASS(_File, RefCounted);
+class File : public RefCounted {
+ GDCLASS(File, RefCounted);
FileAccess *f = nullptr;
bool big_endian = false;
@@ -445,15 +434,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 RefCounted {
- GDCLASS(_Directory, RefCounted);
+class Directory : public RefCounted {
+ GDCLASS(Directory, RefCounted);
DirAccess *d;
bool dir_open = false;
@@ -490,24 +476,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);
@@ -518,13 +504,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 RefCounted {
- GDCLASS(_Mutex, RefCounted);
- Mutex mutex;
+class Mutex : public RefCounted {
+ GDCLASS(Mutex, RefCounted);
+ ::Mutex mutex;
static void _bind_methods();
@@ -534,9 +520,9 @@ public:
void unlock();
};
-class _Semaphore : public RefCounted {
- GDCLASS(_Semaphore, RefCounted);
- Semaphore semaphore;
+class Semaphore : public RefCounted {
+ GDCLASS(Semaphore, RefCounted);
+ ::Semaphore semaphore;
static void _bind_methods();
@@ -546,16 +532,15 @@ public:
void post();
};
-class _Thread : public RefCounted {
- GDCLASS(_Thread, RefCounted);
+class Thread : public RefCounted {
+ GDCLASS(Thread, RefCounted);
protected:
Variant ret;
Variant userdata;
- SafeFlag active;
- Object *target_instance = nullptr;
- StringName target_method;
- Thread thread;
+ SafeFlag running;
+ Callable target_callable;
+ ::Thread thread;
static void _bind_methods();
static void _start_func(void *ud);
@@ -567,16 +552,17 @@ public:
PRIORITY_MAX
};
- Error start(Object *p_instance, const StringName &p_method, const Variant &p_userdata = Variant(), Priority p_priority = PRIORITY_NORMAL);
+ Error start(const Callable &p_callable, const Variant &p_userdata = Variant(), Priority p_priority = PRIORITY_NORMAL);
String get_id() const;
- bool is_active() const;
+ bool is_started() const;
+ bool is_alive() const;
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();
@@ -607,39 +593,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;
@@ -652,8 +645,11 @@ 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;
@@ -661,14 +657,14 @@ public:
void set_print_error_messages(bool p_enabled);
bool is_printing_error_messages() const;
- _Engine() { 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;
@@ -689,10 +685,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();
@@ -711,11 +707,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 de15cfd14a..b2d5a8fdf1 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,9 +105,6 @@ static Vector<_CoreConstant> _global_constants;
#endif
-VARIANT_ENUM_CAST(Key);
-VARIANT_ENUM_CAST(KeyModifierMask);
-
void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(SIDE_LEFT);
BIND_CORE_ENUM_CONSTANT(SIDE_TOP);
@@ -133,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);
@@ -532,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);
@@ -549,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);
@@ -561,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_PEER", Multiplayer::RPC_MODE_ANY_PEER);
+ 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_UNRELIABLE_ORDERED", Multiplayer::TRANSFER_MODE_UNRELIABLE_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);
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..059025aa8f 100644
--- a/core/debugger/engine_debugger.cpp
+++ b/core/debugger/engine_debugger.cpp
@@ -117,14 +117,14 @@ 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);
physics_frame_time = p_physics_frame_time;
// Notify tick to running profilers
- for (Map<StringName, Profiler>::Element *E = profilers.front(); E; E = E->next()) {
- Profiler &p = E->get();
+ for (KeyValue<StringName, Profiler> &E : profilers) {
+ Profiler &p = E.value;
if (!p.active || !p.tick) {
continue;
}
@@ -179,9 +179,9 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, Ve
void EngineDebugger::deinitialize() {
if (singleton) {
// Stop all profilers
- for (Map<StringName, Profiler>::Element *E = profilers.front(); E; E = E->next()) {
- if (E->get().active) {
- singleton->profiler_enable(E->key(), false);
+ for (const KeyValue<StringName, Profiler> &E : profilers) {
+ if (E.value.active) {
+ singleton->profiler_enable(E.key, false);
}
}
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 24833711d5..f7e56351b0 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));
}
}
@@ -166,8 +166,8 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
} else if (line.begins_with("set")) {
if (line.get_slice_count(" ") == 1) {
- for (Map<String, String>::Element *E = options.front(); E; E = E->next()) {
- print_line("\t" + E->key() + "=" + E->value());
+ for (const KeyValue<String, String> &E : options) {
+ print_line("\t" + E.key + "=" + E.value);
}
} else {
@@ -249,8 +249,8 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
}
print_line("Breakpoint(s): " + itos(breakpoints.size()));
- for (Map<int, Set<StringName>>::Element *E = breakpoints.front(); E; E = E->next()) {
- print_line("\t" + String(E->value().front()->get()) + ":" + itos(E->key()));
+ for (const KeyValue<int, Set<StringName>> &E : breakpoints) {
+ print_line("\t" + String(E.value.front()->get()) + ":" + itos(E.key));
}
} else {
@@ -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 0add12ff3d..032c7d55c0 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;
@@ -179,8 +179,8 @@ public:
if (pt - last_profile_time > 100) {
last_profile_time = pt;
DebuggerMarshalls::NetworkProfilerFrame frame;
- for (Map<ObjectID, NodeInfo>::Element *E = multiplayer_node_data.front(); E; E = E->next()) {
- frame.infos.push_back(E->get());
+ for (const KeyValue<ObjectID, NodeInfo> &E : multiplayer_node_data) {
+ frame.infos.push_back(E.value);
}
multiplayer_node_data.clear();
EngineDebugger::get_singleton()->send_message("network:profile_frame", frame.serialize());
@@ -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;
}
@@ -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/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/error/error_macros.h b/core/error/error_macros.h
index f909a67d55..1bed8d366b 100644
--- a/core/error/error_macros.h
+++ b/core/error/error_macros.h
@@ -89,13 +89,6 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
#define GENERATE_TRAP() __builtin_trap()
#endif
-// Used to strip debug messages in release mode
-#ifdef DEBUG_ENABLED
-#define DEBUG_STR(m_msg) m_msg
-#else
-#define DEBUG_STR(m_msg) ""
-#endif
-
/**
* Error macros.
* WARNING: These macros work in the opposite way to assert().
@@ -135,11 +128,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
* If not, prints `m_msg` and the current function returns.
*/
-#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \
- if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
- _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \
- return; \
- } else \
+#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \
+ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \
+ return; \
+ } else \
((void)0)
/**
@@ -160,11 +153,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
* If not, prints `m_msg` and the current function returns `m_retval`.
*/
-#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
- if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
- _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \
- return m_retval; \
- } else \
+#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
+ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \
+ return m_retval; \
+ } else \
((void)0)
/**
@@ -189,11 +182,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.
* If not, prints `m_msg` and the application crashes.
*/
-#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \
- if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
- _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg), true); \
- GENERATE_TRAP(); \
- } else \
+#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \
+ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \
+ GENERATE_TRAP(); \
+ } else \
((void)0)
// Unsigned integer index out of bounds error macros.
@@ -216,11 +209,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Ensures an unsigned integer index `m_index` is less than `m_size`.
* If not, prints `m_msg` and the current function returns.
*/
-#define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \
- if (unlikely((m_index) >= (m_size))) { \
- _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \
- return; \
- } else \
+#define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \
+ if (unlikely((m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \
+ return; \
+ } else \
((void)0)
/**
@@ -241,11 +234,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Ensures an unsigned integer index `m_index` is less than `m_size`.
* If not, prints `m_msg` and the current function returns `m_retval`.
*/
-#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
- if (unlikely((m_index) >= (m_size))) { \
- _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \
- return m_retval; \
- } else \
+#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
+ if (unlikely((m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \
+ return m_retval; \
+ } else \
((void)0)
/**
@@ -270,11 +263,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Ensures an unsigned integer index `m_index` is less than `m_size`.
* If not, prints `m_msg` and the application crashes.
*/
-#define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \
- if (unlikely((m_index) >= (m_size))) { \
- _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg), true); \
- GENERATE_TRAP(); \
- } else \
+#define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \
+ if (unlikely((m_index) >= (m_size))) { \
+ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \
+ GENERATE_TRAP(); \
+ } else \
((void)0)
// Null reference error macros.
@@ -297,11 +290,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Ensures a pointer `m_param` is not null.
* If it is null, prints `m_msg` and the current function returns.
*/
-#define ERR_FAIL_NULL_MSG(m_param, m_msg) \
- if (unlikely(m_param == nullptr)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \
- return; \
- } else \
+#define ERR_FAIL_NULL_MSG(m_param, m_msg) \
+ if (unlikely(m_param == nullptr)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \
+ return; \
+ } else \
((void)0)
/**
@@ -322,11 +315,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Ensures a pointer `m_param` is not null.
* If it is null, prints `m_msg` and the current function returns `m_retval`.
*/
-#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \
- if (unlikely(m_param == nullptr)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \
- return m_retval; \
- } else \
+#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \
+ if (unlikely(m_param == nullptr)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \
+ return m_retval; \
+ } else \
((void)0)
/**
@@ -352,11 +345,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* If checking for null use ERR_FAIL_NULL_MSG instead.
* If checking index bounds use ERR_FAIL_INDEX_MSG instead.
*/
-#define ERR_FAIL_COND_MSG(m_cond, m_msg) \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", DEBUG_STR(m_msg)); \
- return; \
- } else \
+#define ERR_FAIL_COND_MSG(m_cond, m_msg) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg); \
+ return; \
+ } else \
((void)0)
/**
@@ -382,11 +375,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* If checking for null use ERR_FAIL_NULL_V_MSG instead.
* If checking index bounds use ERR_FAIL_INDEX_V_MSG instead.
*/
-#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), DEBUG_STR(m_msg)); \
- return m_retval; \
- } else \
+#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg); \
+ return m_retval; \
+ } else \
((void)0)
/**
@@ -407,11 +400,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Ensures `m_cond` is false.
* If `m_cond` is true, prints `m_msg` and the current loop continues.
*/
-#define ERR_CONTINUE_MSG(m_cond, m_msg) \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", DEBUG_STR(m_msg)); \
- continue; \
- } else \
+#define ERR_CONTINUE_MSG(m_cond, m_msg) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg); \
+ continue; \
+ } else \
((void)0)
/**
@@ -432,11 +425,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Ensures `m_cond` is false.
* If `m_cond` is true, prints `m_msg` and the current loop breaks.
*/
-#define ERR_BREAK_MSG(m_cond, m_msg) \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", DEBUG_STR(m_msg)); \
- break; \
- } else \
+#define ERR_BREAK_MSG(m_cond, m_msg) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg); \
+ break; \
+ } else \
((void)0)
/**
@@ -461,11 +454,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Ensures `m_cond` is false.
* If `m_cond` is true, prints `m_msg` and the application crashes.
*/
-#define CRASH_COND_MSG(m_cond, m_msg) \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", DEBUG_STR(m_msg)); \
- GENERATE_TRAP(); \
- } else \
+#define CRASH_COND_MSG(m_cond, m_msg) \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", m_msg); \
+ GENERATE_TRAP(); \
+ } else \
((void)0)
// Generic error macros.
@@ -490,11 +483,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
*
* Prints `m_msg`, and the current function returns.
*/
-#define ERR_FAIL_MSG(m_msg) \
- if (true) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", DEBUG_STR(m_msg)); \
- return; \
- } else \
+#define ERR_FAIL_MSG(m_msg) \
+ if (true) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg); \
+ return; \
+ } else \
((void)0)
/**
@@ -517,11 +510,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
*
* Prints `m_msg`, and the current function returns `m_retval`.
*/
-#define ERR_FAIL_V_MSG(m_retval, m_msg) \
- if (true) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), DEBUG_STR(m_msg)); \
- return m_retval; \
- } else \
+#define ERR_FAIL_V_MSG(m_retval, m_msg) \
+ if (true) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg); \
+ return m_retval; \
+ } else \
((void)0)
/**
@@ -590,14 +583,14 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
/**
* Warns that the current function is deprecated and prints `m_msg`.
*/
-#define WARN_DEPRECATED_MSG(m_msg) \
- if (true) { \
- static SafeFlag warning_shown; \
- if (!warning_shown.is_set()) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", DEBUG_STR(m_msg), ERR_HANDLER_WARNING); \
- warning_shown.set(); \
- } \
- } else \
+#define WARN_DEPRECATED_MSG(m_msg) \
+ if (true) { \
+ static SafeFlag warning_shown; \
+ if (!warning_shown.is_set()) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", m_msg, ERR_HANDLER_WARNING); \
+ warning_shown.set(); \
+ } \
+ } else \
((void)0)
/**
@@ -618,11 +611,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
*
* Prints `m_msg`, and then the application crashes.
*/
-#define CRASH_NOW_MSG(m_msg) \
- if (true) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", DEBUG_STR(m_msg)); \
- GENERATE_TRAP(); \
- } else \
+#define CRASH_NOW_MSG(m_msg) \
+ if (true) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", m_msg); \
+ GENERATE_TRAP(); \
+ } else \
((void)0)
#endif // ERROR_MACROS_H
diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp
index 660e215478..03b2426370 100644
--- a/core/extension/extension_api_dump.cpp
+++ b/core/extension/extension_api_dump.cpp
@@ -38,6 +38,32 @@
#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;
@@ -60,20 +86,38 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
const uint32_t vec3_elems = 3;
const uint32_t ptrsize_32 = 4;
- const uint32_t ptrsize_64 = 4;
+ const uint32_t ptrsize_64 = 8;
static const char *build_config_name[4] = { "float_32", "float_64", "double_32", "double_64" };
{
//type sizes
- struct {
+ 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(uint32_t), sizeof(uint32_t), sizeof(uint32_t), sizeof(uint32_t) },
+ { 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 },
@@ -98,25 +142,62 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
{ 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, ptrsize_64, ptrsize_32, ptrsize_64 },
- { Variant::PACKED_INT32_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
- { Variant::PACKED_INT64_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
- { Variant::PACKED_FLOAT32_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
- { Variant::PACKED_FLOAT64_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
- { Variant::PACKED_STRING_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
- { Variant::PACKED_VECTOR2_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
- { Variant::PACKED_VECTOR3_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
- { Variant::PACKED_COLOR_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++) {
+ 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;
@@ -272,11 +353,11 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
api_dump["global_constants"] = constants;
Array enums;
- for (Map<String, List<Pair<String, int>>>::Element *E = enum_list.front(); E; E = E->next()) {
+ for (const KeyValue<String, List<Pair<String, int>>> &E : enum_list) {
Dictionary d1;
- d1["name"] = E->key();
+ d1["name"] = E.key;
Array values;
- for (const Pair<String, int> &F : E->get()) {
+ for (const Pair<String, int> &F : E.value) {
Dictionary d2;
d2["name"] = F.first;
d2["value"] = F.second;
@@ -356,6 +437,8 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
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;
@@ -403,6 +486,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
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);
}
}
@@ -482,6 +566,10 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
d["constructors"] = constructors;
}
}
+ {
+ //destructor
+ d["has_destructor"] = Variant::has_destructor(type);
+ }
builtins.push_back(d);
}
@@ -572,7 +660,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
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) {
+ if ((F.flags & METHOD_FLAG_VIRTUAL) && !(F.flags & METHOD_FLAG_OBJECT_CORE)) {
//virtual method
const MethodInfo &mi = F;
Dictionary d2;
@@ -590,16 +678,8 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
if (i >= 0) {
d3["name"] = pinfo.name;
}
- if (pinfo.class_name != StringName()) {
- d3["type"] = String(pinfo.class_name);
- } else {
- Variant::Type type = pinfo.type;
- if (type == Variant::NIL) {
- d3["type"] = "Variant";
- } else {
- d3["type"] = Variant::get_type_name(type);
- }
- }
+
+ d3["type"] = get_type_name(pinfo);
if (i == -1) {
d2["return_value"] = d3;
@@ -641,16 +721,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
if (i >= 0) {
d3["name"] = pinfo.name;
}
- if (pinfo.class_name != StringName()) {
- d3["type"] = String(pinfo.class_name);
- } else {
- Variant::Type type = pinfo.type;
- if (type == Variant::NIL) {
- d3["type"] = "Variant";
- } else {
- d3["type"] = Variant::get_type_name(type);
- }
- }
+ 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" };
@@ -697,14 +768,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
for (int i = 0; i < F.arguments.size(); i++) {
Dictionary d3;
d3["name"] = F.arguments[i].name;
- Variant::Type type = F.arguments[i].type;
- if (F.arguments[i].class_name != StringName()) {
- d3["type"] = String(F.arguments[i].class_name);
- } else if (type == Variant::NIL) {
- d3["type"] = "Variant";
- } else {
- d3["type"] = Variant::get_type_name(type);
- }
+ d3["type"] = get_type_name(F.arguments[i]);
arguments.push_back(d3);
}
if (arguments.size()) {
@@ -732,16 +796,8 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
}
StringName property_name = F.name;
Dictionary d2;
+ d2["type"] = get_type_name(F);
d2["name"] = String(property_name);
-
- if (F.class_name != StringName()) {
- d2["type"] = String(F.class_name);
- } else if (F.type == Variant::NIL && F.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
- d2["type"] = "Variant";
- } else {
- d2["type"] = Variant::get_type_name(F.type);
- }
-
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);
@@ -782,6 +838,37 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
}
}
+ {
+ Array native_structures;
+
+ // AudioStream structures
+ {
+ Dictionary d;
+ d["name"] = "AudioFrame";
+ d["format"] = "float left,float right";
+
+ native_structures.push_back(d);
+ }
+
+ // TextServer structures
+ {
+ Dictionary d;
+ d["name"] = "Glyph";
+ d["format"] = "int start,int end,uint8_t count,uint8_t repeat,uint16_t flags,float x_off,float y_off,float advance,RID font_rid,int font_size,int32_t index";
+
+ native_structures.push_back(d);
+ }
+ {
+ Dictionary d;
+ d["name"] = "CaretInfo";
+ d["format"] = "Rect2 leading_caret,Rect2 trailing_caret,TextServer::Direction leading_direction,TextServer::Direction trailing_direction";
+
+ native_structures.push_back(d);
+ }
+
+ api_dump["native_structures"] = native_structures;
+ }
+
return api_dump;
}
diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp
index 88fff342ee..ff09b0b86c 100644
--- a/core/extension/gdnative_interface.cpp
+++ b/core/extension/gdnative_interface.cpp
@@ -74,8 +74,6 @@ static void gdnative_variant_destroy(GDNativeVariantPtr p_self) {
// variant type
-#define memnew_placement_custom(m_placement, m_class, m_constr) _post_initialize(new (m_placement, sizeof(m_class), "") m_constr)
-
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;
@@ -83,7 +81,7 @@ static void gdnative_variant_call(GDNativeVariantPtr p_self, const GDNativeStrin
Variant ret;
Callable::CallError error;
self->call(*method, args, p_argcount, ret, error);
- memnew_placement_custom(r_return, Variant, Variant(ret));
+ memnew_placement(r_return, Variant(ret));
if (r_error) {
r_error->error = (GDNativeCallErrorType)(error.error);
@@ -99,7 +97,7 @@ static void gdnative_variant_call_static(GDNativeVariantType p_type, const GDNat
Variant ret;
Callable::CallError error;
Variant::call_static(type, *method, args, p_argcount, ret, error);
- memnew_placement_custom(r_return, Variant, Variant(ret));
+ memnew_placement(r_return, Variant(ret));
if (r_error) {
r_error->error = (GDNativeCallErrorType)error.error;
@@ -164,7 +162,7 @@ static void gdnative_variant_get(const GDNativeVariantPtr p_self, const GDNative
const Variant *key = (const Variant *)p_key;
bool valid;
- memnew_placement_custom(r_ret, Variant, Variant(self->get(*key, &valid)));
+ memnew_placement(r_ret, Variant(self->get(*key, &valid)));
*r_valid = valid;
}
@@ -173,7 +171,7 @@ static void gdnative_variant_get_named(const GDNativeVariantPtr p_self, const GD
const StringName *key = (const StringName *)p_key;
bool valid;
- memnew_placement_custom(r_ret, Variant, Variant(self->get_named(*key, valid)));
+ memnew_placement(r_ret, Variant(self->get_named(*key, valid)));
*r_valid = valid;
}
@@ -182,7 +180,7 @@ static void gdnative_variant_get_keyed(const GDNativeVariantPtr p_self, const GD
const Variant *key = (const Variant *)p_key;
bool valid;
- memnew_placement_custom(r_ret, Variant, Variant(self->get_keyed(*key, valid)));
+ memnew_placement(r_ret, Variant(self->get_keyed(*key, valid)));
*r_valid = valid;
}
@@ -191,7 +189,7 @@ static void gdnative_variant_get_indexed(const GDNativeVariantPtr p_self, GDNati
bool valid;
bool oob;
- memnew_placement_custom(r_ret, Variant, Variant(self->get_indexed(p_index, valid, oob)));
+ memnew_placement(r_ret, Variant(self->get_indexed(p_index, valid, oob)));
*r_valid = valid;
*r_oob = oob;
}
@@ -222,7 +220,7 @@ static void gdnative_variant_iter_get(const GDNativeVariantPtr p_self, GDNativeV
Variant *iter = (Variant *)r_iter;
bool valid;
- memnew_placement_custom(r_ret, Variant, Variant(self->iter_next(*iter, valid)));
+ memnew_placement(r_ret, Variant(self->iter_next(*iter, valid)));
*r_valid = valid;
}
@@ -254,12 +252,12 @@ static void gdnative_variant_interpolate(const GDNativeVariantPtr p_a, const GDN
static void gdnative_variant_duplicate(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep) {
const Variant *self = (const Variant *)p_self;
- memnew_placement_custom(r_ret, Variant, Variant(self->duplicate(p_deep)));
+ 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_custom(r_ret, String, String(*self));
+ memnew_placement(r_ret, String(*self));
}
static GDNativeVariantType gdnative_variant_get_type(const GDNativeVariantPtr p_self) {
@@ -288,7 +286,7 @@ static GDNativeBool gdnative_variant_has_key(const GDNativeVariantPtr p_self, co
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_custom(r_ret, String, String(name));
+ memnew_placement(r_ret, String(name));
}
static GDNativeBool gdnative_variant_can_convert(GDNativeVariantType p_from, GDNativeVariantType p_to) {
@@ -299,6 +297,161 @@ static GDNativeBool gdnative_variant_can_convert_strict(GDNativeVariantType p_fr
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));
@@ -316,6 +469,9 @@ static GDNativePtrBuiltInMethod gdnative_variant_get_ptr_builtin_method(GDNative
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);
@@ -350,7 +506,7 @@ static GDNativePtrKeyedChecker gdnative_variant_get_ptr_keyed_checker(GDNativeVa
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_custom(r_ret, Variant, Variant(Variant::get_constant_value(Variant::Type(p_type), p_constant)));
+ 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;
@@ -505,9 +661,147 @@ static const char32_t *gdnative_string_operator_index_const(const GDNativeString
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];
+}
+
+static GDNativeVariantPtr gdnative_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) {
+ Array *self = (Array *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return (GDNativeTypePtr)&self[p_index];
+}
+
+static GDNativeVariantPtr gdnative_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+ const Array *self = (const Array *)p_self;
+ ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ return (GDNativeTypePtr)&self[p_index];
+}
+
/* OBJECT API */
-static void gdnative_object_method_bind_ptrcall(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr p_ret) {
+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);
@@ -521,11 +815,16 @@ 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, GDNativeInstanceBindingCallbacks *p_callbacks) {
+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));
}
@@ -555,14 +854,21 @@ static GDNativeMethodBindPtr gdnative_classdb_get_method_bind(const char *p_clas
return (GDNativeMethodBindPtr)mb;
}
-static GDNativeClassConstructor gdnative_classdb_get_constructor(const char *p_classname) {
+static GDNativeClassConstructor gdnative_classdb_get_constructor(const char *p_classname, GDNativeExtensionPtr *r_extension) {
ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(StringName(p_classname));
if (class_info) {
+ if (r_extension) {
+ *r_extension = class_info->native_extension;
+ }
return (GDNativeClassConstructor)class_info->creation_func;
}
return nullptr;
}
+static GDNativeObjectPtr gdnative_classdb_construct_object(GDNativeClassConstructor p_constructor, GDNativeExtensionPtr p_extension) {
+ return (GDNativeObjectPtr)ClassDB::construct_object((Object * (*)()) p_constructor, (ObjectNativeExtension *)p_extension);
+}
+
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;
@@ -626,15 +932,15 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) {
gdni.variant_can_convert = gdnative_variant_can_convert;
gdni.variant_can_convert_strict = gdnative_variant_can_convert_strict;
- //ptrcalls
-#if 0
- GDNativeVariantFromTypeConstructorFunc (*get_variant_from_type_constructor)(GDNativeVariantType p_type);
- GDNativeTypeFromVariantConstructorFunc (*get_variant_to_type_constructor)(GDNativeVariantType p_type);
-#endif
+ 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;
@@ -666,12 +972,43 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) {
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;
+
+ gdni.array_operator_index = gdnative_array_operator_index;
+ gdni.array_operator_index_const = gdnative_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;
@@ -680,6 +1017,7 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) {
/* CLASSDB */
gdni.classdb_get_constructor = gdnative_classdb_get_constructor;
+ gdni.classdb_construct_object = gdnative_classdb_construct_object;
gdni.classdb_get_method_bind = gdnative_classdb_get_method_bind;
gdni.classdb_get_class_tag = gdnative_classdb_get_class_tag;
@@ -690,6 +1028,8 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) {
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
index 3e69a28d59..73f78bde54 100644
--- a/core/extension/gdnative_interface.h
+++ b/core/extension/gdnative_interface.h
@@ -39,6 +39,11 @@
#include <stdint.h>
#include <stdio.h>
+#ifndef __cplusplus
+typedef uint32_t char32_t;
+typedef uint16_t char16_t;
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -132,6 +137,7 @@ typedef void *GDNativeStringNamePtr;
typedef void *GDNativeStringPtr;
typedef void *GDNativeObjectPtr;
typedef void *GDNativeTypePtr;
+typedef void *GDNativeExtensionPtr;
typedef void *GDNativeMethodBindPtr;
typedef int64_t GDNativeInt;
typedef uint8_t GDNativeBool;
@@ -140,12 +146,12 @@ typedef uint64_t GDObjectInstanceID;
/* VARIANT DATA I/O */
typedef enum {
- NATIVE_CALL_OK,
- NATIVE_CALL_ERROR_INVALID_METHOD,
- NATIVE_CALL_ERROR_INVALID_ARGUMENT, /* expected is variant type */
- NATIVE_CALL_ERROR_TOO_MANY_ARGUMENTS, /* expected is number of arguments */
- NATIVE_CALL_ERROR_TOO_FEW_ARGUMENTS, /* expected is number of arguments */
- NATIVE_CALL_ERROR_INSTANCE_IS_NULL,
+ 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;
@@ -160,6 +166,7 @@ typedef void (*GDNativeTypeFromVariantConstructorFunc)(GDNativeTypePtr, GDNative
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);
@@ -173,7 +180,7 @@ 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_instance, GDNativeBool p_reference);
+typedef GDNativeBool (*GDNativeInstanceBindingReferenceCallback)(void *p_token, void *p_binding, GDNativeBool p_reference);
struct GDNativeInstanceBindingCallbacks {
GDNativeInstanceBindingCreateCallback create_callback;
@@ -206,6 +213,7 @@ typedef void (*GDNativeExtensionClassUnreference)(GDExtensionClassInstancePtr p_
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 {
@@ -219,7 +227,8 @@ typedef struct {
GDNativeExtensionClassUnreference unreference_func;
GDNativeExtensionClassCreateInstance create_instance_func; /* this one is mandatory */
GDNativeExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
- GDNativeExtensionClassGetVirtual get_firtual_func;
+ GDNativeExtensionClassObjectInstance object_instance_func; /* this one is mandatory */
+ GDNativeExtensionClassGetVirtual get_virtual_func;
void *class_userdata;
} GDNativeExtensionClassCreationInfo;
@@ -256,8 +265,8 @@ typedef enum {
GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE
} GDNativeExtensionClassMethodArgumentMetadata;
-typedef void (*GDNativeExtensionClassMethodCall)(GDExtensionClassInstancePtr p_instance, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error);
-typedef void (*GDNativeExtensionClassMethodPtrCall)(GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret);
+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);
@@ -339,6 +348,7 @@ typedef struct {
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);
@@ -378,12 +388,43 @@ typedef struct {
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
+
+ GDNativeVariantPtr (*array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be an Array ptr
+ GDNativeVariantPtr (*array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be an Array ptr
+
/* OBJECT */
- void (*object_method_bind_ptrcall)(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret);
+ 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, GDNativeInstanceBindingCallbacks *p_callbacks);
+ 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);
@@ -391,7 +432,8 @@ typedef struct {
/* CLASSDB */
- GDNativeClassConstructor (*classdb_get_constructor)(const char *p_classname);
+ GDNativeClassConstructor (*classdb_get_constructor)(const char *p_classname, GDNativeExtensionPtr *r_extension);
+ GDNativeObjectPtr (*classdb_construct_object)(GDNativeClassConstructor p_constructor, GDNativeExtensionPtr p_extension);
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);
@@ -399,8 +441,10 @@ typedef struct {
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_enum_name, const char *p_class_name, const char *p_constant_name, uint32_t p_constant_value);
+ 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;
@@ -412,6 +456,8 @@ typedef enum {
GDNATIVE_INITIALIZATION_SERVERS,
GDNATIVE_INITIALIZATION_SCENE,
GDNATIVE_INITIALIZATION_EDITOR,
+ GDNATIVE_INITIALIZATION_DRIVER,
+ GDNATIVE_MAX_INITIALIZATION_LEVEL,
} GDNativeInitializationLevel;
typedef struct {
diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp
index 16bc28e0a2..a6b0a708c3 100644
--- a/core/extension/native_extension.cpp
+++ b/core/extension/native_extension.cpp
@@ -29,11 +29,16 @@
/*************************************************************************/
#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"
+String NativeExtension::get_extension_list_config_file() {
+ return ProjectSettings::get_singleton()->get_project_data_path().plus_file("extension_list.cfg");
+}
+
class NativeExtensionMethodBind : public MethodBind {
GDNativeExtensionClassMethodCall call_func;
GDNativeExtensionClassMethodPtrCall ptrcall_func;
@@ -68,8 +73,8 @@ public:
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;
- call_func(extension_instance, (const GDNativeVariantPtr *)p_args, p_arg_count, (GDNativeVariantPtr)&ret, &ce);
+ 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;
@@ -78,7 +83,7 @@ public:
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(extension_instance, (const GDNativeTypePtr *)p_args, (GDNativeTypePtr)r_ret);
+ ptrcall_func(method_userdata, extension_instance, (const GDNativeTypePtr *)p_args, (GDNativeTypePtr)r_ret);
}
virtual bool is_vararg() const {
@@ -91,6 +96,7 @@ public:
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;
@@ -109,7 +115,7 @@ void NativeExtension::_register_extension_class(const GDNativeExtensionClassLibr
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 clas '" + class_name + "', which is not a valid class identifier.");
+ 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;
@@ -150,7 +156,9 @@ void NativeExtension::_register_extension_class(const GDNativeExtensionClassLibr
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);
}
@@ -159,29 +167,31 @@ void NativeExtension::_register_extension_class_method(const GDNativeExtensionCl
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 + "'.");
+ 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, uint32_t p_constant_value) {
+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 + "'.");
+ 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 + "'.");
+ 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;
@@ -195,11 +205,29 @@ void NativeExtension::_register_extension_class_property(const GDNativeExtension
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 + "'.");
+ 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;
@@ -220,7 +248,7 @@ void NativeExtension::_unregister_extension_class(const GDNativeExtensionClassLi
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 + "'.");
+ 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.");
@@ -318,6 +346,8 @@ void NativeExtension::initialize_native_extensions() {
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;
}
@@ -368,7 +398,7 @@ RES NativeExtensionResourceLoader::load(const String &p_path, const String &p_or
}
}
- if (library_path != String()) {
+ if (library_path == String()) {
if (r_error) {
*r_error = ERR_FILE_NOT_FOUND;
}
@@ -381,7 +411,8 @@ RES NativeExtensionResourceLoader::load(const String &p_path, const String &p_or
Ref<NativeExtension> lib;
lib.instantiate();
- err = lib->open_library(library_path, entry_symbol);
+ String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path);
+ err = lib->open_library(abs_path, entry_symbol);
if (r_error) {
*r_error = err;
diff --git a/core/extension/native_extension.h b/core/extension/native_extension.h
index 0a23848eb2..f7f235d8fc 100644
--- a/core/extension/native_extension.h
+++ b/core/extension/native_extension.h
@@ -35,8 +35,8 @@
#include "core/io/resource_loader.h"
#include "core/object/ref_counted.h"
-class NativeExtension : public RefCounted {
- GDCLASS(NativeExtension, RefCounted)
+class NativeExtension : public Resource {
+ GDCLASS(NativeExtension, Resource)
void *library = nullptr; // pointer if valid,
@@ -48,8 +48,10 @@ class NativeExtension : public RefCounted {
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, uint32_t p_constant_value);
+ 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);
@@ -60,6 +62,8 @@ protected:
static void _bind_methods();
public:
+ static String get_extension_list_config_file();
+
Error open_library(const String &p_path, const String &p_entry_symbol);
void close_library();
@@ -68,6 +72,7 @@ public:
INITIALIZATION_LEVEL_SERVERS,
INITIALIZATION_LEVEL_SCENE,
INITIALIZATION_LEVEL_EDITOR,
+ INITIALIZATION_LEVEL_DRIVER,
};
bool is_library_open() const;
diff --git a/core/extension/native_extension_manager.cpp b/core/extension/native_extension_manager.cpp
index 7be2593845..c8755250d5 100644
--- a/core/extension/native_extension_manager.cpp
+++ b/core/extension/native_extension_manager.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#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)) {
@@ -76,10 +77,15 @@ NativeExtensionManager::LoadStatus NativeExtensionManager::unload_extension(cons
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());
+ for (const KeyValue<String, Ref<NativeExtension>> &E : native_extension_map) {
+ ret.push_back(E.key);
}
return ret;
}
@@ -91,20 +97,31 @@ Ref<NativeExtension> NativeExtensionManager::get_extension(const String &p_path)
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);
+ for (KeyValue<String, Ref<NativeExtension>> &E : native_extension_map) {
+ E.value->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);
+ for (KeyValue<String, Ref<NativeExtension>> &E : native_extension_map) {
+ E.value->deinitialize_library(p_level);
}
level = int32_t(p_level) - 1;
}
+void NativeExtensionManager::load_extensions() {
+ FileAccessRef f = FileAccess::open(NativeExtension::get_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;
}
@@ -112,6 +129,8 @@ 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);
diff --git a/core/extension/native_extension_manager.h b/core/extension/native_extension_manager.h
index 78465bd5cf..89ccd155fe 100644
--- a/core/extension/native_extension_manager.h
+++ b/core/extension/native_extension_manager.h
@@ -55,6 +55,7 @@ public:
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);
@@ -63,6 +64,8 @@ public:
static NativeExtensionManager *get_singleton();
+ void load_extensions();
+
NativeExtensionManager();
};
diff --git a/core/input/input.cpp b/core/input/input.cpp
index c205726b0a..f9a361c761 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -35,10 +35,6 @@
#include "core/input/input_map.h"
#include "core/os/os.h"
-#ifdef TOOLS_ENABLED
-#include "editor/editor_settings.h"
-#endif
-
static const char *_joy_buttons[JOY_BUTTON_SDL_MAX] = {
"a",
"b",
@@ -162,10 +158,6 @@ 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) ? "'" : "\"";
-
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" ||
@@ -180,10 +172,9 @@ void Input::get_argument_options(const StringName &p_function, int p_idx, List<S
}
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());
}
}
-#endif
}
void Input::SpeedTrack::update(const Vector2 &p_delta_p) {
@@ -221,7 +212,7 @@ 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);
}
@@ -241,18 +232,12 @@ bool Input::is_joy_button_pressed(int p_device, JoyButton p_button) const {
}
bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const {
-#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 +255,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 +273,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;
@@ -460,10 +439,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 +447,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()) {
@@ -838,25 +811,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,15 +849,15 @@ 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();
_joy_axis.clear();
- for (Map<StringName, Input::Action>::Element *E = action_state.front(); E; E = E->next()) {
- if (E->get().pressed) {
- action_release(E->key());
+ for (const KeyValue<StringName, Input::Action> &E : action_state) {
+ if (E.value.pressed) {
+ action_release(E.key);
}
}
}
@@ -1329,8 +1314,8 @@ void Input::add_joy_mapping(String p_mapping, bool p_update_existing) {
if (p_update_existing) {
Vector<String> entry = p_mapping.split(",");
String uid = entry[0];
- for (Map<int, Joypad>::Element *E = joy_names.front(); E; E = E->next()) {
- Joypad &joy = E->get();
+ for (KeyValue<int, Joypad> &E : joy_names) {
+ Joypad &joy = E.value;
if (joy.uid == uid) {
joy.mapping = map_db.size() - 1;
}
@@ -1344,8 +1329,8 @@ void Input::remove_joy_mapping(String p_guid) {
map_db.remove(i);
}
}
- for (Map<int, Joypad>::Element *E = joy_names.front(); E; E = E->next()) {
- Joypad &joy = E->get();
+ for (KeyValue<int, Joypad> &E : joy_names) {
+ Joypad &joy = E.value;
if (joy.uid == p_guid) {
joy.mapping = -1;
}
diff --git a/core/input/input.h b/core/input/input.h
index fbcd5836ea..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 {
@@ -110,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;
@@ -212,7 +214,7 @@ private:
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;
@@ -244,7 +246,7 @@ public:
static Input *get_singleton();
- bool is_key_pressed(int p_keycode) 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;
@@ -322,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_event.cpp b/core/input/input_event.cpp
index 4a2abffae8..1d2b5f19ee 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;
@@ -306,21 +307,21 @@ 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;
}
@@ -386,7 +387,7 @@ 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.instantiate();
ie->set_keycode(p_keycode & KEY_CODE_MASK);
@@ -430,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;
}
@@ -586,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;
}
@@ -861,6 +864,8 @@ void InputEventMouseMotion::_bind_methods() {
///////////////////////////////////
void InputEventJoypadMotion::set_axis(JoyAxis p_axis) {
+ ERR_FAIL_INDEX(p_axis, JOY_AXIS_MAX);
+
axis = p_axis;
emit_changed();
}
@@ -997,10 +1002,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;
}
@@ -1209,6 +1215,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);
@@ -1274,10 +1296,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;
}
@@ -1512,3 +1535,37 @@ 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 {
+ ERR_FAIL_COND_V(shortcut.is_null(), "None");
+
+ return vformat(RTR("Input Event with Shortcut=%s"), shortcut->get_as_text());
+}
+
+String InputEventShortcut::to_string() {
+ ERR_FAIL_COND_V(shortcut.is_null(), "None");
+
+ return vformat("InputEventShortcut: shortcut=%s", shortcut->get_as_text());
+}
diff --git a/core/input/input_event.h b/core/input/input_event.h
index 76a45c04a4..3fc8078a09 100644
--- a/core/input/input_event.h
+++ b/core/input/input_event.h
@@ -34,6 +34,7 @@
#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"
@@ -42,6 +43,8 @@
* The events are pretty obvious.
*/
+class Shortcut;
+
/**
* Input Modifier Status
* for keyboard/mouse events.
@@ -161,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
@@ -174,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;
@@ -197,7 +200,7 @@ public:
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() {}
};
@@ -407,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() {}
};
@@ -538,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 6714705bb5..1ec4299093 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "core/input/input.h"
#include "core/os/keyboard.h"
+#include "core/os/os.h"
InputMap *InputMap::singleton = nullptr;
@@ -59,7 +60,7 @@ 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;
@@ -93,7 +94,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);
}
@@ -147,20 +148,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.
}
@@ -169,12 +170,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) {
@@ -186,7 +187,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();
}
@@ -196,7 +197,7 @@ Array InputMap::_action_get_events(const StringName &p_action) {
const List<Ref<InputEvent>> *al = action_get_events(p_action);
if (al) {
for (const List<Ref<InputEvent>>::Element *E = al->front(); E; E = E->next()) {
- ret.push_back(E);
+ ret.push_back(E->get());
}
}
@@ -218,15 +219,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;
}
@@ -316,36 +318,36 @@ 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_toggle_insert_mode", TTRC("Toggle Insert Mode") },
@@ -465,7 +467,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
// ///// UI Text Input Shortcuts /////
inputs = List<Ref<InputEvent>>();
- inputs.push_back(InputEventKey::create_reference(KEY_SPACE | KEY_MASK_CMD));
+ inputs.push_back(InputEventKey::create_reference(KEY_SPACE | KEY_MASK_CTRL));
default_builtin_cache.insert("ui_text_completion_query", inputs);
inputs = List<Ref<InputEvent>>();
@@ -515,14 +517,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));
@@ -534,14 +536,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
@@ -555,7 +557,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));
@@ -567,7 +569,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
@@ -588,7 +590,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));
@@ -597,7 +599,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
@@ -617,7 +619,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));
@@ -625,7 +627,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
@@ -635,7 +637,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));
@@ -643,7 +645,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
@@ -698,34 +700,57 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
return default_builtin_cache;
}
-void InputMap::load_default() {
+const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with_feature_overrides_applied() {
+ if (default_builtin_with_overrides_cache.size() > 0) {
+ return default_builtin_with_overrides_cache;
+ }
+
OrderedHashMap<String, List<Ref<InputEvent>>> builtins = get_builtins();
- // List of Builtins which have an override for OSX.
- Vector<String> osx_builtins;
+ // Get a list of all built in inputs which are valid overrides for the OS
+ // Key = builtin name (e.g. ui_accept)
+ // Value = override/feature names (e.g. macos, if it was defined as "ui_accept.macos" and the platform supports that feature)
+ Map<String, Vector<String>> builtins_with_overrides;
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]);
+ String fullname = E.key();
+
+ Vector<String> split = fullname.split(".");
+ String name = split[0];
+ String override_for = split.size() > 1 ? split[1] : String();
+
+ if (override_for != String() && OS::get_singleton()->has_feature(override_for)) {
+ builtins_with_overrides[name].push_back(override_for);
}
}
for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) {
String fullname = E.key();
- String name = fullname.split(".")[0];
- 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.
+ Vector<String> split = fullname.split(".");
+ String name = split[0];
+ String override_for = split.size() > 1 ? split[1] : String();
+
+ if (builtins_with_overrides.has(name) && override_for == String()) {
+ // Builtin has an override but this particular one is not an override, so skip.
continue;
}
-#else
- if (override_for == "OSX") {
- // Override for OSX - not needed on non-osx platforms.
+
+ if (override_for != String() && !OS::get_singleton()->has_feature(override_for)) {
+ // OS does not support this override - skip.
continue;
}
-#endif
+
+ default_builtin_with_overrides_cache.insert(name, E.value());
+ }
+
+ return default_builtin_with_overrides_cache;
+}
+
+void InputMap::load_default() {
+ OrderedHashMap<String, List<Ref<InputEvent>>> builtins = get_builtins_with_feature_overrides_applied();
+
+ for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) {
+ String name = E.key();
add_action(name);
@@ -745,3 +770,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..8bef722089 100644
--- a/core/input/input_map.h
+++ b/core/input/input_map.h
@@ -56,12 +56,12 @@ private:
mutable OrderedHashMap<StringName, Action> input_map;
OrderedHashMap<String, List<Ref<InputEvent>>> default_builtin_cache;
+ OrderedHashMap<String, List<Ref<InputEvent>>> default_builtin_with_overrides_cache;
List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const;
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 +89,15 @@ 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();
+ const OrderedHashMap<String, List<Ref<InputEvent>>> &get_builtins_with_feature_overrides_applied();
InputMap();
+ ~InputMap();
};
#endif // INPUT_MAP_H
diff --git a/core/input/shortcut.cpp b/core/input/shortcut.cpp
new file mode 100644
index 0000000000..30e35190e4
--- /dev/null
+++ b/core/input/shortcut.cpp
@@ -0,0 +1,132 @@
+/*************************************************************************/
+/* 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_events(const Array &p_events) {
+ for (int i = 0; i < p_events.size(); i++) {
+ Ref<InputEventShortcut> ies = p_events[i];
+ ERR_FAIL_COND_MSG(ies.is_valid(), "Cannot set a shortcut event to an instance of InputEventShortcut.");
+ }
+
+ events = p_events;
+ emit_changed();
+}
+
+void Shortcut::set_events_list(const List<Ref<InputEvent>> *p_events) {
+ events.clear();
+
+ for (const Ref<InputEvent> &ie : *p_events) {
+ events.push_back(ie);
+ }
+}
+
+Array Shortcut::get_events() const {
+ return events;
+}
+
+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;
+ }
+ }
+
+ for (int i = 0; i < events.size(); i++) {
+ Ref<InputEvent> ie = events[i];
+ bool valid = ie.is_valid() && ie->is_match(p_event);
+
+ // Stop on first valid event - don't need to check further.
+ if (valid) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+String Shortcut::get_as_text() const {
+ for (int i = 0; i < events.size(); i++) {
+ Ref<InputEvent> ie = events[i];
+ // Return first shortcut which is valid
+ if (ie.is_valid()) {
+ return ie->as_text();
+ }
+ }
+
+ return "None";
+}
+
+bool Shortcut::has_valid_event() const {
+ // Tests if there is ANY input event which is valid.
+ for (int i = 0; i < events.size(); i++) {
+ Ref<InputEvent> ie = events[i];
+ if (ie.is_valid()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Shortcut::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_events", "events"), &Shortcut::set_events);
+ ClassDB::bind_method(D_METHOD("get_events"), &Shortcut::get_events);
+
+ 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::ARRAY, "events", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")), "set_events", "get_events");
+}
+
+bool Shortcut::is_event_array_equal(const Array &p_event_array1, const Array &p_event_array2) {
+ if (p_event_array1.size() != p_event_array2.size()) {
+ return false;
+ }
+
+ bool is_same = true;
+ for (int i = 0; i < p_event_array1.size(); i++) {
+ Ref<InputEvent> ie_1 = p_event_array1[i];
+ Ref<InputEvent> ie_2 = p_event_array2[i];
+
+ is_same = ie_1->is_match(ie_2);
+
+ // Break on the first that doesn't match - don't need to check further.
+ if (!is_same) {
+ break;
+ }
+ }
+
+ return is_same;
+}
diff --git a/core/io/multiplayer_peer.h b/core/input/shortcut.h
index 91a3ad7954..a989b10626 100644
--- a/core/io/multiplayer_peer.h
+++ b/core/input/shortcut.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* multiplayer_peer.h */
+/* shortcut.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,56 +28,31 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef NETWORKED_MULTIPLAYER_PEER_H
-#define NETWORKED_MULTIPLAYER_PEER_H
+#ifndef SHORTCUT_H
+#define SHORTCUT_H
-#include "core/io/packet_peer.h"
+#include "core/input/input_event.h"
+#include "core/io/resource.h"
-class MultiplayerPeer : public PacketPeer {
- GDCLASS(MultiplayerPeer, PacketPeer);
+class Shortcut : public Resource {
+ GDCLASS(Shortcut, Resource);
+
+ Array events;
protected:
static void _bind_methods();
public:
- enum {
- TARGET_PEER_BROADCAST = 0,
- TARGET_PEER_SERVER = 1
- };
- enum TransferMode {
- TRANSFER_MODE_UNRELIABLE,
- TRANSFER_MODE_UNRELIABLE_ORDERED,
- TRANSFER_MODE_RELIABLE,
- };
-
- enum ConnectionStatus {
- CONNECTION_DISCONNECTED,
- CONNECTION_CONNECTING,
- CONNECTION_CONNECTED,
- };
-
- virtual void set_transfer_mode(TransferMode p_mode) = 0;
- virtual TransferMode get_transfer_mode() const = 0;
- virtual void set_target_peer(int p_peer_id) = 0;
-
- virtual int get_packet_peer() const = 0;
+ void set_events(const Array &p_events);
+ Array get_events() const;
- virtual bool is_server() const = 0;
+ void set_events_list(const List<Ref<InputEvent>> *p_events);
- virtual void poll() = 0;
+ bool matches_event(const Ref<InputEvent> &p_event) const;
+ bool has_valid_event() const;
- virtual int get_unique_id() const = 0;
+ String get_as_text() const;
- virtual void set_refuse_new_connections(bool p_enable) = 0;
- virtual bool is_refusing_new_connections() const = 0;
-
- virtual ConnectionStatus get_connection_status() const = 0;
- uint32_t generate_unique_id() const;
-
- MultiplayerPeer() {}
+ static bool is_event_array_equal(const Array &p_event_array1, const Array &p_event_array2);
};
-
-VARIANT_ENUM_CAST(MultiplayerPeer::TransferMode)
-VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus)
-
-#endif // NETWORKED_MULTIPLAYER_PEER_H
+#endif // SHORTCUT_H
diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp
index aeaf25f321..49fa73dab2 100644
--- a/core/io/config_file.cpp
+++ b/core/io/config_file.cpp
@@ -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/dir_access.cpp b/core/io/dir_access.cpp
index 8234adea06..3bff0a3fd5 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -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;
}
diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp
index d21c0bd9a2..e6e79dff8a 100644
--- a/core/io/file_access.cpp
+++ b/core/io/file_access.cpp
@@ -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;
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index 7b43daf9c0..b2832b2a75 100644
--- a/core/io/file_access_pack.cpp
+++ b/core/io/file_access_pack.cpp
@@ -110,8 +110,8 @@ PackedData::PackedData() {
}
void PackedData::_free_packed_dirs(PackedDir *p_dir) {
- for (Map<String, PackedDir *>::Element *E = p_dir->subdirs.front(); E; E = E->next()) {
- _free_packed_dirs(E->get());
+ for (const KeyValue<String, PackedDir *> &E : p_dir->subdirs) {
+ _free_packed_dirs(E.value);
}
memdelete(p_dir);
}
@@ -395,8 +395,8 @@ Error DirAccessPack::list_dir_begin() {
list_dirs.clear();
list_files.clear();
- for (Map<String, PackedData::PackedDir *>::Element *E = current->subdirs.front(); E; E = E->next()) {
- list_dirs.push_back(E->key());
+ for (const KeyValue<String, PackedData::PackedDir *> &E : current->subdirs) {
+ list_dirs.push_back(E.key);
}
for (Set<String>::Element *E = current->files.front(); E; E = E->next()) {
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index f291086808..b3d35b3603 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -45,6 +45,8 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss
conn_port = p_port;
conn_host = p_host;
+ ip_candidates.clear();
+
ssl = p_ssl;
ssl_verify_host = p_verify_host;
@@ -234,6 +236,7 @@ void HTTPClientTCP::close() {
resolving = IP::RESOLVER_INVALID_ID;
}
+ ip_candidates.clear();
response_headers.clear();
response_str.clear();
body_size = -1;
@@ -256,10 +259,17 @@ Error HTTPClientTCP::poll() {
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_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;
@@ -313,6 +323,7 @@ Error HTTPClientTCP::poll() {
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) {
@@ -323,15 +334,24 @@ Error HTTPClientTCP::poll() {
}
// ... 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_CANT_CONNECT;
+ return err;
} break;
}
} break;
diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h
index e178399fbe..170afb551c 100644
--- a/core/io/http_client_tcp.h
+++ b/core/io/http_client_tcp.h
@@ -37,6 +37,7 @@ 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;
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 3112dd217f..c70f4b86bd 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -2506,7 +2506,7 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po
clipped_src_rect.position.y = ABS(p_dest.y);
}
- if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) {
+ if (clipped_src_rect.has_no_area()) {
return;
}
@@ -2561,7 +2561,7 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co
clipped_src_rect.position.y = ABS(p_dest.y);
}
- if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) {
+ if (clipped_src_rect.has_no_area()) {
return;
}
@@ -2615,7 +2615,7 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const P
clipped_src_rect.position.y = ABS(p_dest.y);
}
- if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) {
+ if (clipped_src_rect.has_no_area()) {
return;
}
@@ -2664,7 +2664,7 @@ void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, c
clipped_src_rect.position.y = ABS(p_dest.y);
}
- if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) {
+ if (clipped_src_rect.has_no_area()) {
return;
}
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index cd1b6d1994..68b4e4b354 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,17 +120,23 @@ 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();
for (int i = 0; i < res.size(); ++i) {
if (res[i].is_valid()) {
@@ -128,14 +147,23 @@ IPAddress IP::resolve_hostname(const String &p_hostname, IP::Type p_type) {
}
Array IP::resolve_hostname_addresses(const String &p_hostname, Type p_type) {
- MutexLock lock(resolver->mutex);
-
+ 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;
for (int i = 0; i < res.size(); ++i) {
@@ -178,13 +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");
return IP::RESOLVER_STATUS_NONE;
}
- return resolver->queue[p_id].status.get();
+ return res;
}
IPAddress IP::get_resolve_item_address(ResolverID p_id) const {
@@ -230,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);
}
@@ -263,8 +288,8 @@ Array IP::_get_local_interfaces() const {
Array results;
Map<String, Interface_Info> interfaces;
get_local_interfaces(&interfaces);
- for (Map<String, Interface_Info>::Element *E = interfaces.front(); E; E = E->next()) {
- Interface_Info &c = E->get();
+ for (KeyValue<String, Interface_Info> &E : interfaces) {
+ Interface_Info &c = E.value;
Dictionary rc;
rc["name"] = c.name;
rc["friendly"] = c.name_friendly;
@@ -285,8 +310,8 @@ Array IP::_get_local_interfaces() const {
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 IPAddress &F : E->get().ip_addresses) {
+ for (const KeyValue<String, Interface_Info> &E : interfaces) {
+ for (const IPAddress &F : E.value.ip_addresses) {
r_addresses->push_front(F);
}
}
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index 4f85eced93..e7d5b78d14 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -746,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);
@@ -795,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);
@@ -804,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++) {
@@ -1519,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++) {
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp
deleted file mode 100644
index d4f09b2135..0000000000
--- a/core/io/multiplayer_api.cpp
+++ /dev/null
@@ -1,1110 +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
-
-String _get_rpc_md5(const Node *p_node) {
- String rpc_list;
- const Vector<MultiplayerAPI::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<MultiplayerAPI::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();
-}
-
-const MultiplayerAPI::RPCConfig _get_rpc_config(const Node *p_node, const StringName &p_method, uint16_t &r_id) {
- const Vector<MultiplayerAPI::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<MultiplayerAPI::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 MultiplayerAPI::RPCConfig();
-}
-
-const MultiplayerAPI::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_id) {
- Vector<MultiplayerAPI::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 MultiplayerAPI::RPCConfig();
-}
-
-_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: {
- return true;
- } break;
- case MultiplayerAPI::RPC_MODE_MASTER: {
- return p_node->is_network_master();
- } break;
- 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() == MultiplayerPeer::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<MultiplayerPeer> &p_peer) {
- if (p_peer == network_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 (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<MultiplayerPeer> 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: {
- // 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);
- _process_rpc(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.
- const 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) + ", 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(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 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 = _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]);
-
- network_peer->set_transfer_mode(MultiplayerPeer::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 = _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) {
- network_peer->set_target_peer(E); // To all of you.
- network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
- network_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::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, 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() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree.");
-
- ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::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 = 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 = _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_config.transfer_mode);
-
- 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(SNAME("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 (const NodePath &E : keys) {
- PathSentCache *psc = path_send_cache.getptr(E);
- psc->confirmed_peers.erase(p_id);
- }
- emit_signal(SNAME("network_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"));
-}
-
-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() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected.");
-
- int node_id = network_peer->get_unique_id();
- bool call_local_native = false;
- bool call_local_script = false;
- uint16_t rpc_id = UINT16_MAX;
- const 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) {
- 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(p_peer_id == node_id && !config.sync, "RPC '" + p_method + "' on yourself is not allowed by selected mode.");
-}
-
-Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, MultiplayerPeer::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() != MultiplayerPeer::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(SNAME("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 {
- return network_peer.is_valid() && 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(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::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, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_network_peer", "get_network_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_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);
-}
-
-MultiplayerAPI::MultiplayerAPI() {
- clear();
-}
-
-MultiplayerAPI::~MultiplayerAPI() {
- clear();
-}
diff --git a/core/io/multiplayer_peer.cpp b/core/io/multiplayer_peer.cpp
deleted file mode 100644
index 8b3b1eef8e..0000000000
--- a/core/io/multiplayer_peer.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*************************************************************************/
-/* multiplayer_peer.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_peer.h"
-
-#include "core/os/os.h"
-
-uint32_t MultiplayerPeer::generate_unique_id() const {
- uint32_t hash = 0;
-
- 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
-
- hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion
- }
-
- return hash;
-}
-
-void MultiplayerPeer::_bind_methods() {
- 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);
-
- BIND_ENUM_CONSTANT(CONNECTION_DISCONNECTED);
- BIND_ENUM_CONSTANT(CONNECTION_CONNECTING);
- BIND_ENUM_CONSTANT(CONNECTION_CONNECTED);
-
- BIND_CONSTANT(TARGET_PEER_BROADCAST);
- BIND_CONSTANT(TARGET_PEER_SERVER);
-
- ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
- ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
- ADD_SIGNAL(MethodInfo("server_disconnected"));
- ADD_SIGNAL(MethodInfo("connection_succeeded"));
- ADD_SIGNAL(MethodInfo("connection_failed"));
-}
diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp
index 8da44fd290..87d2b66e5b 100644
--- a/core/io/packet_peer.cpp
+++ b/core/io/packet_peer.cpp
@@ -138,6 +138,7 @@ Error PacketPeer::_get_packet_error() const {
void PacketPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &PacketPeer::_bnd_get_var, DEFVAL(false));
ClassDB::bind_method(D_METHOD("put_var", "var", "full_objects"), &PacketPeer::put_var, DEFVAL(false));
+
ClassDB::bind_method(D_METHOD("get_packet"), &PacketPeer::_get_packet);
ClassDB::bind_method(D_METHOD("put_packet", "buffer"), &PacketPeer::_put_packet);
ClassDB::bind_method(D_METHOD("get_packet_error"), &PacketPeer::_get_packet_error);
@@ -151,6 +152,51 @@ void PacketPeer::_bind_methods() {
/***************/
+int PacketPeerExtension::get_available_packet_count() const {
+ int count;
+ if (GDVIRTUAL_CALL(_get_available_packet_count, count)) {
+ return count;
+ }
+ WARN_PRINT_ONCE("PacketPeerExtension::_get_available_packet_count is unimplemented!");
+ return -1;
+}
+
+Error PacketPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ int err;
+ if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("PacketPeerExtension::_get_packet_native is unimplemented!");
+ return FAILED;
+}
+
+Error PacketPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ int err;
+ if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("PacketPeerExtension::_put_packet_native is unimplemented!");
+ return FAILED;
+}
+
+int PacketPeerExtension::get_max_packet_size() const {
+ int size;
+ if (GDVIRTUAL_CALL(_get_max_packet_size, size)) {
+ return size;
+ }
+ WARN_PRINT_ONCE("PacketPeerExtension::_get_max_packet_size is unimplemented!");
+ return 0;
+}
+
+void PacketPeerExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size");
+ GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size");
+ GDVIRTUAL_BIND(_get_available_packet_count);
+ GDVIRTUAL_BIND(_get_max_packet_size);
+}
+
+/***************/
+
void PacketPeerStream::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream_peer", "peer"), &PacketPeerStream::set_stream_peer);
ClassDB::bind_method(D_METHOD("get_stream_peer"), &PacketPeerStream::get_stream_peer);
diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h
index 9a345af3d0..bc1f4aaabf 100644
--- a/core/io/packet_peer.h
+++ b/core/io/packet_peer.h
@@ -35,6 +35,10 @@
#include "core/object/class_db.h"
#include "core/templates/ring_buffer.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
+#include "core/variant/native_ptr.h"
+
class PacketPeer : public RefCounted {
GDCLASS(PacketPeer, RefCounted);
@@ -73,6 +77,25 @@ public:
~PacketPeer() {}
};
+class PacketPeerExtension : public PacketPeer {
+ GDCLASS(PacketPeerExtension, PacketPeer);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual int get_available_packet_count() const override;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
+ virtual int get_max_packet_size() const override;
+
+ /* GDExtension */
+ GDVIRTUAL0RC(int, _get_available_packet_count);
+ GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>);
+ GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int);
+ GDVIRTUAL0RC(int, _get_max_packet_size);
+};
+
class PacketPeerStream : public PacketPeer {
GDCLASS(PacketPeerStream, PacketPeer);
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index 727611a573..1cefa52d69 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -352,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;
@@ -422,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() :
@@ -541,9 +540,9 @@ void ResourceCache::dump(const char *p_file, bool p_short) {
}
}
- for (Map<String, int>::Element *E = type_count.front(); E; E = E->next()) {
+ for (const KeyValue<String, int> &E : type_count) {
if (f) {
- f->store_line(E->key() + " count: " + itos(E->get()));
+ f->store_line(E.key + " count: " + itos(E.value));
}
}
if (f) {
@@ -552,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 e864b371ad..9ccc247887 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -62,8 +62,6 @@ private:
String path_cache;
String scene_unique_id;
- virtual bool _use_builtin_script() const { return true; }
-
#ifdef TOOLS_ENABLED
uint64_t last_modified_time = 0;
uint64_t import_last_modified_time = 0;
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index a3ebc32cc5..cbb033f6c6 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -335,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));
}
@@ -626,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));
}
@@ -903,10 +903,11 @@ void ResourceLoaderBinary::open(FileAccess *p_f, bool p_no_resources, bool p_kee
if (using_uids) {
uid = f->get_64();
} else {
+ f->get_64(); // skip over uid field
uid = ResourceUID::INVALID_ID;
}
- for (int i = 0; i < 5; i++) {
+ for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) {
f->get_32(); //skip a few reserved fields
}
@@ -1209,8 +1210,8 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
fw->store_32(flags);
fw->store_64(uid_data);
- for (int i = 0; i < 5; i++) {
- f->store_32(0); // reserved
+ for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) {
+ fw->store_32(0); // reserved
f->get_32();
}
@@ -1264,7 +1265,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
if (using_uids) {
ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(full_path);
- f->store_64(uid);
+ fw->store_64(uid);
}
}
@@ -1902,7 +1903,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
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 < 5; i++) {
+ for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) {
f->store_32(0); // reserved
}
@@ -1959,8 +1960,8 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
Vector<RES> save_order;
save_order.resize(external_resources.size());
- for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) {
- save_order.write[E->get()] = E->key();
+ for (const KeyValue<RES, int> &E : external_resources) {
+ save_order.write[E.value] = E.key;
}
for (int i = 0; i < save_order.size(); i++) {
diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h
index ac964d2053..a6e6d1848e 100644
--- a/core/io/resource_format_binary.h
+++ b/core/io/resource_format_binary.h
@@ -164,6 +164,8 @@ 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, Map<RES, int> &resource_map, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint = PropertyInfo());
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index 1e166015b0..cd44c537a8 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -418,7 +418,7 @@ Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_extension(const St
}
String ResourceFormatImporter::get_import_base_path(const String &p_for_file) const {
- return ProjectSettings::IMPORTED_FILES_PATH.plus_file(p_for_file.get_file() + "-" + p_for_file.md5_text());
+ return ProjectSettings::get_singleton()->get_imported_files_path().plus_file(p_for_file.get_file() + "-" + p_for_file.md5_text());
}
bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) const {
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index d02d827443..2198761c2a 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -68,25 +68,28 @@ 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 {
- if (get_script_instance() && get_script_instance()->has_method("_get_resource_uid")) {
- return get_script_instance()->call("_get_resource_uid", p_path);
+ int64_t uid;
+ if (GDVIRTUAL_CALL(_get_resource_uid, p_path, uid)) {
+ return uid;
}
return ResourceUID::INVALID_ID;
@@ -105,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();
@@ -143,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 (KeyValue<String, String> E : p_map) {
+ 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;
- BIND_VMETHOD(info);
- }
-
- BIND_VMETHOD(MethodInfo(Variant::PACKED_STRING_ARRAY, "_get_recognized_extensions"));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_handles_type", PropertyInfo(Variant::STRING_NAME, "typename")));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_resource_type", PropertyInfo(Variant::STRING, "path")));
- BIND_VMETHOD(MethodInfo("_get_dependencies", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "add_types")));
- BIND_VMETHOD(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");
}
///////////////////////////////////
@@ -282,7 +278,7 @@ 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_rel_path()) {
+ } else if (p_path.is_relative_path()) {
return "res://" + p_path;
} else {
return ProjectSettings::get_singleton()->localize_path(p_path);
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index e525e80a9d..f1d9815635 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -32,6 +32,8 @@
#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"
@@ -48,6 +50,16 @@ public:
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;
diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp
index 564de5ee69..823b5f75b1 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -42,44 +42,37 @@ 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");
- BIND_VMETHOD(MethodInfo(Variant::INT, "_save", arg0, arg1, arg2));
- }
-
- BIND_VMETHOD(MethodInfo(Variant::PACKED_STRING_ARRAY, "_get_recognized_extensions", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
- BIND_VMETHOD(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) {
diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h
index 2fc8d32126..fcde835dab 100644
--- a/core/io/resource_saver.h
+++ b/core/io/resource_saver.h
@@ -32,6 +32,8 @@
#define RESOURCE_SAVER_H
#include "core/io/resource.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
class ResourceFormatSaver : public RefCounted {
GDCLASS(ResourceFormatSaver, RefCounted);
@@ -39,6 +41,10 @@ class ResourceFormatSaver : public 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;
diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp
index 97d683f415..b7d01712ff 100644
--- a/core/io/resource_uid.cpp
+++ b/core/io/resource_uid.cpp
@@ -29,6 +29,8 @@
/*************************************************************************/
#include "resource_uid.h"
+
+#include "core/config/project_settings.h"
#include "core/crypto/crypto.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
@@ -36,7 +38,9 @@
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::get_cache_file() {
+ return ProjectSettings::get_singleton()->get_project_data_path().plus_file("uid_cache.bin");
+}
String ResourceUID::id_to_text(ID p_id) const {
if (p_id < 0) {
@@ -126,8 +130,7 @@ 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;
+ return String::utf8(cs.ptr());
}
void ResourceUID::remove_id(ID p_id) {
MutexLock l(mutex);
@@ -136,12 +139,13 @@ void ResourceUID::remove_id(ID p_id) {
}
Error ResourceUID::save_to_cache() {
- if (!FileAccess::exists(CACHE_FILE)) {
+ String cache_file = get_cache_file();
+ 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
+ d->make_dir_recursive(String(cache_file).get_base_dir()); //ensure base dir exists
}
- FileAccessRef f = FileAccess::open(CACHE_FILE, FileAccess::WRITE);
+ FileAccessRef f = FileAccess::open(cache_file, FileAccess::WRITE);
if (!f) {
return ERR_CANT_OPEN;
}
@@ -165,7 +169,7 @@ Error ResourceUID::save_to_cache() {
}
Error ResourceUID::load_from_cache() {
- FileAccessRef f = FileAccess::open(CACHE_FILE, FileAccess::READ);
+ FileAccessRef f = FileAccess::open(get_cache_file(), FileAccess::READ);
if (!f) {
return ERR_CANT_OPEN;
}
@@ -207,7 +211,7 @@ Error ResourceUID::update_cache() {
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
+ f = FileAccess::open(get_cache_file(), FileAccess::READ_WRITE); //append
if (!f) {
return ERR_CANT_OPEN;
}
diff --git a/core/io/resource_uid.h b/core/io/resource_uid.h
index b12138425a..2f1bfdf243 100644
--- a/core/io/resource_uid.h
+++ b/core/io/resource_uid.h
@@ -44,7 +44,7 @@ public:
INVALID_ID = -1
};
- static const char *CACHE_FILE;
+ static String get_cache_file();
private:
mutable Ref<Crypto> crypto;
diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp
index 27f8d4e88f..8ab025dda1 100644
--- a/core/io/stream_peer.cpp
+++ b/core/io/stream_peer.cpp
@@ -410,6 +410,63 @@ void StreamPeer::_bind_methods() {
////////////////////////////////
+int StreamPeerExtension::get_available_bytes() const {
+ int count;
+ if (GDVIRTUAL_CALL(_get_available_bytes, count)) {
+ return count;
+ }
+ WARN_PRINT_ONCE("StreamPeerExtension::_get_available_bytes is unimplemented!");
+ return -1;
+}
+
+Error StreamPeerExtension::get_data(uint8_t *r_buffer, int p_bytes) {
+ int err;
+ int received = 0;
+ if (GDVIRTUAL_CALL(_get_data, r_buffer, p_bytes, &received, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("StreamPeerExtension::_get_data is unimplemented!");
+ return FAILED;
+}
+
+Error StreamPeerExtension::get_partial_data(uint8_t *r_buffer, int p_bytes, int &r_received) {
+ int err;
+ if (GDVIRTUAL_CALL(_get_partial_data, r_buffer, p_bytes, &r_received, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("StreamPeerExtension::_get_partial_data is unimplemented!");
+ return FAILED;
+}
+
+Error StreamPeerExtension::put_data(const uint8_t *p_data, int p_bytes) {
+ int err;
+ int sent = 0;
+ if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &sent, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("StreamPeerExtension::_put_data is unimplemented!");
+ return FAILED;
+}
+
+Error StreamPeerExtension::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
+ int err;
+ if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &r_sent, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("StreamPeerExtension::_put_partial_data is unimplemented!");
+ return FAILED;
+}
+
+void StreamPeerExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_get_data, "r_buffer", "r_bytes", "r_received");
+ GDVIRTUAL_BIND(_get_partial_data, "r_buffer", "r_bytes", "r_received");
+ GDVIRTUAL_BIND(_put_data, "p_data", "p_bytes", "r_sent");
+ GDVIRTUAL_BIND(_put_partial_data, "p_data", "p_bytes", "r_sent");
+ GDVIRTUAL_BIND(_get_available_bytes);
+}
+
+////////////////////////////////
+
void StreamPeerBuffer::_bind_methods() {
ClassDB::bind_method(D_METHOD("seek", "position"), &StreamPeerBuffer::seek);
ClassDB::bind_method(D_METHOD("get_size"), &StreamPeerBuffer::get_size);
diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h
index effc3850af..89432951c5 100644
--- a/core/io/stream_peer.h
+++ b/core/io/stream_peer.h
@@ -33,6 +33,10 @@
#include "core/object/ref_counted.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
+#include "core/variant/native_ptr.h"
+
class StreamPeer : public RefCounted {
GDCLASS(StreamPeer, RefCounted);
OBJ_CATEGORY("Networking");
@@ -58,6 +62,7 @@ public:
virtual int get_available_bytes() const = 0;
+ /* helpers */
void set_big_endian(bool p_big_endian);
bool is_big_endian_enabled() const;
@@ -92,6 +97,26 @@ public:
StreamPeer() {}
};
+class StreamPeerExtension : public StreamPeer {
+ GDCLASS(StreamPeerExtension, StreamPeer);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual Error put_data(const uint8_t *p_data, int p_bytes) override;
+ virtual Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override;
+ virtual Error get_data(uint8_t *p_buffer, int p_bytes) override;
+ virtual Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override;
+ virtual int get_available_bytes() const override;
+
+ GDVIRTUAL3R(int, _put_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>);
+ GDVIRTUAL3R(int, _put_partial_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>);
+ GDVIRTUAL3R(int, _get_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>);
+ GDVIRTUAL3R(int, _get_partial_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>);
+ GDVIRTUAL0RC(int, _get_available_bytes);
+};
+
class StreamPeerBuffer : public StreamPeer {
GDCLASS(StreamPeerBuffer, StreamPeer);
diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp
index 88e11a630c..d59dbf1ba8 100644
--- a/core/math/a_star.cpp
+++ b/core/math/a_star.cpp
@@ -35,26 +35,20 @@
#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;
}
void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) {
- ERR_FAIL_COND(p_id < 0);
- ERR_FAIL_COND(p_weight_scale < 1);
+ ERR_FAIL_COND_MSG(p_id < 0, vformat("Can't add a point with negative id: %d.", p_id));
+ ERR_FAIL_COND_MSG(p_weight_scale < 1, vformat("Can't add a point with weight scale less than one: %f.", p_weight_scale));
Point *found_pt;
bool p_exists = points.lookup(p_id, found_pt);
@@ -78,7 +72,7 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) {
Vector3 AStar::get_point_position(int p_id) const {
Point *p;
bool p_exists = points.lookup(p_id, p);
- ERR_FAIL_COND_V(!p_exists, Vector3());
+ ERR_FAIL_COND_V_MSG(!p_exists, Vector3(), vformat("Can't get point's position. Point with id: %d doesn't exist.", p_id));
return p->pos;
}
@@ -86,7 +80,7 @@ Vector3 AStar::get_point_position(int p_id) const {
void AStar::set_point_position(int p_id, const Vector3 &p_pos) {
Point *p;
bool p_exists = points.lookup(p_id, p);
- ERR_FAIL_COND(!p_exists);
+ ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set point's position. Point with id: %d doesn't exist.", p_id));
p->pos = p_pos;
}
@@ -94,7 +88,7 @@ void AStar::set_point_position(int p_id, const Vector3 &p_pos) {
real_t AStar::get_point_weight_scale(int p_id) const {
Point *p;
bool p_exists = points.lookup(p_id, p);
- ERR_FAIL_COND_V(!p_exists, 0);
+ ERR_FAIL_COND_V_MSG(!p_exists, 0, vformat("Can't get point's weight scale. Point with id: %d doesn't exist.", p_id));
return p->weight_scale;
}
@@ -102,8 +96,8 @@ real_t AStar::get_point_weight_scale(int p_id) const {
void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) {
Point *p;
bool p_exists = points.lookup(p_id, p);
- ERR_FAIL_COND(!p_exists);
- ERR_FAIL_COND(p_weight_scale < 1);
+ ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set point's weight scale. Point with id: %d doesn't exist.", p_id));
+ ERR_FAIL_COND_MSG(p_weight_scale < 1, vformat("Can't set point's weight scale less than one: %f.", p_weight_scale));
p->weight_scale = p_weight_scale;
}
@@ -111,7 +105,7 @@ void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) {
void AStar::remove_point(int p_id) {
Point *p;
bool p_exists = points.lookup(p_id, p);
- ERR_FAIL_COND(!p_exists);
+ ERR_FAIL_COND_MSG(!p_exists, vformat("Can't remove point. Point with id: %d doesn't exist.", p_id));
for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
Segment s(p_id, (*it.key));
@@ -135,15 +129,15 @@ void AStar::remove_point(int p_id) {
}
void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) {
- ERR_FAIL_COND(p_id == p_with_id);
+ ERR_FAIL_COND_MSG(p_id == p_with_id, vformat("Can't connect point with id: %d to itself.", p_id));
Point *a;
bool from_exists = points.lookup(p_id, a);
- ERR_FAIL_COND(!from_exists);
+ ERR_FAIL_COND_MSG(!from_exists, vformat("Can't connect points. Point with id: %d doesn't exist.", p_id));
Point *b;
bool to_exists = points.lookup(p_with_id, b);
- ERR_FAIL_COND(!to_exists);
+ ERR_FAIL_COND_MSG(!to_exists, vformat("Can't connect points. Point with id: %d doesn't exist.", p_with_id));
a->neighbours.set(b->id, b);
@@ -175,11 +169,11 @@ void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) {
void AStar::disconnect_points(int p_id, int p_with_id, bool bidirectional) {
Point *a;
bool a_exists = points.lookup(p_id, a);
- ERR_FAIL_COND(!a_exists);
+ ERR_FAIL_COND_MSG(!a_exists, vformat("Can't disconnect points. Point with id: %d doesn't exist.", p_id));
Point *b;
bool b_exists = points.lookup(p_with_id, b);
- ERR_FAIL_COND(!b_exists);
+ ERR_FAIL_COND_MSG(!b_exists, vformat("Can't disconnect points. Point with id: %d doesn't exist.", p_with_id));
Segment s(p_id, p_with_id);
int remove_direction = bidirectional ? (int)Segment::BIDIRECTIONAL : s.direction;
@@ -229,7 +223,7 @@ Array AStar::get_points() {
Vector<int> AStar::get_point_connections(int p_id) {
Point *p;
bool p_exists = points.lookup(p_id, p);
- ERR_FAIL_COND_V(!p_exists, Vector<int>());
+ ERR_FAIL_COND_V_MSG(!p_exists, Vector<int>(), vformat("Can't get point's connections. Point with id: %d doesn't exist.", p_id));
Vector<int> point_list;
@@ -266,8 +260,8 @@ int AStar::get_point_capacity() const {
}
void AStar::reserve_space(int p_num_nodes) {
- ERR_FAIL_COND_MSG(p_num_nodes <= 0, "New capacity must be greater than 0, was: " + itos(p_num_nodes) + ".");
- ERR_FAIL_COND_MSG((uint32_t)p_num_nodes < points.get_capacity(), "New capacity must be greater than current capacity: " + itos(points.get_capacity()) + ", new was: " + itos(p_num_nodes) + ".");
+ ERR_FAIL_COND_MSG(p_num_nodes <= 0, vformat("New capacity must be greater than 0, new was: %d.", p_num_nodes));
+ ERR_FAIL_COND_MSG((uint32_t)p_num_nodes < points.get_capacity(), vformat("New capacity must be greater than current capacity: %d, new was: %d.", points.get_capacity(), p_num_nodes));
points.reserve(p_num_nodes);
}
@@ -388,33 +382,35 @@ 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;
bool from_exists = points.lookup(p_from_id, from_point);
- ERR_FAIL_COND_V(!from_exists, 0);
+ ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_from_id));
Point *to_point;
bool to_exists = points.lookup(p_to_id, to_point);
- ERR_FAIL_COND_V(!to_exists, 0);
+ ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_to_id));
return from_point->pos.distance_to(to_point->pos);
}
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;
bool from_exists = points.lookup(p_from_id, from_point);
- ERR_FAIL_COND_V(!from_exists, 0);
+ ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_from_id));
Point *to_point;
bool to_exists = points.lookup(p_to_id, to_point);
- ERR_FAIL_COND_V(!to_exists, 0);
+ ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_to_id));
return from_point->pos.distance_to(to_point->pos);
}
@@ -422,11 +418,11 @@ real_t AStar::_compute_cost(int p_from_id, int p_to_id) {
Vector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) {
Point *a;
bool from_exists = points.lookup(p_from_id, a);
- ERR_FAIL_COND_V(!from_exists, Vector<Vector3>());
+ ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id));
Point *b;
bool to_exists = points.lookup(p_to_id, b);
- ERR_FAIL_COND_V(!to_exists, Vector<Vector3>());
+ ERR_FAIL_COND_V_MSG(!to_exists, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_to_id));
if (a == b) {
Vector<Vector3> ret;
@@ -471,11 +467,11 @@ Vector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) {
Vector<int> AStar::get_id_path(int p_from_id, int p_to_id) {
Point *a;
bool from_exists = points.lookup(p_from_id, a);
- ERR_FAIL_COND_V(!from_exists, Vector<int>());
+ ERR_FAIL_COND_V_MSG(!from_exists, Vector<int>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id));
Point *b;
bool to_exists = points.lookup(p_to_id, b);
- ERR_FAIL_COND_V(!to_exists, Vector<int>());
+ ERR_FAIL_COND_V_MSG(!to_exists, Vector<int>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_to_id));
if (a == b) {
Vector<int> ret;
@@ -520,7 +516,7 @@ Vector<int> AStar::get_id_path(int p_from_id, int p_to_id) {
void AStar::set_point_disabled(int p_id, bool p_disabled) {
Point *p;
bool p_exists = points.lookup(p_id, p);
- ERR_FAIL_COND(!p_exists);
+ ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set if point is disabled. Point with id: %d doesn't exist.", p_id));
p->enabled = !p_disabled;
}
@@ -528,7 +524,7 @@ void AStar::set_point_disabled(int p_id, bool p_disabled) {
bool AStar::is_point_disabled(int p_id) const {
Point *p;
bool p_exists = points.lookup(p_id, p);
- ERR_FAIL_COND_V(!p_exists, false);
+ ERR_FAIL_COND_V_MSG(!p_exists, false, vformat("Can't get if point is disabled. Point with id: %d doesn't exist.", p_id));
return !p->enabled;
}
@@ -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,33 +656,35 @@ 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;
bool from_exists = astar.points.lookup(p_from_id, from_point);
- ERR_FAIL_COND_V(!from_exists, 0);
+ ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_from_id));
AStar::Point *to_point;
bool to_exists = astar.points.lookup(p_to_id, to_point);
- ERR_FAIL_COND_V(!to_exists, 0);
+ ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_to_id));
return from_point->pos.distance_to(to_point->pos);
}
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;
bool from_exists = astar.points.lookup(p_from_id, from_point);
- ERR_FAIL_COND_V(!from_exists, 0);
+ ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_from_id));
AStar::Point *to_point;
bool to_exists = astar.points.lookup(p_to_id, to_point);
- ERR_FAIL_COND_V(!to_exists, 0);
+ ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_to_id));
return from_point->pos.distance_to(to_point->pos);
}
@@ -694,11 +692,11 @@ real_t AStar2D::_compute_cost(int p_from_id, int p_to_id) {
Vector<Vector2> AStar2D::get_point_path(int p_from_id, int p_to_id) {
AStar::Point *a;
bool from_exists = astar.points.lookup(p_from_id, a);
- ERR_FAIL_COND_V(!from_exists, Vector<Vector2>());
+ ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id));
AStar::Point *b;
bool to_exists = astar.points.lookup(p_to_id, b);
- ERR_FAIL_COND_V(!to_exists, Vector<Vector2>());
+ ERR_FAIL_COND_V_MSG(!to_exists, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_to_id));
if (a == b) {
Vector<Vector2> ret;
@@ -743,11 +741,11 @@ Vector<Vector2> AStar2D::get_point_path(int p_from_id, int p_to_id) {
Vector<int> AStar2D::get_id_path(int p_from_id, int p_to_id) {
AStar::Point *a;
bool from_exists = astar.points.lookup(p_from_id, a);
- ERR_FAIL_COND_V(!from_exists, Vector<int>());
+ ERR_FAIL_COND_V_MSG(!from_exists, Vector<int>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id));
AStar::Point *b;
bool to_exists = astar.points.lookup(p_to_id, b);
- ERR_FAIL_COND_V(!to_exists, Vector<int>());
+ ERR_FAIL_COND_V_MSG(!to_exists, Vector<int>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_to_id));
if (a == b) {
Vector<int> ret;
@@ -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 44758cb046..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/gdvirtual.gen.inc"
#include "core/object/ref_counted.h"
+#include "core/object/script_language.h"
#include "core/templates/oa_hash_map.h"
/**
@@ -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;
@@ -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 33aa65f15d..51a1309f0e 100644
--- a/core/math/aabb.cpp
+++ b/core/math/aabb.cpp
@@ -52,8 +52,8 @@ void AABB::merge_with(const AABB &p_aabb) {
beg_1 = position;
beg_2 = p_aabb.position;
- end_1 = Vector3(size.x, size.y, size.z) + beg_1;
- end_2 = Vector3(p_aabb.size.x, p_aabb.size.y, p_aabb.size.z) + beg_2;
+ end_1 = size + beg_1;
+ end_2 = p_aabb.size + beg_2;
min.x = (beg_1.x < beg_2.x) ? beg_1.x : beg_2.x;
min.y = (beg_1.y < beg_2.y) ? beg_1.y : beg_2.y;
diff --git a/core/math/aabb.h b/core/math/aabb.h
index e16246902a..97d92fbe37 100644
--- a/core/math/aabb.h
+++ b/core/math/aabb.h
@@ -118,6 +118,10 @@ public:
return position + size;
}
+ _FORCE_INLINE_ Vector3 get_center() const {
+ return position + (size * 0.5);
+ }
+
operator String() const;
_FORCE_INLINE_ AABB() {}
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index aa3831d4cf..a7f89522d7 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -207,6 +207,10 @@ Basis Basis::transposed() const {
return tr;
}
+Basis Basis::from_scale(const Vector3 &p_scale) {
+ return Basis(p_scale.x, 0, 0, 0, p_scale.y, 0, 0, 0, p_scale.z);
+}
+
// Multiplies the matrix from left by the scaling matrix: M -> S.M
// See the comment for Basis::rotated for further explanation.
void Basis::scale(const Vector3 &p_scale) {
@@ -246,10 +250,7 @@ void Basis::make_scale_uniform() {
}
Basis Basis::scaled_local(const Vector3 &p_scale) const {
- Basis b;
- b.set_diagonal(p_scale);
-
- return (*this) * b;
+ return (*this) * Basis::from_scale(p_scale);
}
Vector3 Basis::get_scale_abs() const {
@@ -381,6 +382,18 @@ Quaternion Basis::get_rotation_quaternion() const {
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 {
// 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().
@@ -763,7 +776,7 @@ Basis::operator String() const {
Quaternion Basis::get_quaternion() const {
#ifdef MATH_CHECKS
- 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() 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;
@@ -979,21 +992,23 @@ void Basis::set_axis_angle(const Vector3 &p_axis, real_t p_phi) {
}
void Basis::set_axis_angle_scale(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) {
- set_diagonal(p_scale);
+ _set_diagonal(p_scale);
rotate(p_axis, p_phi);
}
void Basis::set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale) {
- set_diagonal(p_scale);
+ _set_diagonal(p_scale);
rotate(p_euler);
}
void Basis::set_quaternion_scale(const Quaternion &p_quaternion, const Vector3 &p_scale) {
- set_diagonal(p_scale);
+ _set_diagonal(p_scale);
rotate(p_quaternion);
}
-void Basis::set_diagonal(const Vector3 &p_diag) {
+// This also sets the non-diagonal elements to 0, which is misleading from the
+// name, so we want this method to be private. Use `from_scale` externally.
+void Basis::_set_diagonal(const Vector3 &p_diag) {
elements[0][0] = p_diag.x;
elements[0][1] = 0;
elements[0][2] = 0;
@@ -1129,3 +1144,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 2889a4aa5e..eb107d7e4e 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -35,6 +35,9 @@
#include "core/math/vector3.h"
class Basis {
+private:
+ void _set_diagonal(const Vector3 &p_diag);
+
public:
Vector3 elements[3] = {
Vector3(1, 0, 0),
@@ -88,6 +91,8 @@ public:
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;
@@ -164,8 +169,6 @@ public:
int get_orthogonal_index() const;
void set_orthogonal_index(int p_index);
- void set_diagonal(const Vector3 &p_diag);
-
bool is_orthogonal() const;
bool is_diagonal() const;
bool is_rotation() const;
@@ -242,6 +245,8 @@ public:
operator Quaternion() const { return get_quaternion(); }
+ static Basis looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0));
+
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); }
@@ -250,6 +255,7 @@ public:
Basis(const Vector3 &p_axis, real_t p_phi) { set_axis_angle(p_axis, p_phi); }
Basis(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { set_axis_angle_scale(p_axis, p_phi, p_scale); }
+ static Basis from_scale(const Vector3 &p_scale);
_FORCE_INLINE_ Basis(const Vector3 &row0, const Vector3 &row1, const Vector3 &row2) {
elements[0] = row0;
diff --git a/core/math/bvh_cull.inc b/core/math/bvh_cull.inc
index cba8ea6cb3..d7edc8a884 100644
--- a/core/math/bvh_cull.inc
+++ b/core/math/bvh_cull.inc
@@ -14,7 +14,7 @@ struct CullParams {
uint32_t pairable_type;
// optional components for different tests
- Vector3 point;
+ Point point;
BVHABB_CLASS abb;
typename BVHABB_CLASS::ConvexHull hull;
typename BVHABB_CLASS::Segment segment;
diff --git a/core/math/bvh_debug.inc b/core/math/bvh_debug.inc
index a97304334c..55db794ee3 100644
--- a/core/math/bvh_debug.inc
+++ b/core/math/bvh_debug.inc
@@ -6,24 +6,21 @@ void _debug_recursive_print_tree(int p_tree_id) const {
}
String _debug_aabb_to_string(const BVHABB_CLASS &aabb) const {
- String sz = "(";
- sz += itos(aabb.min.x);
- sz += " ~ ";
- sz += itos(-aabb.neg_max.x);
- sz += ") (";
+ Point size = aabb.calculate_size();
- sz += itos(aabb.min.y);
- sz += " ~ ";
- sz += itos(-aabb.neg_max.y);
- sz += ") (";
+ String sz;
+ float vol = 0.0;
- sz += itos(aabb.min.z);
- sz += " ~ ";
- sz += itos(-aabb.neg_max.z);
- sz += ") ";
+ for (int i = 0; i < Point::AXES_COUNT; ++i) {
+ sz += "(";
+ sz += itos(aabb.min[i]);
+ sz += " ~ ";
+ sz += itos(-aabb.neg_max[i]);
+ sz += ") ";
+
+ vol += size[i];
+ }
- Vector3 size = aabb.calculate_size();
- float vol = size.x * size.y * size.z;
sz += "vol " + itos(vol);
return sz;
diff --git a/core/math/bvh_split.inc b/core/math/bvh_split.inc
index 3fcc4c7b10..6f54d06ce7 100644
--- a/core/math/bvh_split.inc
+++ b/core/math/bvh_split.inc
@@ -28,11 +28,15 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
Point centre = full_bound.calculate_centre();
Point size = full_bound.calculate_size();
- int order[3];
+ int order[Point::AXIS_COUNT];
order[0] = size.min_axis();
- order[2] = size.max_axis();
- order[1] = 3 - (order[0] + order[2]);
+ order[Point::AXIS_COUNT - 1] = size.max_axis();
+
+ static_assert(Point::AXIS_COUNT <= 3);
+ if (Point::AXIS_COUNT == 3) {
+ order[1] = 3 - (order[0] + order[2]);
+ }
// simplest case, split on the longest axis
int split_axis = order[0];
@@ -54,7 +58,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
// detect when split on longest axis failed
int min_threshold = MAX_ITEMS / 4;
- int min_group_size[3];
+ int min_group_size[Point::AXIS_COUNT];
min_group_size[0] = MIN(num_a, num_b);
if (min_group_size[0] < min_threshold) {
// slow but sure .. first move everything back into a
@@ -64,7 +68,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
num_b = 0;
// now calculate the best split
- for (int axis = 1; axis < 3; axis++) {
+ for (int axis = 1; axis < Point::AXIS_COUNT; axis++) {
split_axis = order[axis];
int count = 0;
@@ -82,7 +86,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
// best axis
int best_axis = 0;
int best_min = min_group_size[0];
- for (int axis = 1; axis < 3; axis++) {
+ for (int axis = 1; axis < Point::AXIS_COUNT; axis++) {
if (min_group_size[axis] > best_min) {
best_min = min_group_size[axis];
best_axis = axis;
diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp
index 66c18f7b3c..8066a59281 100644
--- a/core/math/camera_matrix.cpp
+++ b/core/math/camera_matrix.cpp
@@ -341,8 +341,8 @@ bool CameraMatrix::get_endpoints(const Transform3D &p_transform, Vector3 *p_8poi
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;
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_2d.h b/core/math/delaunay_2d.h
index 95064e5700..2f80cb5634 100644
--- a/core/math/delaunay_2d.h
+++ b/core/math/delaunay_2d.h
@@ -101,7 +101,7 @@ public:
}
float delta_max = MAX(rect.size.width, rect.size.height);
- Vector2 center = rect.position + rect.size * 0.5;
+ Vector2 center = rect.get_center();
points.push_back(Vector2(center.x - 20 * delta_max, center.y - delta_max));
points.push_back(Vector2(center.x, center.y + 20 * delta_max));
diff --git a/core/math/face3.cpp b/core/math/face3.cpp
index 9af3f868d2..045ab67ce8 100644
--- a/core/math/face3.cpp
+++ b/core/math/face3.cpp
@@ -151,8 +151,8 @@ Face3::Side Face3::get_side_of(const Face3 &p_face, ClockDirection p_clock_dir)
}
Vector3 Face3::get_random_point_inside() const {
- real_t a = Math::random(0, 1);
- real_t b = Math::random(0, 1);
+ real_t a = Math::random(0.0, 1.0);
+ real_t b = Math::random(0.0, 1.0);
if (a > b) {
SWAP(a, b);
}
diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h
index a2894bc1d3..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,8 +361,14 @@ 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;
+ }
}
}
diff --git a/core/math/math_defs.h b/core/math/math_defs.h
index 7692e1be47..c3a8f910c0 100644
--- a/core/math/math_defs.h
+++ b/core/math/math_defs.h
@@ -81,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/quick_hull.cpp b/core/math/quick_hull.cpp
index 0960fe19a6..d438a9a377 100644
--- a/core/math/quick_hull.cpp
+++ b/core/math/quick_hull.cpp
@@ -265,8 +265,8 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
//create new faces from horizon edges
List<List<Face>::Element *> new_faces; //new faces
- for (Map<Edge, FaceConnect>::Element *E = lit_edges.front(); E; E = E->next()) {
- FaceConnect &fc = E->get();
+ for (KeyValue<Edge, FaceConnect> &E : lit_edges) {
+ FaceConnect &fc = E.value;
if (fc.left && fc.right) {
continue; //edge is uninteresting, not on horizon
}
@@ -275,8 +275,8 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
Face face;
face.vertices[0] = f.points_over[next];
- face.vertices[1] = E->key().vertices[0];
- face.vertices[2] = E->key().vertices[1];
+ face.vertices[1] = E.key.vertices[0];
+ face.vertices[2] = E.key.vertices[1];
Plane p(p_points[face.vertices[0]], p_points[face.vertices[1]], p_points[face.vertices[2]]);
@@ -418,13 +418,13 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
}
// remove all edge connections to this face
- for (Map<Edge, RetFaceConnect>::Element *G = ret_edges.front(); G; G = G->next()) {
- if (G->get().left == O) {
- G->get().left = nullptr;
+ for (KeyValue<Edge, RetFaceConnect> &G : ret_edges) {
+ if (G.value.left == O) {
+ G.value.left = nullptr;
}
- if (G->get().right == O) {
- G->get().right = nullptr;
+ if (G.value.right == O) {
+ G.value.right = nullptr;
}
}
@@ -444,10 +444,10 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
}
r_mesh.edges.resize(ret_edges.size());
idx = 0;
- for (Map<Edge, RetFaceConnect>::Element *E = ret_edges.front(); E; E = E->next()) {
+ for (const KeyValue<Edge, RetFaceConnect> &E : ret_edges) {
Geometry3D::MeshData::Edge e;
- e.a = E->key().vertices[0];
- e.b = E->key().vertices[1];
+ e.a = E.key.vertices[0];
+ e.b = E.key.vertices[1];
r_mesh.edges.write[idx++] = e;
}
diff --git a/core/math/rect2.h b/core/math/rect2.h
index ab0b489b4a..2557959fa2 100644
--- a/core/math/rect2.h
+++ b/core/math/rect2.h
@@ -46,6 +46,8 @@ struct Rect2 {
real_t get_area() const { return size.width * size.height; }
+ _FORCE_INLINE_ Vector2 get_center() const { return position + (size * 0.5); }
+
inline bool intersects(const Rect2 &p_rect, const bool p_include_borders = false) const {
if (p_include_borders) {
if (position.x > (p_rect.position.x + p_rect.size.width)) {
@@ -259,7 +261,7 @@ struct Rect2 {
}
_FORCE_INLINE_ bool intersects_filled_polygon(const Vector2 *p_points, int p_point_count) const {
- Vector2 center = position + size * 0.5;
+ Vector2 center = get_center();
int side_plus = 0;
int side_minus = 0;
Vector2 end = position + size;
@@ -344,6 +346,8 @@ struct Rect2i {
int get_area() const { return size.width * size.height; }
+ _FORCE_INLINE_ Vector2i get_center() const { return position + (size / 2); }
+
inline bool intersects(const Rect2i &p_rect) const {
if (position.x > (p_rect.position.x + p_rect.size.width)) {
return false;
diff --git a/core/math/static_raycaster.cpp b/core/math/static_raycaster.cpp
new file mode 100644
index 0000000000..da05d49428
--- /dev/null
+++ b/core/math/static_raycaster.cpp
@@ -0,0 +1,40 @@
+/*************************************************************************/
+/* static_raycaster.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 "static_raycaster.h"
+
+StaticRaycaster *(*StaticRaycaster::create_function)() = nullptr;
+
+Ref<StaticRaycaster> StaticRaycaster::create() {
+ if (create_function) {
+ return Ref<StaticRaycaster>(create_function());
+ }
+ return Ref<StaticRaycaster>();
+}
diff --git a/core/math/static_raycaster.h b/core/math/static_raycaster.h
new file mode 100644
index 0000000000..3759c788a7
--- /dev/null
+++ b/core/math/static_raycaster.h
@@ -0,0 +1,111 @@
+/*************************************************************************/
+/* static_raycaster.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 STATIC_RAYCASTER_H
+#define STATIC_RAYCASTER_H
+
+#include "core/object/ref_counted.h"
+
+#if !defined(__aligned)
+
+#if defined(_WIN32) && defined(_MSC_VER)
+#define __aligned(...) __declspec(align(__VA_ARGS__))
+#else
+#define __aligned(...) __attribute__((aligned(__VA_ARGS__)))
+#endif
+
+#endif
+
+class StaticRaycaster : public RefCounted {
+ GDCLASS(StaticRaycaster, RefCounted)
+protected:
+ static StaticRaycaster *(*create_function)();
+
+public:
+ // compatible with embree3 rays
+ struct __aligned(16) Ray {
+ const static unsigned int INVALID_GEOMETRY_ID = ((unsigned int)-1); // from rtcore_common.h
+
+ /*! Default construction does nothing. */
+ _FORCE_INLINE_ Ray() :
+ geomID(INVALID_GEOMETRY_ID) {}
+
+ /*! Constructs a ray from origin, direction, and ray segment. Near
+ * has to be smaller than far. */
+ _FORCE_INLINE_ Ray(const Vector3 &org,
+ const Vector3 &dir,
+ float tnear = 0.0f,
+ float tfar = INFINITY) :
+ org(org),
+ tnear(tnear),
+ dir(dir),
+ time(0.0f),
+ tfar(tfar),
+ mask(-1),
+ u(0.0),
+ v(0.0),
+ primID(INVALID_GEOMETRY_ID),
+ geomID(INVALID_GEOMETRY_ID),
+ instID(INVALID_GEOMETRY_ID) {}
+
+ /*! Tests if we hit something. */
+ _FORCE_INLINE_ explicit operator bool() const { return geomID != INVALID_GEOMETRY_ID; }
+
+ public:
+ Vector3 org; //!< Ray origin + tnear
+ float tnear; //!< Start of ray segment
+ Vector3 dir; //!< Ray direction + tfar
+ float time; //!< Time of this ray for motion blur.
+ float tfar; //!< End of ray segment
+ unsigned int mask; //!< used to mask out objects during traversal
+ unsigned int id; //!< ray ID
+ unsigned int flags; //!< ray flags
+
+ Vector3 normal; //!< Not normalized geometry normal
+ float u; //!< Barycentric u coordinate of hit
+ float v; //!< Barycentric v coordinate of hit
+ unsigned int primID; //!< primitive ID
+ unsigned int geomID; //!< geometry ID
+ unsigned int instID; //!< instance ID
+ };
+
+ virtual bool intersect(Ray &p_ray) = 0;
+ virtual void intersect(Vector<Ray> &r_rays) = 0;
+
+ virtual void add_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, unsigned int p_id) = 0;
+ virtual void commit() = 0;
+
+ virtual void set_mesh_filter(const Set<int> &p_mesh_ids) = 0;
+ virtual void clear_mesh_filter() = 0;
+
+ static Ref<StaticRaycaster> create();
+};
+
+#endif // STATIC_RAYCASTER_H
diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp
index 16934d67df..496a557844 100644
--- a/core/math/transform_2d.cpp
+++ b/core/math/transform_2d.cpp
@@ -63,7 +63,7 @@ Transform2D Transform2D::affine_inverse() const {
return inv;
}
-void Transform2D::rotate(real_t p_phi) {
+void Transform2D::rotate(const real_t p_phi) {
*this = Transform2D(p_phi, Vector2()) * (*this);
}
@@ -72,7 +72,7 @@ real_t Transform2D::get_skew() const {
return Math::acos(elements[0].normalized().dot(SGN(det) * elements[1].normalized())) - Math_PI * 0.5;
}
-void Transform2D::set_skew(float p_angle) {
+void Transform2D::set_skew(const real_t p_angle) {
real_t det = basis_determinant();
elements[1] = SGN(det) * elements[0].rotated((Math_PI * 0.5 + p_angle)).normalized() * elements[1].length();
}
@@ -81,7 +81,7 @@ real_t Transform2D::get_rotation() const {
return Math::atan2(elements[0].y, elements[0].x);
}
-void Transform2D::set_rotation(real_t p_rot) {
+void Transform2D::set_rotation(const real_t p_rot) {
Size2 scale = get_scale();
real_t cr = Math::cos(p_rot);
real_t sr = Math::sin(p_rot);
@@ -92,7 +92,7 @@ void Transform2D::set_rotation(real_t p_rot) {
set_scale(scale);
}
-Transform2D::Transform2D(real_t p_rot, const Vector2 &p_pos) {
+Transform2D::Transform2D(const real_t p_rot, const Vector2 &p_pos) {
real_t cr = Math::cos(p_rot);
real_t sr = Math::sin(p_rot);
elements[0][0] = cr;
@@ -102,6 +102,14 @@ Transform2D::Transform2D(real_t p_rot, const Vector2 &p_pos) {
elements[2] = p_pos;
}
+Transform2D::Transform2D(const real_t p_rot, const Size2 &p_scale, const real_t p_skew, const Vector2 &p_pos) {
+ elements[0][0] = Math::cos(p_rot) * p_scale.x;
+ elements[1][1] = Math::cos(p_rot + p_skew) * p_scale.y;
+ elements[1][0] = -Math::sin(p_rot + p_skew) * p_scale.y;
+ elements[0][1] = Math::sin(p_rot) * p_scale.x;
+ elements[2] = p_pos;
+}
+
Size2 Transform2D::get_scale() const {
real_t det_sign = SGN(basis_determinant());
return Size2(elements[0].length(), det_sign * elements[1].length());
@@ -126,7 +134,7 @@ void Transform2D::scale_basis(const Size2 &p_scale) {
elements[1][1] *= p_scale.y;
}
-void Transform2D::translate(real_t p_tx, real_t p_ty) {
+void Transform2D::translate(const real_t p_tx, const real_t p_ty) {
translate(Vector2(p_tx, p_ty));
}
@@ -231,7 +239,7 @@ Transform2D Transform2D::translated(const Vector2 &p_offset) const {
return copy;
}
-Transform2D Transform2D::rotated(real_t p_phi) const {
+Transform2D Transform2D::rotated(const real_t p_phi) const {
Transform2D copy = *this;
copy.rotate(p_phi);
return copy;
@@ -241,7 +249,7 @@ real_t Transform2D::basis_determinant() const {
return elements[0].x * elements[1].y - elements[0].y * elements[1].x;
}
-Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, real_t p_c) const {
+Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, const real_t p_c) const {
//extract parameters
Vector2 p1 = get_origin();
Vector2 p2 = p_transform.get_origin();
@@ -271,7 +279,7 @@ Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, real_t
}
//construct matrix
- Transform2D res(Math::atan2(v.y, v.x), p1.lerp(p2, p_c));
+ Transform2D res(v.angle(), p1.lerp(p2, p_c));
res.scale_basis(s1.lerp(s2, p_c));
return res;
}
diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h
index 34cfd0c1a9..6ed3af2ba7 100644
--- a/core/math/transform_2d.h
+++ b/core/math/transform_2d.h
@@ -68,17 +68,17 @@ struct Transform2D {
void affine_invert();
Transform2D affine_inverse() const;
- void set_rotation(real_t p_rot);
+ void set_rotation(const real_t p_rot);
real_t get_rotation() const;
real_t get_skew() const;
- void set_skew(float p_angle);
- _FORCE_INLINE_ void set_rotation_and_scale(real_t p_rot, const Size2 &p_scale);
- _FORCE_INLINE_ void set_rotation_scale_and_skew(real_t p_rot, const Size2 &p_scale, float p_skew);
- void rotate(real_t p_phi);
+ void set_skew(const real_t p_angle);
+ _FORCE_INLINE_ void set_rotation_and_scale(const real_t p_rot, const Size2 &p_scale);
+ _FORCE_INLINE_ void set_rotation_scale_and_skew(const real_t p_rot, const Size2 &p_scale, const real_t p_skew);
+ void rotate(const real_t p_phi);
void scale(const Size2 &p_scale);
void scale_basis(const Size2 &p_scale);
- void translate(real_t p_tx, real_t p_ty);
+ void translate(const real_t p_tx, const real_t p_ty);
void translate(const Vector2 &p_translation);
real_t basis_determinant() const;
@@ -92,7 +92,7 @@ struct Transform2D {
Transform2D scaled(const Size2 &p_scale) const;
Transform2D basis_scaled(const Size2 &p_scale) const;
Transform2D translated(const Vector2 &p_offset) const;
- Transform2D rotated(real_t p_phi) const;
+ Transform2D rotated(const real_t p_phi) const;
Transform2D untranslated() const;
@@ -110,7 +110,7 @@ struct Transform2D {
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;
+ Transform2D interpolate_with(const Transform2D &p_transform, const real_t p_c) const;
_FORCE_INLINE_ Vector2 basis_xform(const Vector2 &p_vec) const;
_FORCE_INLINE_ Vector2 basis_xform_inv(const Vector2 &p_vec) const;
@@ -123,7 +123,7 @@ struct Transform2D {
operator String() const;
- Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) {
+ Transform2D(const real_t xx, const real_t xy, const real_t yx, const real_t yy, const real_t ox, const real_t oy) {
elements[0][0] = xx;
elements[0][1] = xy;
elements[1][0] = yx;
@@ -138,7 +138,10 @@ struct Transform2D {
elements[2] = p_origin;
}
- Transform2D(real_t p_rot, const Vector2 &p_pos);
+ Transform2D(const real_t p_rot, const Vector2 &p_pos);
+
+ Transform2D(const real_t p_rot, const Size2 &p_scale, const real_t p_skew, const Vector2 &p_pos);
+
Transform2D() {
elements[0][0] = 1.0;
elements[1][1] = 1.0;
@@ -185,14 +188,14 @@ Rect2 Transform2D::xform(const Rect2 &p_rect) const {
return new_rect;
}
-void Transform2D::set_rotation_and_scale(real_t p_rot, const Size2 &p_scale) {
+void Transform2D::set_rotation_and_scale(const real_t p_rot, const Size2 &p_scale) {
elements[0][0] = Math::cos(p_rot) * p_scale.x;
elements[1][1] = Math::cos(p_rot) * p_scale.y;
elements[1][0] = -Math::sin(p_rot) * p_scale.y;
elements[0][1] = Math::sin(p_rot) * p_scale.x;
}
-void Transform2D::set_rotation_scale_and_skew(real_t p_rot, const Size2 &p_scale, float p_skew) {
+void Transform2D::set_rotation_scale_and_skew(const real_t p_rot, const Size2 &p_scale, const real_t p_skew) {
elements[0][0] = Math::cos(p_rot) * p_scale.x;
elements[1][1] = Math::cos(p_rot + p_skew) * p_scale.y;
elements[1][0] = -Math::sin(p_rot + p_skew) * p_scale.y;
diff --git a/core/math/transform_3d.cpp b/core/math/transform_3d.cpp
index 51766b39f4..4f4943c8ef 100644
--- a/core/math/transform_3d.cpp
+++ b/core/math/transform_3d.cpp
@@ -71,40 +71,12 @@ void Transform3D::rotate_basis(const Vector3 &p_axis, real_t p_phi) {
Transform3D Transform3D::looking_at(const Vector3 &p_target, const Vector3 &p_up) const {
Transform3D t = *this;
- t.set_look_at(origin, p_target, p_up);
+ t.basis = Basis::looking_at(p_target - origin, p_up);
return t;
}
void Transform3D::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);
-
+ basis = Basis::looking_at(p_target - p_eye, p_up);
origin = p_eye;
}
diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h
index 3d8e70cec7..345e0fade0 100644
--- a/core/math/transform_3d.h
+++ b/core/math/transform_3d.h
@@ -75,16 +75,24 @@ public:
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;
-
- _FORCE_INLINE_ Vector<Vector3> xform(const Vector<Vector3> &p_array) const;
- _FORCE_INLINE_ Vector<Vector3> xform_inv(const Vector<Vector3> &p_array) 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);
void operator*=(const Transform3D &p_transform);
Transform3D operator*(const Transform3D &p_transform) const;
@@ -130,34 +138,24 @@ _FORCE_INLINE_ Vector3 Transform3D::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));
}
+// 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 {
- 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);
+ Basis b = basis.inverse();
+ b.transpose();
+ return xform_fast(p_plane, b);
}
_FORCE_INLINE_ Plane Transform3D::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);
+ Transform3D inv = affine_inverse();
+ Basis basis_transpose = basis.transposed();
+ return xform_inv_fast(p_plane, inv, basis_transpose);
}
_FORCE_INLINE_ AABB Transform3D::xform(const AABB &p_aabb) const {
- /* http://dev.theomader.com/transform-bounding-boxes/ */
+ /* https://dev.theomader.com/transform-bounding-boxes/ */
Vector3 min = p_aabb.position;
Vector3 max = p_aabb.position + p_aabb.size;
Vector3 tmin, tmax;
@@ -231,4 +229,37 @@ Vector<Vector3> Transform3D::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 bf06c848c5..2f3da0b6a8 100644
--- a/core/math/triangle_mesh.cpp
+++ b/core/math/triangle_mesh.cpp
@@ -76,7 +76,7 @@ int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, in
int index = r_max_alloc++;
BVH *_new = &p_bvh[index];
_new->aabb = aabb;
- _new->center = aabb.position + aabb.size * 0.5;
+ _new->center = aabb.get_center();
_new->face_index = -1;
_new->left = left;
_new->right = right;
@@ -152,13 +152,13 @@ void TriangleMesh::create(const Vector<Vector3> &p_faces) {
bw[i].left = -1;
bw[i].right = -1;
bw[i].face_index = i;
- bw[i].center = bw[i].aabb.position + bw[i].aabb.size * 0.5;
+ bw[i].center = bw[i].aabb.get_center();
}
vertices.resize(db.size());
Vector3 *vw = vertices.ptrw();
- for (Map<Vector3, int>::Element *E = db.front(); E; E = E->next()) {
- vw[E->get()] = E->key();
+ for (const KeyValue<Vector3, int> &E : db) {
+ vw[E.value] = E.key;
}
}
diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h
index 463b0dd5c8..2d3b4db4bb 100644
--- a/core/math/triangle_mesh.h
+++ b/core/math/triangle_mesh.h
@@ -37,11 +37,13 @@
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 eb3301f5d0..16e43d7d06 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);
}
@@ -75,7 +79,7 @@ real_t Vector2::angle_to(const Vector2 &p_vector2) const {
}
real_t Vector2::angle_to_point(const Vector2 &p_vector2) const {
- return Math::atan2(y - p_vector2.y, x - p_vector2.x);
+ return (*this - p_vector2).angle();
}
real_t Vector2::dot(const Vector2 &p_other) const {
@@ -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(
@@ -145,7 +149,7 @@ Vector2 Vector2::limit_length(const 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;
diff --git a/core/math/vector2.h b/core/math/vector2.h
index 4d9f3126e9..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;
}
@@ -106,11 +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;
- _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;
@@ -147,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);
}
@@ -168,29 +169,29 @@ struct Vector2 {
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
@@ -357,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 3d59064af6..fa212c178a 100644
--- a/core/math/vector3.cpp
+++ b/core/math/vector3.cpp
@@ -32,22 +32,22 @@
#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);
}
@@ -59,13 +59,13 @@ Vector3 Vector3::clamp(const Vector3 &p_min, const Vector3 &p_max) const {
CLAMP(z, p_min.z, p_max.z));
}
-void Vector3::snap(Vector3 p_step) {
+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;
@@ -82,7 +82,7 @@ Vector3 Vector3::limit_length(const real_t p_len) const {
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::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;
@@ -115,12 +115,6 @@ Basis Vector3::outer(const Vector3 &p_b) const {
return Basis(row0, row1, row2);
}
-Basis Vector3::to_diagonal_matrix() const {
- return Basis(x, 0, 0,
- 0, y, 0,
- 0, 0, z);
-}
-
bool Vector3::is_equal_approx(const Vector3 &p_v) const {
return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z);
}
diff --git a/core/math/vector3.h b/core/math/vector3.h
index d8d3cd3cc0..e65ac31c02 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;
}
@@ -90,23 +90,22 @@ struct Vector3 {
_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;
_FORCE_INLINE_ real_t dot(const Vector3 &p_b) const;
Basis outer(const Vector3 &p_b) const;
- Basis to_diagonal_matrix() const;
_FORCE_INLINE_ Vector3 abs() const;
_FORCE_INLINE_ Vector3 floor() const;
@@ -143,10 +142,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;
@@ -168,7 +167,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;
@@ -208,14 +207,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);
}
@@ -303,29 +302,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 2de1e4e331..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);
}
diff --git a/core/math/vector3i.h b/core/math/vector3i.h
index 37c7c1c368..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;
@@ -84,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;
@@ -103,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;
@@ -175,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..31b7bf0043
--- /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_UNRELIABLE_ORDERED,
+ TRANSFER_MODE_RELIABLE
+};
+
+enum RPCMode {
+ RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)
+ RPC_MODE_ANY_PEER, // 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 cc994a9852..1fb0318403 100644
--- a/core/io/multiplayer_api.h
+++ b/core/multiplayer/multiplayer_api.h
@@ -31,37 +31,38 @@
#ifndef MULTIPLAYER_API_H
#define MULTIPLAYER_API_H
-#include "core/io/multiplayer_peer.h"
+#include "core/multiplayer/multiplayer.h"
+#include "core/multiplayer/multiplayer_peer.h"
#include "core/object/ref_counted.h"
+class MultiplayerReplicator;
+class RPCManager;
+
class MultiplayerAPI : public RefCounted {
GDCLASS(MultiplayerAPI, RefCounted);
public:
- 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 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
+ 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,
};
- struct RPCConfig {
- StringName name;
- RPCMode rpc_mode = RPC_MODE_DISABLED;
- bool sync = false;
- MultiplayerPeer::TransferMode transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE;
- int channel = 0;
-
- bool operator==(RPCConfig const &p_other) const {
- return name == p_other.name;
- }
+ // 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,
};
- struct SortRPCConfig {
- StringName::AlphCompare compare;
- bool operator()(const RPCConfig &p_a, const RPCConfig &p_b) const {
- return compare(p_a.name, p_b.name);
- }
+ // This is the mask that will be used to extract the command.
+ enum {
+ CMD_MASK = 7, // 0x7 -> 0b00001111
};
private:
@@ -81,61 +82,52 @@ private:
Map<int, NodeInfo> nodes;
};
- Ref<MultiplayerPeer> 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_raw(int p_from, const uint8_t *p_packet, int p_packet_len);
- void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, 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_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,
- };
-
void poll();
void clear();
void set_root_node(Node *p_node);
Node *get_root_node();
- void set_network_peer(const Ref<MultiplayerPeer> &p_peer);
- Ref<MultiplayerPeer> get_network_peer() const;
- Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, MultiplayerPeer::TransferMode p_mode = MultiplayerPeer::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);
+ 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);
@@ -143,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/multiplayer/multiplayer_peer.cpp b/core/multiplayer/multiplayer_peer.cpp
new file mode 100644
index 0000000000..3c33948e2f
--- /dev/null
+++ b/core/multiplayer/multiplayer_peer.cpp
@@ -0,0 +1,271 @@
+/*************************************************************************/
+/* multiplayer_peer.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_peer.h"
+
+#include "core/os/os.h"
+
+uint32_t MultiplayerPeer::generate_unique_id() const {
+ uint32_t hash = 0;
+
+ 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
+
+ hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion
+ }
+
+ return hash;
+}
+
+void MultiplayerPeer::set_transfer_channel(int p_channel) {
+ transfer_channel = p_channel;
+}
+
+int MultiplayerPeer::get_transfer_channel() const {
+ return transfer_channel;
+}
+
+void MultiplayerPeer::set_transfer_mode(Multiplayer::TransferMode p_mode) {
+ transfer_mode = p_mode;
+}
+
+Multiplayer::TransferMode MultiplayerPeer::get_transfer_mode() const {
+ return transfer_mode;
+}
+
+void MultiplayerPeer::set_refuse_new_connections(bool p_enable) {
+ refuse_connections = p_enable;
+}
+
+bool MultiplayerPeer::is_refusing_new_connections() const {
+ return refuse_connections;
+}
+
+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");
+ 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);
+ BIND_ENUM_CONSTANT(CONNECTION_CONNECTED);
+
+ BIND_CONSTANT(TARGET_PEER_BROADCAST);
+ BIND_CONSTANT(TARGET_PEER_SERVER);
+
+ ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("server_disconnected"));
+ ADD_SIGNAL(MethodInfo("connection_succeeded"));
+ ADD_SIGNAL(MethodInfo("connection_failed"));
+}
+
+/*************/
+
+int MultiplayerPeerExtension::get_available_packet_count() const {
+ int count;
+ if (GDVIRTUAL_CALL(_get_available_packet_count, count)) {
+ return count;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_available_packet_count is unimplemented!");
+ return -1;
+}
+
+Error MultiplayerPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ int err;
+ if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_native is unimplemented!");
+ return FAILED;
+}
+
+Error MultiplayerPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ int err;
+ if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_put_packet_native is unimplemented!");
+ return FAILED;
+}
+
+int MultiplayerPeerExtension::get_max_packet_size() const {
+ int size;
+ if (GDVIRTUAL_CALL(_get_max_packet_size, size)) {
+ return size;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_max_packet_size is unimplemented!");
+ return 0;
+}
+
+void MultiplayerPeerExtension::set_transfer_channel(int p_channel) {
+ if (GDVIRTUAL_CALL(_set_transfer_channel, p_channel)) {
+ return;
+ }
+ MultiplayerPeer::set_transfer_channel(p_channel);
+}
+
+int MultiplayerPeerExtension::get_transfer_channel() const {
+ int channel;
+ if (GDVIRTUAL_CALL(_get_transfer_channel, channel)) {
+ return channel;
+ }
+ return MultiplayerPeer::get_transfer_channel();
+}
+
+void MultiplayerPeerExtension::set_transfer_mode(Multiplayer::TransferMode p_mode) {
+ if (GDVIRTUAL_CALL(_set_transfer_mode, p_mode)) {
+ return;
+ }
+ MultiplayerPeer::set_transfer_mode(p_mode);
+}
+
+Multiplayer::TransferMode MultiplayerPeerExtension::get_transfer_mode() const {
+ int mode;
+ if (GDVIRTUAL_CALL(_get_transfer_mode, mode)) {
+ return (Multiplayer::TransferMode)mode;
+ }
+ return MultiplayerPeer::get_transfer_mode();
+}
+
+void MultiplayerPeerExtension::set_target_peer(int p_peer_id) {
+ if (GDVIRTUAL_CALL(_set_target_peer, p_peer_id)) {
+ return;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_set_target_peer is unimplemented!");
+}
+
+int MultiplayerPeerExtension::get_packet_peer() const {
+ int peer;
+ if (GDVIRTUAL_CALL(_get_packet_peer, peer)) {
+ return peer;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_peer is unimplemented!");
+ return 0;
+}
+
+bool MultiplayerPeerExtension::is_server() const {
+ bool server;
+ if (GDVIRTUAL_CALL(_is_server, server)) {
+ return server;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_is_server is unimplemented!");
+ return false;
+}
+
+void MultiplayerPeerExtension::poll() {
+ int err;
+ if (GDVIRTUAL_CALL(_poll, err)) {
+ return;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_poll is unimplemented!");
+}
+
+int MultiplayerPeerExtension::get_unique_id() const {
+ int id;
+ if (GDVIRTUAL_CALL(_get_unique_id, id)) {
+ return id;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_unique_id is unimplemented!");
+ return 0;
+}
+
+void MultiplayerPeerExtension::set_refuse_new_connections(bool p_enable) {
+ if (GDVIRTUAL_CALL(_set_refuse_new_connections, p_enable)) {
+ return;
+ }
+ MultiplayerPeer::set_refuse_new_connections(p_enable);
+}
+
+bool MultiplayerPeerExtension::is_refusing_new_connections() const {
+ bool refusing;
+ if (GDVIRTUAL_CALL(_is_refusing_new_connections, refusing)) {
+ return refusing;
+ }
+ return MultiplayerPeer::is_refusing_new_connections();
+}
+
+MultiplayerPeer::ConnectionStatus MultiplayerPeerExtension::get_connection_status() const {
+ int status;
+ if (GDVIRTUAL_CALL(_get_connection_status, status)) {
+ return (ConnectionStatus)status;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_connection_status is unimplemented!");
+ return CONNECTION_DISCONNECTED;
+}
+
+void MultiplayerPeerExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size");
+ GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size");
+ GDVIRTUAL_BIND(_get_available_packet_count);
+ GDVIRTUAL_BIND(_get_max_packet_size);
+
+ GDVIRTUAL_BIND(_set_transfer_channel, "p_channel");
+ GDVIRTUAL_BIND(_get_transfer_channel);
+
+ GDVIRTUAL_BIND(_set_transfer_mode, "p_mode");
+ GDVIRTUAL_BIND(_get_transfer_mode);
+
+ GDVIRTUAL_BIND(_set_target_peer, "p_peer");
+
+ GDVIRTUAL_BIND(_get_packet_peer);
+ GDVIRTUAL_BIND(_is_server);
+ GDVIRTUAL_BIND(_poll);
+ GDVIRTUAL_BIND(_get_unique_id);
+ GDVIRTUAL_BIND(_set_refuse_new_connections, "p_enable");
+ GDVIRTUAL_BIND(_is_refusing_new_connections);
+ GDVIRTUAL_BIND(_get_connection_status);
+}
diff --git a/core/multiplayer/multiplayer_peer.h b/core/multiplayer/multiplayer_peer.h
new file mode 100644
index 0000000000..126ba9e645
--- /dev/null
+++ b/core/multiplayer/multiplayer_peer.h
@@ -0,0 +1,144 @@
+/*************************************************************************/
+/* multiplayer_peer.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 NETWORKED_MULTIPLAYER_PEER_H
+#define NETWORKED_MULTIPLAYER_PEER_H
+
+#include "core/io/packet_peer.h"
+#include "core/multiplayer/multiplayer.h"
+
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
+#include "core/variant/native_ptr.h"
+
+class MultiplayerPeer : public PacketPeer {
+ GDCLASS(MultiplayerPeer, PacketPeer);
+
+protected:
+ static void _bind_methods();
+
+private:
+ int transfer_channel = 0;
+ Multiplayer::TransferMode transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE;
+ bool refuse_connections = false;
+
+public:
+ enum {
+ TARGET_PEER_BROADCAST = 0,
+ TARGET_PEER_SERVER = 1
+ };
+
+ enum ConnectionStatus {
+ CONNECTION_DISCONNECTED,
+ CONNECTION_CONNECTING,
+ CONNECTION_CONNECTED,
+ };
+
+ virtual void set_transfer_channel(int p_channel);
+ virtual int get_transfer_channel() const;
+ virtual void set_transfer_mode(Multiplayer::TransferMode p_mode);
+ virtual Multiplayer::TransferMode get_transfer_mode() const;
+ virtual void set_refuse_new_connections(bool p_enable);
+ virtual bool is_refusing_new_connections() const;
+
+ virtual void set_target_peer(int p_peer_id) = 0;
+
+ virtual int get_packet_peer() const = 0;
+
+ virtual bool is_server() const = 0;
+
+ virtual void poll() = 0;
+
+ virtual int get_unique_id() const = 0;
+
+ virtual ConnectionStatus get_connection_status() const = 0;
+
+ uint32_t generate_unique_id() const;
+
+ MultiplayerPeer() {}
+};
+
+VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus);
+
+class MultiplayerPeerExtension : public MultiplayerPeer {
+ GDCLASS(MultiplayerPeerExtension, MultiplayerPeer);
+
+protected:
+ static void _bind_methods();
+
+public:
+ /* PacketPeer */
+ virtual int get_available_packet_count() const override;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
+ virtual int get_max_packet_size() const override;
+
+ /* MultiplayerPeer */
+ virtual void set_transfer_channel(int p_channel) override;
+ virtual int get_transfer_channel() const override;
+ virtual void set_transfer_mode(Multiplayer::TransferMode p_mode) override;
+ virtual Multiplayer::TransferMode get_transfer_mode() const override;
+ virtual void set_target_peer(int p_peer_id) override;
+
+ virtual int get_packet_peer() const override;
+
+ virtual bool is_server() const override;
+
+ virtual void poll() override;
+
+ virtual int get_unique_id() const override;
+
+ virtual void set_refuse_new_connections(bool p_enable) override;
+ virtual bool is_refusing_new_connections() const override;
+
+ virtual ConnectionStatus get_connection_status() const override;
+
+ /* PacketPeer GDExtension */
+ GDVIRTUAL0RC(int, _get_available_packet_count);
+ GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>);
+ GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int);
+ GDVIRTUAL0RC(int, _get_max_packet_size);
+
+ /* MultiplayerPeer GDExtension */
+ GDVIRTUAL1(_set_transfer_channel, int);
+ GDVIRTUAL0RC(int, _get_transfer_channel);
+ GDVIRTUAL1(_set_transfer_mode, int);
+ GDVIRTUAL0RC(int, _get_transfer_mode);
+ GDVIRTUAL1(_set_target_peer, int);
+ GDVIRTUAL0RC(int, _get_packet_peer);
+ GDVIRTUAL0RC(bool, _is_server);
+ GDVIRTUAL0R(int, _poll);
+ GDVIRTUAL0RC(int, _get_unique_id);
+ GDVIRTUAL1(_set_refuse_new_connections, bool);
+ GDVIRTUAL0RC(bool, _is_refusing_new_connections);
+ GDVIRTUAL0RC(int, _get_connection_status);
+};
+
+#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..20ab7a25bb
--- /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_PEER: {
+ 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/class_db.cpp b/core/object/class_db.cpp
index c6ba39be94..d5509c50f1 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -505,11 +505,12 @@ 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) {
+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;
@@ -528,7 +529,7 @@ Object *ClassDB::instantiate(const StringName &p_class) {
}
ERR_FAIL_COND_V_MSG(!ti, nullptr, "Cannot get class '" + String(p_class) + "'.");
ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled.");
- ERR_FAIL_COND_V(!ti->creation_func, nullptr);
+ ERR_FAIL_COND_V_MSG(!ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated.");
}
#ifdef TOOLS_ENABLED
if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) {
@@ -544,6 +545,15 @@ Object *ClassDB::instantiate(const StringName &p_class) {
return ti->creation_func();
}
+Object *ClassDB::construct_object(Object *(*p_create_func)(), ObjectNativeExtension *p_extension) {
+ if (p_extension) {
+ initializing_with_extension = true;
+ initializing_extension = p_extension;
+ initializing_extension_instance = p_extension->create_instance(p_extension->class_userdata);
+ }
+ return p_create_func();
+}
+
bool ClassDB::can_instantiate(const StringName &p_class) {
OBJTYPE_RLOCK;
@@ -891,6 +901,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;
@@ -1001,6 +1037,18 @@ void ClassDB::add_property_subgroup(const StringName &p_class, const String &p_n
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(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index) {
lock.read_lock();
@@ -1067,6 +1115,20 @@ void ClassDB::set_property_default_value(const StringName &p_class, const String
default_values[p_class][p_name] = p_default;
}
+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;
@@ -1406,7 +1468,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;
@@ -1416,6 +1478,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;
@@ -1592,7 +1667,7 @@ 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));
+ 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);
@@ -1604,6 +1679,7 @@ void ClassDB::register_extension_class(ObjectNativeExtension *p_extension) {
c.inherits = parent->name;
c.class_ptr = parent->class_ptr;
c.inherits_ptr = parent;
+ c.exposed = true;
classes[p_extension->class_name] = c;
}
diff --git a/core/object/class_db.h b/core/object/class_db.h
index 3a84e9ab38..3a1cbf8559 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -132,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;
@@ -233,7 +234,8 @@ public:
static bool is_parent_class(const StringName &p_class, const StringName &p_inherits);
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);
+ static Object *construct_object(Object *(*p_create_func)(), ObjectNativeExtension *p_extension);
+ 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);
@@ -330,7 +332,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;
@@ -352,8 +354,11 @@ public:
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);
@@ -371,7 +376,7 @@ public:
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);
@@ -384,6 +389,8 @@ 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);
@@ -409,39 +416,53 @@ 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);
+ ::ClassDB::bind_integer_constant(get_class_static(), __constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant);
-#else
-
-#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>(); \
+ ::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>(); \
+#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"
diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py
index 2c6b8cddc9..86c2891e5d 100644
--- a/core/object/make_virtuals.py
+++ b/core/object/make_virtuals.py
@@ -1,8 +1,8 @@
proto = """
#define GDVIRTUAL$VER($RET m_name $ARG) \\
-GDNativeExtensionClassCallVirtual _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, #m_name) : (GDNativeExtensionClassCallVirtual) nullptr;\\
StringName _gdvirtual_##m_name##_sn = #m_name;\\
-bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
+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; \\
@@ -23,6 +23,16 @@ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
\\
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;\\
@@ -32,7 +42,6 @@ _FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() { \\
return method_info;\\
}
-
"""
@@ -77,7 +86,7 @@ def generate_version(argcount, const=False, returns=False):
callptrargs += "\t\t"
callptrargsptr += ", "
argtext += "m_type" + str(i + 1)
- callargtext += "const m_type" + str(i + 1) + "& arg" + str(i + 1)
+ callargtext += "m_type" + str(i + 1) + " arg" + str(i + 1)
callsiargs += "Variant(arg" + str(i + 1) + ")"
callsiargptrs += "&vargs[" + str(i) + "]"
callptrargs += (
@@ -103,7 +112,7 @@ def generate_version(argcount, const=False, returns=False):
if returns:
if argcount > 0:
callargtext += ","
- callargtext += " m_ret& r_ret"
+ callargtext += " m_ret& r_ret"
s = s.replace("$CALLSIBEGIN", "Variant ret = ")
s = s.replace("$CALLSIRET", "r_ret = ret;")
s = s.replace("$CALLPTRRETPASS", "&ret")
diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp
index 4751c69f1e..736e940846 100644
--- a/core/object/message_queue.cpp
+++ b/core/object/message_queue.cpp
@@ -227,16 +227,16 @@ void MessageQueue::statistics() {
print_line("TOTAL BYTES: " + itos(buffer_end));
print_line("NULL count: " + itos(null_count));
- for (Map<StringName, int>::Element *E = set_count.front(); E; E = E->next()) {
- print_line("SET " + E->key() + ": " + itos(E->get()));
+ for (const KeyValue<StringName, int> &E : set_count) {
+ print_line("SET " + E.key + ": " + itos(E.value));
}
- for (Map<Callable, int>::Element *E = call_count.front(); E; E = E->next()) {
- print_line("CALL " + E->key() + ": " + itos(E->get()));
+ for (const KeyValue<Callable, int> &E : call_count) {
+ print_line("CALL " + E.key + ": " + itos(E.value));
}
- for (Map<int, int>::Element *E = notify_count.front(); E; E = E->next()) {
- print_line("NOTIFY " + itos(E->key()) + ": " + itos(E->get()));
+ for (const KeyValue<int, int> &E : notify_count) {
+ print_line("NOTIFY " + itos(E.key) + ": " + itos(E.value));
}
}
diff --git a/core/object/method_bind.h b/core/object/method_bind.h
index 92b964772a..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,
};
diff --git a/core/object/object.cpp b/core/object/object.cpp
index d552d5e5e0..b5797a4633 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -1409,7 +1409,7 @@ void Object::_disconnect(const StringName &p_signal, const Callable &p_callable,
if (!p_force) {
slot->reference_count--; // by default is zero, if it was not referenced it will go below it
- if (slot->reference_count >= 0) {
+ if (slot->reference_count > 0) {
return;
}
}
@@ -1619,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);
@@ -1811,11 +1814,26 @@ void *Object::get_instance_binding(void *p_token, const GDNativeInstanceBindingC
return binding;
}
+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);
- ClassDB::instance_get_native_extension_data(&_extension, &_extension_instance);
+ ClassDB::instance_get_native_extension_data(&_extension, &_extension_instance, this);
#ifdef DEBUG_ENABLED
_lock_index.init(1);
@@ -1876,7 +1894,7 @@ Object::~Object() {
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, _instance_bindings[i].binding, this);
+ _instance_bindings[i].free_callback(_instance_bindings[i].token, this, _instance_bindings[i].binding);
}
}
memfree(_instance_bindings);
diff --git a/core/object/object.h b/core/object/object.h
index b7d0916a54..a44d921bff 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -97,6 +97,7 @@ 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
};
@@ -131,27 +132,38 @@ 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;
+#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;
@@ -272,11 +284,19 @@ struct ObjectNativeExtension {
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_BIND(m_name) ClassDB::add_virtual_method(get_class_static(), _gdvirtual_##m_name##_get_method_info());
+#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.
@@ -298,7 +318,7 @@ 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 { \
@@ -371,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(); \
} \
@@ -414,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); \
@@ -808,6 +828,7 @@ public:
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();
diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h
index e0af2c18bb..f2dd2aa324 100644
--- a/core/object/ref_counted.h
+++ b/core/object/ref_counted.h
@@ -274,8 +274,6 @@ struct PtrToArg<const Ref<T> &> {
}
};
-#ifdef DEBUG_METHODS_ENABLED
-
template <class T>
struct GetTypeInfo<Ref<T>> {
static const Variant::Type VARIANT_TYPE = Variant::OBJECT;
@@ -296,6 +294,4 @@ struct GetTypeInfo<const Ref<T> &> {
}
};
-#endif // DEBUG_METHODS_ENABLED
-
#endif // REF_COUNTED_H
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index 0fb8c7350c..b0ce46ca2b 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -93,8 +93,8 @@ Dictionary Script::_get_script_constant_map() {
Dictionary ret;
Map<StringName, Variant> map;
get_constants(&map);
- for (Map<StringName, Variant>::Element *E = map.front(); E; E = E->next()) {
- ret[E->key()] = E->value();
+ for (const KeyValue<StringName, Variant> &E : map) {
+ ret[E.key] = E.value;
}
return ret;
}
diff --git a/core/object/script_language.h b/core/object/script_language.h
index 2cbaa0f52e..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"
@@ -159,7 +159,7 @@ public:
virtual bool is_placeholder_fallback_enabled() const { return false; }
- virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const = 0;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const = 0;
Script() {}
};
@@ -200,7 +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 const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const = 0;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const = 0;
virtual ScriptLanguage *get_language() = 0;
virtual ~ScriptInstance();
@@ -310,6 +310,7 @@ public:
Ref<Script> script;
String class_name;
String class_member;
+ String class_path;
int location;
};
@@ -418,7 +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 const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const { return Vector<MultiplayerAPI::RPCConfig>(); }
+ 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 adf068eb2f..b7d2bac96d 100644
--- a/core/object/undo_redo.cpp
+++ b/core/object/undo_redo.cpp
@@ -405,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();
}
diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h
index 8f009830e3..d1ce252d86 100644
--- a/core/object/undo_redo.h
+++ b/core/object/undo_redo.h
@@ -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 3c0e56f5a8..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(double p_time) {
- if (get_script_instance()) {
- return get_script_instance()->call("_physics_process", p_time);
+ bool quit;
+ if (GDVIRTUAL_CALL(_physics_process, p_time, quit)) {
+ return quit;
}
return false;
}
bool MainLoop::process(double p_time) {
- if (get_script_instance()) {
- return get_script_instance()->call("_process", 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 b42e9b18ff..4da01d767e 100644
--- a/core/os/main_loop.h
+++ b/core/os/main_loop.h
@@ -32,6 +32,7 @@
#define MAIN_LOOP_H
#include "core/input/input_event.h"
+#include "core/object/gdvirtual.gen.inc"
#include "core/object/ref_counted.h"
#include "core/object/script_language.h"
@@ -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
diff --git a/core/os/memory.h b/core/os/memory.h
index 9d09626b8c..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
@@ -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/os.cpp b/core/os/os.cpp
index f7af74da3e..12f85858c3 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -36,7 +36,6 @@
#include "core/io/file_access.h"
#include "core/os/midi_driver.h"
#include "core/version_generated.gen.h"
-#include "servers/audio_server.h"
#include <stdarg.h>
@@ -146,6 +145,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;
}
@@ -178,7 +181,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 {
@@ -215,14 +218,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;
}
@@ -235,6 +230,12 @@ String OS::get_locale() const {
return "en";
}
+// Non-virtual helper to extract the 2 or 3-letter language code from
+// `get_locale()` in a way that's consistent for all platforms.
+String OS::get_locale_language() const {
+ return get_locale().left(3).replace("_", "");
+}
+
// Helper function to ensure that a dir name/path will be valid on the OS
String OS::get_safe_dir_name(const String &p_dir_name, bool p_allow_dir_separator) const {
Vector<String> invalid_chars = String(": * ? \" < > |").split(" ");
@@ -280,23 +281,23 @@ String OS::get_bundle_resource_dir() const {
return ".";
}
+// Path to macOS .app bundle embedded icon
+String OS::get_bundle_icon_path() const {
+ return String();
+}
+
// OS specific path for user://
String OS::get_user_data_dir() const {
return ".";
}
-// Android OS path to app's external data storage
-String OS::get_external_data_dir() const {
- return get_user_data_dir();
-};
-
// Absolute path to res://
String OS::get_resource_dir() const {
return ProjectSettings::get_singleton()->get_resource_path();
}
// 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 ".";
}
@@ -370,9 +371,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 fcb195afe1..29d33ce4f0 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -52,8 +52,8 @@ 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;
@@ -225,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() {}
@@ -241,6 +243,7 @@ public:
RenderThreadMode get_render_thread_mode() const { return _render_thread_mode; }
virtual String get_locale() const;
+ String get_locale_language() const;
String get_safe_dir_name(const String &p_dir_name, bool p_allow_dir_separator = false) const;
virtual String get_godot_dir_name() const;
@@ -249,9 +252,9 @@ public:
virtual String get_config_path() const;
virtual String get_cache_path() const;
virtual String get_bundle_resource_dir() const;
+ virtual String get_bundle_icon_path() const;
virtual String get_user_data_dir() const;
- virtual String get_external_data_dir() const;
virtual String get_resource_dir() const;
enum SystemDir {
@@ -265,13 +268,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/thread.cpp b/core/os/thread.cpp
index 73e31bdb3d..27aefc98de 100644
--- a/core/os/thread.cpp
+++ b/core/os/thread.cpp
@@ -28,6 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef PLATFORM_THREAD_OVERRIDE // See details in thread.h
+
#include "thread.h"
#include "core/object/script_language.h"
@@ -126,3 +128,4 @@ Thread::~Thread() {
}
#endif
+#endif // PLATFORM_THREAD_OVERRIDE
diff --git a/core/os/thread.h b/core/os/thread.h
index 17ac82c650..59cb58ac57 100644
--- a/core/os/thread.h
+++ b/core/os/thread.h
@@ -28,6 +28,12 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+// Define PLATFORM_THREAD_OVERRIDE in your platform's `platform_config.h`
+// to use a custom Thread implementation defined in `platform/[your_platform]/platform_thread.h`
+// Overriding the platform implementation is required in some proprietary platforms
+#ifdef PLATFORM_THREAD_OVERRIDE
+#include "platform_thread.h"
+#else
#ifndef THREAD_H
#define THREAD_H
@@ -116,3 +122,4 @@ public:
};
#endif // THREAD_H
+#endif // PLATFORM_THREAD_OVERRIDE
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 3d1cb4a8e1..e33c21cc00 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -41,14 +41,13 @@
#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/multiplayer_peer.h"
#include "core/io/packed_data_container.h"
#include "core/io/packet_peer.h"
#include "core/io/packet_peer_dtls.h"
@@ -68,6 +67,9 @@
#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"
@@ -82,19 +84,20 @@ 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 _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;
@@ -143,10 +146,12 @@ void register_core_types() {
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);
@@ -164,11 +169,13 @@ void register_core_types() {
GDREGISTER_VIRTUAL_CLASS(IP);
GDREGISTER_VIRTUAL_CLASS(StreamPeer);
+ GDREGISTER_CLASS(StreamPeerExtension);
GDREGISTER_CLASS(StreamPeerBuffer);
GDREGISTER_CLASS(StreamPeerTCP);
GDREGISTER_CLASS(TCPServer);
GDREGISTER_VIRTUAL_CLASS(PacketPeer);
+ GDREGISTER_CLASS(PacketPeerExtension);
GDREGISTER_CLASS(PacketPeerStream);
GDREGISTER_CLASS(PacketPeerUDP);
GDREGISTER_CLASS(UDPServer);
@@ -192,6 +199,8 @@ void register_core_types() {
ResourceLoader::add_resource_format_loader(resource_format_loader_crypto);
GDREGISTER_VIRTUAL_CLASS(MultiplayerPeer);
+ GDREGISTER_VIRTUAL_CLASS(MultiplayerPeerExtension);
+ GDREGISTER_VIRTUAL_CLASS(MultiplayerReplicator);
GDREGISTER_CLASS(MultiplayerAPI);
GDREGISTER_CLASS(MainLoop);
GDREGISTER_CLASS(Translation);
@@ -202,11 +211,11 @@ void register_core_types() {
GDREGISTER_CLASS(ResourceFormatLoader);
GDREGISTER_CLASS(ResourceFormatSaver);
- GDREGISTER_CLASS(_File);
- GDREGISTER_CLASS(_Directory);
- GDREGISTER_CLASS(_Thread);
- GDREGISTER_CLASS(_Mutex);
- GDREGISTER_CLASS(_Semaphore);
+ 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);
@@ -234,18 +243,21 @@ void register_core_types() {
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);
+ _geometry_2d = memnew(core_bind::Geometry2D);
+ _geometry_3d = memnew(core_bind::Geometry3D);
- _resource_loader = memnew(_ResourceLoader);
- _resource_saver = memnew(_ResourceSaver);
- _os = memnew(_OS);
- _engine = memnew(_Engine);
- _classdb = memnew(_ClassDB);
- _marshalls = memnew(_Marshalls);
- _engine_debugger = memnew(_EngineDebugger);
+ _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() {
@@ -262,35 +274,35 @@ void register_core_settings() {
void register_core_singletons() {
GDREGISTER_CLASS(ProjectSettings);
GDREGISTER_VIRTUAL_CLASS(IP);
- GDREGISTER_CLASS(_Geometry2D);
- GDREGISTER_CLASS(_Geometry3D);
- GDREGISTER_CLASS(_ResourceLoader);
- GDREGISTER_CLASS(_ResourceSaver);
- GDREGISTER_CLASS(_OS);
- GDREGISTER_CLASS(_Engine);
- GDREGISTER_CLASS(_ClassDB);
- GDREGISTER_CLASS(_Marshalls);
+ 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(_EngineDebugger);
+ 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(), "IP"));
- 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("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("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()));
@@ -298,13 +310,8 @@ void register_core_singletons() {
void register_core_extensions() {
// Hardcoded for now.
- if (ProjectSettings::get_singleton()->has_setting("native_extensions/paths")) {
- Vector<String> paths = ProjectSettings::get_singleton()->get("native_extensions/paths");
- for (int i = 0; i < paths.size(); i++) {
- NativeExtensionManager::LoadStatus status = native_extension_manager->load_extension(paths[i]);
- ERR_CONTINUE_MSG(status != NativeExtensionManager::LOAD_STATUS_OK, "Error loading extension: " + paths[i]);
- }
- }
+ NativeExtension::initialize_native_extensions();
+ native_extension_manager->load_extensions();
native_extension_manager->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_CORE);
}
@@ -349,6 +356,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/string/optimized_translation.cpp b/core/string/optimized_translation.cpp
index 5863bd1c46..839b7a9c01 100644
--- a/core/string/optimized_translation.cpp
+++ b/core/string/optimized_translation.cpp
@@ -162,11 +162,11 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) {
btw[btindex++] = t.size();
btw[btindex++] = hfunc_table[i];
- for (Map<uint32_t, int>::Element *E = t.front(); E; E = E->next()) {
- btw[btindex++] = E->key();
- btw[btindex++] = compressed[E->get()].offset;
- btw[btindex++] = compressed[E->get()].compressed.size();
- btw[btindex++] = compressed[E->get()].orig_len;
+ for (const KeyValue<uint32_t, int> &E : t) {
+ btw[btindex++] = E.key;
+ btw[btindex++] = compressed[E.value].offset;
+ btw[btindex++] = compressed[E.value].compressed.size();
+ btw[btindex++] = compressed[E.value].orig_len;
}
}
diff --git a/core/string/translation.cpp b/core/string/translation.cpp
index 19d23fd375..cf61467d08 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -35,7 +35,6 @@
#include "core/os/os.h"
#ifdef TOOLS_ENABLED
-#include "editor/editor_settings.h"
#include "main/main.h"
#endif
@@ -810,9 +809,12 @@ static const char *locale_names[] = {
// - https://msdn.microsoft.com/en-us/library/windows/desktop/ms693062(v=vs.85).aspx
static const char *locale_renames[][2] = {
- { "in", "id" }, // Indonesian
- { "iw", "he" }, // Hebrew
- { "no", "nb" }, // Norwegian Bokmål
+ { "in", "id" }, // Indonesian
+ { "iw", "he" }, // Hebrew
+ { "no", "nb" }, // Norwegian Bokmål
+ { "C", "en" }, // "C" is the simple/default/untranslated Computer locale.
+ // ASCII-only, English, no currency symbols. Godot treats this as "en".
+ // See https://unix.stackexchange.com/a/87763/164141 "The C locale is"...
{ nullptr, nullptr }
};
@@ -820,8 +822,8 @@ static const char *locale_renames[][2] = {
Dictionary Translation::_get_messages() const {
Dictionary d;
- for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) {
- d[E->key()] = E->value();
+ for (const KeyValue<StringName, StringName> &E : translation_map) {
+ d[E.key] = E.value;
}
return d;
}
@@ -830,8 +832,8 @@ Vector<String> Translation::_get_message_list() const {
Vector<String> msgs;
msgs.resize(translation_map.size());
int idx = 0;
- for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) {
- msgs.set(idx, E->key());
+ for (const KeyValue<StringName, StringName> &E : translation_map) {
+ msgs.set(idx, E.key);
idx += 1;
}
@@ -875,6 +877,11 @@ void Translation::add_plural_message(const StringName &p_src_text, const Vector<
}
StringName Translation::get_message(const StringName &p_src_text, const StringName &p_context) const {
+ StringName ret;
+ if (GDVIRTUAL_CALL(_get_message, p_src_text, p_context, ret)) {
+ return ret;
+ }
+
if (p_context != StringName()) {
WARN_PRINT("Translation class doesn't handle context. Using context in get_message() on a Translation instance is probably a mistake. \nUse a derived Translation class that handles context, such as TranslationPO class");
}
@@ -888,6 +895,11 @@ StringName Translation::get_message(const StringName &p_src_text, const StringNa
}
StringName Translation::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const {
+ StringName ret;
+ if (GDVIRTUAL_CALL(_get_plural_message, p_src_text, p_plural_text, p_n, p_context, ret)) {
+ return ret;
+ }
+
WARN_PRINT("Translation class doesn't handle plural messages. Calling get_plural_message() on a Translation instance is probably a mistake. \nUse a derived Translation class that handles plurals, such as TranslationPO class");
return get_message(p_src_text);
}
@@ -901,8 +913,8 @@ void Translation::erase_message(const StringName &p_src_text, const StringName &
}
void Translation::get_message_list(List<StringName> *r_messages) const {
- for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) {
- r_messages->push_back(E->key());
+ for (const KeyValue<StringName, StringName> &E : translation_map) {
+ r_messages->push_back(E.key);
}
}
@@ -923,12 +935,75 @@ void Translation::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_messages"), &Translation::_set_messages);
ClassDB::bind_method(D_METHOD("_get_messages"), &Translation::_get_messages);
+ GDVIRTUAL_BIND(_get_plural_message, "src_message", "src_plural_message", "n", "context");
+ GDVIRTUAL_BIND(_get_message, "src_message", "context");
+
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "messages", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_messages", "_get_messages");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "locale"), "set_locale", "get_locale");
}
///////////////////////////////////////////////
+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;
@@ -1101,10 +1176,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 {
@@ -1217,7 +1292,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 = "";
@@ -1258,10 +1344,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 {
@@ -1306,6 +1392,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);
@@ -1322,6 +1583,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..6aec0bb8ea 100644
--- a/core/string/translation.h
+++ b/core/string/translation.h
@@ -32,6 +32,8 @@
#define TRANSLATION_H
#include "core/io/resource.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
class Translation : public Resource {
GDCLASS(Translation, Resource);
@@ -48,6 +50,9 @@ class Translation : public Resource {
protected:
static void _bind_methods();
+ GDVIRTUAL2RC(StringName, _get_message, StringName, StringName);
+ GDVIRTUAL4RC(StringName, _get_plural_message, StringName, StringName, int, StringName);
+
public:
void set_locale(const String &p_locale);
_FORCE_INLINE_ String get_locale() const { return locale; }
@@ -77,6 +82,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 +129,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/ustring.cpp b/core/string/ustring.cpp
index 7beecdb6b5..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_DECIMALS 32
-#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;
@@ -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,15 @@ String String::num(double p_num, int p_decimals) {
return "inf";
}
}
-#ifndef NO_USE_STDLIB
if (p_decimals < 0) {
- p_decimals = 14 - (int)floor(log10(p_num));
+ 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;
@@ -1444,87 +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_DECIMALS) {
- p_decimals = MAX_DECIMALS;
- }
-
- 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_DECIMALS) { //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) {
@@ -1609,38 +1546,52 @@ String String::num_real(double p_num, bool p_trailing) {
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;
-#if REAL_T_IS_DOUBLE
- int decimals = 14 - (int)floor(log10(p_num));
+#ifdef REAL_T_IS_DOUBLE
+ int decimals = 14;
+ double tolerance = 1e-14;
#else
- int decimals = 6 - (int)floor(log10(p_num));
+ 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;
}
- int dec_int = 0;
- int dec_max = 0;
+ // 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;
}
@@ -1650,7 +1601,7 @@ String String::num_real(double p_num, bool p_trailing) {
}
dec *= 10;
- int last = (int)dec % 10;
+ int last = (int64_t)dec % 10;
if (last > 5) {
if (dec_int == dec_max) {
@@ -1704,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
@@ -1727,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) {
@@ -2198,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;
@@ -2239,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';
@@ -2269,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;
@@ -2298,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;
@@ -2329,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;
@@ -2449,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;
}
@@ -2524,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;
}
@@ -2614,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 == '-') {
@@ -2631,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;
@@ -3616,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 {
@@ -3800,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;
@@ -3830,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];
@@ -3855,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;
@@ -3963,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;
@@ -4191,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;
@@ -4219,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 {
@@ -4388,28 +4331,44 @@ bool String::is_resource_file() const {
return begins_with("res://") && find("::") == -1;
}
-bool String::is_rel_path() const {
+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 ffb354d6e1..1d80ccf58d 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -51,11 +51,15 @@ class CharProxy {
CowData<T> &_cowdata;
static const T _null = 0;
- _FORCE_INLINE_ CharProxy(const int &p_index, CowData<T> &cowdata) :
+ _FORCE_INLINE_ CharProxy(const int &p_index, CowData<T> &p_cowdata) :
_index(p_index),
- _cowdata(cowdata) {}
+ _cowdata(p_cowdata) {}
public:
+ _FORCE_INLINE_ CharProxy(const CharProxy<T> &p_other) :
+ _index(p_other._index),
+ _cowdata(p_other._cowdata) {}
+
_FORCE_INLINE_ operator T() const {
if (unlikely(_index == _cowdata.size())) {
return _null;
@@ -68,12 +72,12 @@ public:
return _cowdata.ptr() + _index;
}
- _FORCE_INLINE_ void operator=(const T &other) const {
- _cowdata.set(_index, other);
+ _FORCE_INLINE_ void operator=(const T &p_other) const {
+ _cowdata.set(_index, p_other);
}
- _FORCE_INLINE_ void operator=(const CharProxy<T> &other) const {
- _cowdata.set(_index, other.operator T());
+ _FORCE_INLINE_ void operator=(const CharProxy<T> &p_other) const {
+ _cowdata.set(_index, p_other.operator T());
}
};
@@ -398,7 +402,7 @@ public:
// path functions
bool is_absolute_path() const;
- bool is_rel_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;
@@ -523,10 +527,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/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/local_vector.h b/core/templates/local_vector.h
index 668ec513d6..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;
diff --git a/core/templates/map.h b/core/templates/map.h
index a47547d355..badb407e5d 100644
--- a/core/templates/map.h
+++ b/core/templates/map.h
@@ -36,7 +36,7 @@
#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 {
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/pooled_list.h b/core/templates/pooled_list.h
index b4a6d2d1dd..b139dadb75 100644
--- a/core/templates/pooled_list.h
+++ b/core/templates/pooled_list.h
@@ -28,13 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#pragma once
+#ifndef POOLED_LIST_H
+#define POOLED_LIST_H
+
+#include "core/templates/local_vector.h"
// Simple template to provide a pool with O(1) allocate and free.
// The freelist could alternatively be a linked list placed within the unused elements
// to use less memory, however a separate freelist is probably more cache friendly.
-
-// NOTE : Take great care when using this with non POD types. The construction and destruction
+//
+// NOTE: Take great care when using this with non POD types. The construction and destruction
// is done in the LocalVector, NOT as part of the pool. So requesting a new item does not guarantee
// a constructor is run, and free does not guarantee a destructor.
// You should generally handle clearing
@@ -42,9 +45,6 @@
// This is by design for fastest use in the BVH. If you want a more general pool
// that does call constructors / destructors on request / free, this should probably be
// a separate template.
-
-#include "core/templates/local_vector.h"
-
template <class T, bool force_trivial = false>
class PooledList {
LocalVector<T, uint32_t, force_trivial> list;
@@ -93,3 +93,5 @@ public:
_used_size--;
}
};
+
+#endif // POOLED_LIST_H
diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h
index 8d139551ef..71d41eacc4 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() {}
};
@@ -149,7 +151,7 @@ public:
return _allocate_rid();
}
- _FORCE_INLINE_ T *getornull(const RID &p_rid, bool p_initialize = false) {
+ _FORCE_INLINE_ T *get_or_null(const RID &p_rid, bool p_initialize = false) {
if (p_rid == RID()) {
return nullptr;
}
@@ -208,12 +210,12 @@ public:
return ptr;
}
void initialize_rid(RID p_rid) {
- T *mem = getornull(p_rid, true);
+ T *mem = get_or_null(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);
+ T *mem = get_or_null(p_rid, true);
ERR_FAIL_COND(!mem);
memnew_placement(mem, T(p_value));
}
@@ -397,8 +399,8 @@ public:
alloc.initialize_rid(p_rid, p_ptr);
}
- _FORCE_INLINE_ T *getornull(const RID &p_rid) {
- T **ptr = alloc.getornull(p_rid);
+ _FORCE_INLINE_ T *get_or_null(const RID &p_rid) {
+ T **ptr = alloc.get_or_null(p_rid);
if (unlikely(!ptr)) {
return nullptr;
}
@@ -406,7 +408,7 @@ public:
}
_FORCE_INLINE_ void replace(const RID &p_rid, T *p_new_ptr) {
- T **ptr = alloc.getornull(p_rid);
+ T **ptr = alloc.get_or_null(p_rid);
ERR_FAIL_COND(!ptr);
*ptr = p_new_ptr;
}
@@ -467,8 +469,8 @@ public:
alloc.initialize_rid(p_rid, p_ptr);
}
- _FORCE_INLINE_ T *getornull(const RID &p_rid) {
- return alloc.getornull(p_rid);
+ _FORCE_INLINE_ T *get_or_null(const RID &p_rid) {
+ return alloc.get_or_null(p_rid);
}
_FORCE_INLINE_ bool owns(const RID &p_rid) {
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/search_array.h b/core/templates/search_array.h
new file mode 100644
index 0000000000..8efc32df82
--- /dev/null
+++ b/core/templates/search_array.h
@@ -0,0 +1,67 @@
+/*************************************************************************/
+/* search_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 SEARCH_ARRAY_H
+#define SEARCH_ARRAY_H
+
+#include <core/templates/sort_array.h>
+
+template <class T, class Comparator = _DefaultComparator<T>>
+class SearchArray {
+public:
+ Comparator compare;
+
+ inline int bisect(const T *p_array, int p_len, const T &p_value, bool p_before) const {
+ int lo = 0;
+ int hi = p_len;
+ if (p_before) {
+ while (lo < hi) {
+ const int mid = (lo + hi) / 2;
+ if (compare(p_array[mid], p_value)) {
+ lo = mid + 1;
+ } else {
+ hi = mid;
+ }
+ }
+ } else {
+ while (lo < hi) {
+ const int mid = (lo + hi) / 2;
+ if (compare(p_value, p_array[mid])) {
+ hi = mid;
+ } else {
+ lo = mid + 1;
+ }
+ }
+ }
+ return lo;
+ }
+};
+
+#endif // SEARCH_ARRAY_H
diff --git a/core/templates/set.h b/core/templates/set.h
index 9261d2d3d2..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 {
diff --git a/core/templates/vector.h b/core/templates/vector.h
index 08cbef6ba4..4b008a45a4 100644
--- a/core/templates/vector.h
+++ b/core/templates/vector.h
@@ -40,6 +40,7 @@
#include "core/error/error_macros.h"
#include "core/os/memory.h"
#include "core/templates/cowdata.h"
+#include "core/templates/search_array.h"
#include "core/templates/sort_array.h"
template <class T>
@@ -92,7 +93,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;
}
@@ -112,6 +113,11 @@ public:
sort_custom<_DefaultComparator<T>>();
}
+ int bsearch(const T &p_value, bool p_before) {
+ SearchArray<T> search;
+ return search.bisect(ptrw(), size(), p_value, p_before);
+ }
+
Vector<T> duplicate() {
return *this;
}
@@ -229,7 +235,7 @@ public:
_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(T *p_ptr) { elem_ptr = p_ptr; }
+ ConstIterator(const T *p_ptr) { elem_ptr = p_ptr; }
ConstIterator() {}
ConstIterator(const ConstIterator &p_it) { elem_ptr = p_it.elem_ptr; }
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index 09cf785390..b4d6dffc6f 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -34,6 +34,7 @@
#include "core/object/class_db.h"
#include "core/object/script_language.h"
#include "core/templates/hashfuncs.h"
+#include "core/templates/search_array.h"
#include "core/templates/vector.h"
#include "core/variant/callable.h"
#include "core/variant/variant.h"
@@ -203,9 +204,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) {
@@ -484,44 +485,19 @@ void Array::shuffle() {
}
}
-template <typename Less>
-_FORCE_INLINE_ int bisect(const Vector<Variant> &p_array, const Variant &p_value, bool p_before, const Less &p_less) {
- int lo = 0;
- int hi = p_array.size();
- if (p_before) {
- while (lo < hi) {
- const int mid = (lo + hi) / 2;
- if (p_less(p_array.get(mid), p_value)) {
- lo = mid + 1;
- } else {
- hi = mid;
- }
- }
- } else {
- while (lo < hi) {
- const int mid = (lo + hi) / 2;
- if (p_less(p_value, p_array.get(mid))) {
- hi = mid;
- } else {
- lo = mid + 1;
- }
- }
- }
- return lo;
-}
-
int Array::bsearch(const Variant &p_value, bool p_before) {
ERR_FAIL_COND_V(!_p->typed.validate(p_value, "binary search"), -1);
- return bisect(_p->array, p_value, p_before, _ArrayVariantSort());
+ SearchArray<Variant, _ArrayVariantSort> avs;
+ return avs.bisect(_p->array.ptrw(), _p->array.size(), p_value, p_before);
}
int Array::bsearch_custom(const Variant &p_value, Callable p_callable, bool p_before) {
ERR_FAIL_COND_V(!_p->typed.validate(p_value, "custom binary search"), -1);
- _ArrayVariantSortCustom less;
- less.func = p_callable;
+ SearchArray<Variant, _ArrayVariantSortCustom> avs;
+ avs.compare.func = p_callable;
- return bisect(_p->array, p_value, p_before, less);
+ return avs.bisect(_p->array.ptrw(), _p->array.size(), p_value, p_before);
}
void Array::reverse() {
@@ -535,8 +511,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 +521,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 3cb2a6bfb5..3b2c837096 100644
--- a/core/variant/binder_common.h
+++ b/core/variant/binder_common.h
@@ -33,6 +33,7 @@
#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"
@@ -96,11 +97,14 @@ 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);
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index f487e718f4..dcded6e61f 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -377,11 +377,11 @@ Error Signal::emit(const Variant **p_arguments, int p_argcount) const {
return obj->emit_signal(name, p_arguments, p_argcount);
}
-Error Signal::connect(const Callable &p_callable, const Vector<Variant> &p_binds, uint32_t p_flags) {
+Error Signal::connect(const Callable &p_callable, uint32_t p_flags) {
Object *object = get_object();
ERR_FAIL_COND_V(!object, ERR_UNCONFIGURED);
- return object->connect(name, p_callable, p_binds, p_flags);
+ return object->connect(name, p_callable, varray(), p_flags);
}
void Signal::disconnect(const Callable &p_callable) {
diff --git a/core/variant/callable.h b/core/variant/callable.h
index 52094af3aa..de886492ea 100644
--- a/core/variant/callable.h
+++ b/core/variant/callable.h
@@ -159,7 +159,7 @@ public:
operator String() const;
Error emit(const Variant **p_arguments, int p_argcount) const;
- Error connect(const Callable &p_callable, const Vector<Variant> &p_binds = Vector<Variant>(), uint32_t p_flags = 0);
+ Error connect(const Callable &p_callable, uint32_t p_flags = 0);
void disconnect(const Callable &p_callable);
bool is_connected(const Callable &p_callable) 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/method_ptrcall.h b/core/variant/method_ptrcall.h
index 8836e257a9..98304621f2 100644
--- a/core/variant/method_ptrcall.h
+++ b/core/variant/method_ptrcall.h
@@ -191,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>> { \
@@ -236,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>> { \
@@ -285,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>> { \
@@ -325,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>> { \
@@ -372,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> { \
@@ -395,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) {
@@ -429,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..913d4d8f7c
--- /dev/null
+++ b/core/variant/native_ptr.h
@@ -0,0 +1,131 @@
+/*************************************************************************/
+/* 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<const 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(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 76cb065d10..b70d29bbac 100644
--- a/core/variant/type_info.h
+++ b/core/variant/type_info.h
@@ -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 900dcf7689..2e96f4e445 100644
--- a/core/variant/typed_array.h
+++ b/core/variant/typed_array.h
@@ -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;
@@ -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 97a1b4c02a..0dbeb6e4cb 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -1627,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:
@@ -2832,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);
@@ -2847,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);
@@ -2857,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;
@@ -2878,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);
@@ -3533,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 780f9b4e70..9ec131a1b8 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -208,7 +208,7 @@ private:
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);
@@ -271,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();
@@ -534,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);
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index f8538f71d3..6284caae2d 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1241,8 +1241,8 @@ void Variant::get_constants_for_type(Variant::Type p_type, List<StringName> *p_c
for (List<StringName>::Element *E = cd.value_ordered.front(); E; E = E->next()) {
p_constants->push_back(E->get());
#else
- for (Map<StringName, int>::Element *E = cd.value.front(); E; E = E->next()) {
- p_constants->push_back(E->key());
+ for (const KeyValue<StringName, int> &E : cd.value) {
+ p_constants->push_back(E.key);
#endif
}
@@ -1250,8 +1250,8 @@ void Variant::get_constants_for_type(Variant::Type p_type, List<StringName> *p_c
for (List<StringName>::Element *E = cd.variant_value_ordered.front(); E; E = E->next()) {
p_constants->push_back(E->get());
#else
- for (Map<StringName, Variant>::Element *E = cd.variant_value.front(); E; E = E->next()) {
- p_constants->push_back(E->key());
+ for (const KeyValue<StringName, Variant> &E : cd.variant_value) {
+ p_constants->push_back(E.key);
#endif
}
}
@@ -1421,7 +1421,8 @@ static void _register_variant_builtin_methods() {
//bind_method(String, humanize_size, sarray("size"), varray());
bind_method(String, is_absolute_path, sarray(), varray());
- bind_method(String, is_rel_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));
@@ -1501,6 +1502,8 @@ static void _register_variant_builtin_methods() {
bind_method(Vector2, clamp, sarray("min", "max"), varray());
bind_method(Vector2, snapped, sarray("step"), varray());
+ bind_static_method(Vector2, from_angle, sarray("angle"), varray());
+
/* Vector2i */
bind_method(Vector2i, aspect, sarray(), varray());
@@ -1510,6 +1513,7 @@ static void _register_variant_builtin_methods() {
/* Rect2 */
+ bind_method(Rect2, get_center, sarray(), varray());
bind_method(Rect2, get_area, sarray(), varray());
bind_method(Rect2, has_no_area, sarray(), varray());
bind_method(Rect2, has_point, sarray("point"), varray());
@@ -1526,6 +1530,7 @@ static void _register_variant_builtin_methods() {
/* Rect2i */
+ bind_method(Rect2i, get_center, sarray(), varray());
bind_method(Rect2i, get_area, sarray(), varray());
bind_method(Rect2i, has_no_area, sarray(), varray());
bind_method(Rect2i, has_point, sarray("point"), varray());
@@ -1565,7 +1570,6 @@ static void _register_variant_builtin_methods() {
bind_method(Vector3, dot, sarray("with"), varray());
bind_method(Vector3, cross, sarray("with"), varray());
bind_method(Vector3, outer, sarray("with"), varray());
- bind_method(Vector3, to_diagonal_matrix, sarray(), varray());
bind_method(Vector3, abs, sarray(), varray());
bind_method(Vector3, floor, sarray(), varray());
bind_method(Vector3, ceil, sarray(), varray());
@@ -1686,7 +1690,7 @@ static void _register_variant_builtin_methods() {
bind_method(Signal, get_object_id, sarray(), varray());
bind_method(Signal, get_name, sarray(), varray());
- bind_method(Signal, connect, sarray("callable", "binds", "flags"), varray(Array(), 0));
+ bind_method(Signal, connect, sarray("callable", "flags"), varray(0));
bind_method(Signal, disconnect, sarray("callable"), varray());
bind_method(Signal, is_connected, sarray("callable"), varray());
bind_method(Signal, get_connections, sarray(), varray());
@@ -1700,6 +1704,7 @@ static void _register_variant_builtin_methods() {
bind_method(Transform2D, get_rotation, sarray(), varray());
bind_method(Transform2D, get_origin, sarray(), varray());
bind_method(Transform2D, get_scale, sarray(), varray());
+ bind_method(Transform2D, get_skew, sarray(), varray());
bind_method(Transform2D, orthonormalized, sarray(), varray());
bind_method(Transform2D, rotated, sarray("phi"), varray());
bind_method(Transform2D, scaled, sarray("scale"), varray());
@@ -1709,7 +1714,9 @@ static void _register_variant_builtin_methods() {
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(Transform2D()));
+ bind_method(Transform2D, set_scale, sarray("scale"), varray());
+ bind_method(Transform2D, set_skew, sarray("skew"), varray());
+ bind_method(Transform2D, looking_at, sarray("target"), varray(Vector2()));
/* Basis */
@@ -1728,10 +1735,13 @@ static void _register_variant_builtin_methods() {
bind_method(Basis, slerp, sarray("to", "weight"), varray());
bind_method(Basis, is_equal_approx, sarray("b"), varray());
bind_method(Basis, get_rotation_quaternion, sarray(), varray());
+ bind_static_method(Basis, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0)));
+ bind_static_method(Basis, from_scale, sarray("scale"), varray());
/* AABB */
bind_method(AABB, abs, sarray(), varray());
+ bind_method(AABB, get_center, sarray(), varray());
bind_method(AABB, get_area, sarray(), varray());
bind_method(AABB, has_no_area, sarray(), varray());
bind_method(AABB, has_no_surface, sarray(), varray());
@@ -1805,6 +1815,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());
@@ -1834,6 +1845,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedByteArray, reverse, sarray(), varray());
bind_method(PackedByteArray, subarray, sarray("from", "to"), varray());
bind_method(PackedByteArray, sort, sarray(), varray());
+ bind_method(PackedByteArray, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedByteArray, duplicate, sarray(), varray());
bind_function(PackedByteArray, get_string_from_ascii, _VariantCall::func_PackedByteArray_get_string_from_ascii, sarray(), varray());
@@ -1895,6 +1907,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedInt32Array, subarray, sarray("from", "to"), varray());
bind_method(PackedInt32Array, to_byte_array, sarray(), varray());
bind_method(PackedInt32Array, sort, sarray(), varray());
+ bind_method(PackedInt32Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedInt32Array, duplicate, sarray(), varray());
/* Int64 Array */
@@ -1914,6 +1927,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedInt64Array, subarray, sarray("from", "to"), varray());
bind_method(PackedInt64Array, to_byte_array, sarray(), varray());
bind_method(PackedInt64Array, sort, sarray(), varray());
+ bind_method(PackedInt64Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedInt64Array, duplicate, sarray(), varray());
/* Float32 Array */
@@ -1933,6 +1947,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedFloat32Array, subarray, sarray("from", "to"), varray());
bind_method(PackedFloat32Array, to_byte_array, sarray(), varray());
bind_method(PackedFloat32Array, sort, sarray(), varray());
+ bind_method(PackedFloat32Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedFloat32Array, duplicate, sarray(), varray());
/* Float64 Array */
@@ -1952,6 +1967,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedFloat64Array, subarray, sarray("from", "to"), varray());
bind_method(PackedFloat64Array, to_byte_array, sarray(), varray());
bind_method(PackedFloat64Array, sort, sarray(), varray());
+ bind_method(PackedFloat64Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedFloat64Array, duplicate, sarray(), varray());
/* String Array */
@@ -1971,6 +1987,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedStringArray, subarray, sarray("from", "to"), varray());
bind_method(PackedStringArray, to_byte_array, sarray(), varray());
bind_method(PackedStringArray, sort, sarray(), varray());
+ bind_method(PackedStringArray, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedStringArray, duplicate, sarray(), varray());
/* Vector2 Array */
@@ -1990,6 +2007,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedVector2Array, subarray, sarray("from", "to"), varray());
bind_method(PackedVector2Array, to_byte_array, sarray(), varray());
bind_method(PackedVector2Array, sort, sarray(), varray());
+ bind_method(PackedVector2Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedVector2Array, duplicate, sarray(), varray());
/* Vector3 Array */
@@ -2009,6 +2027,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedVector3Array, subarray, sarray("from", "to"), varray());
bind_method(PackedVector3Array, to_byte_array, sarray(), varray());
bind_method(PackedVector3Array, sort, sarray(), varray());
+ bind_method(PackedVector3Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedVector3Array, duplicate, sarray(), varray());
/* Color Array */
@@ -2028,6 +2047,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedColorArray, subarray, sarray("from", "to"), varray());
bind_method(PackedColorArray, to_byte_array, sarray(), varray());
bind_method(PackedColorArray, sort, sarray(), varray());
+ bind_method(PackedColorArray, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedColorArray, duplicate, sarray(), varray());
/* Register constants */
diff --git a/core/variant/variant_construct.cpp b/core/variant/variant_construct.cpp
index a1a2bec369..4317b9dc98 100644
--- a/core/variant/variant_construct.cpp
+++ b/core/variant/variant_construct.cpp
@@ -114,6 +114,7 @@ void Variant::_register_variant_constructors() {
add_constructor<VariantConstructNoArgs<Transform2D>>(sarray());
add_constructor<VariantConstructor<Transform2D, Transform2D>>(sarray("from"));
add_constructor<VariantConstructor<Transform2D, float, Vector2>>(sarray("rotation", "position"));
+ add_constructor<VariantConstructor<Transform2D, float, Size2, float, Vector2>>(sarray("rotation", "scale", "skew", "position"));
add_constructor<VariantConstructor<Transform2D, Vector2, Vector2, Vector2>>(sarray("x_axis", "y_axis", "origin"));
add_constructor<VariantConstructNoArgs<Plane>>(sarray());
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 78e1ad06ae..40c8a1bfde 100644
--- a/core/variant/variant_internal.h
+++ b/core/variant/variant_internal.h
@@ -104,7 +104,7 @@ public:
init_color_array(v);
break;
case Variant::OBJECT:
- object_assign_null(v);
+ init_object(v);
break;
default:
break;
@@ -280,6 +280,10 @@ 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();
@@ -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 {
};
@@ -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 16c7428781..b94588f480 100644
--- a/core/variant/variant_op.cpp
+++ b/core/variant/variant_op.cpp
@@ -171,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);
@@ -183,7 +183,7 @@ 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);
@@ -195,10 +195,10 @@ void Variant::_register_variant_operators() {
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);
@@ -314,6 +314,74 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorEqual<PackedVector3Array, PackedVector3Array>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::PACKED_VECTOR3_ARRAY);
register_op<OperatorEvaluatorEqual<PackedColorArray, PackedColorArray>>(Variant::OP_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::PACKED_COLOR_ARRAY);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::BOOL, Variant::NIL>>(Variant::OP_EQUAL, Variant::BOOL, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::INT, Variant::NIL>>(Variant::OP_EQUAL, Variant::INT, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::FLOAT, Variant::NIL>>(Variant::OP_EQUAL, Variant::FLOAT, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::STRING, Variant::NIL>>(Variant::OP_EQUAL, Variant::STRING, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR2, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR2, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR2I, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR2I, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::RECT2, Variant::NIL>>(Variant::OP_EQUAL, Variant::RECT2, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::RECT2I, Variant::NIL>>(Variant::OP_EQUAL, Variant::RECT2I, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR3, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR3, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR3I, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR3I, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::TRANSFORM2D, Variant::NIL>>(Variant::OP_EQUAL, Variant::TRANSFORM2D, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PLANE, Variant::NIL>>(Variant::OP_EQUAL, Variant::PLANE, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::QUATERNION, Variant::NIL>>(Variant::OP_EQUAL, Variant::QUATERNION, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::AABB, Variant::NIL>>(Variant::OP_EQUAL, Variant::AABB, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::BASIS, Variant::NIL>>(Variant::OP_EQUAL, Variant::BASIS, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::TRANSFORM3D, Variant::NIL>>(Variant::OP_EQUAL, Variant::TRANSFORM3D, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::COLOR, Variant::NIL>>(Variant::OP_EQUAL, Variant::COLOR, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::STRING_NAME, Variant::NIL>>(Variant::OP_EQUAL, Variant::STRING_NAME, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NODE_PATH, Variant::NIL>>(Variant::OP_EQUAL, Variant::NODE_PATH, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::RID, Variant::NIL>>(Variant::OP_EQUAL, Variant::RID, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::CALLABLE, Variant::NIL>>(Variant::OP_EQUAL, Variant::CALLABLE, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::SIGNAL, Variant::NIL>>(Variant::OP_EQUAL, Variant::SIGNAL, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::DICTIONARY, Variant::NIL>>(Variant::OP_EQUAL, Variant::DICTIONARY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_BYTE_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_BYTE_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_INT32_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_INT32_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_INT64_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_INT64_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_FLOAT32_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_FLOAT32_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_FLOAT64_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_FLOAT64_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_STRING_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_STRING_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL);
+
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::BOOL>>(Variant::OP_EQUAL, Variant::NIL, Variant::BOOL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::INT>>(Variant::OP_EQUAL, Variant::NIL, Variant::INT);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::FLOAT>>(Variant::OP_EQUAL, Variant::NIL, Variant::FLOAT);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::STRING>>(Variant::OP_EQUAL, Variant::NIL, Variant::STRING);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR2>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR2);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR2I>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR2I);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::RECT2>>(Variant::OP_EQUAL, Variant::NIL, Variant::RECT2);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::RECT2I>>(Variant::OP_EQUAL, Variant::NIL, Variant::RECT2I);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3I>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3I);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::TRANSFORM2D>>(Variant::OP_EQUAL, Variant::NIL, Variant::TRANSFORM2D);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PLANE>>(Variant::OP_EQUAL, Variant::NIL, Variant::PLANE);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::QUATERNION>>(Variant::OP_EQUAL, Variant::NIL, Variant::QUATERNION);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::AABB>>(Variant::OP_EQUAL, Variant::NIL, Variant::AABB);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::BASIS>>(Variant::OP_EQUAL, Variant::NIL, Variant::BASIS);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::TRANSFORM3D>>(Variant::OP_EQUAL, Variant::NIL, Variant::TRANSFORM3D);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::COLOR>>(Variant::OP_EQUAL, Variant::NIL, Variant::COLOR);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::STRING_NAME>>(Variant::OP_EQUAL, Variant::NIL, Variant::STRING_NAME);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::NODE_PATH>>(Variant::OP_EQUAL, Variant::NIL, Variant::NODE_PATH);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::RID>>(Variant::OP_EQUAL, Variant::NIL, Variant::RID);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::CALLABLE>>(Variant::OP_EQUAL, Variant::NIL, Variant::CALLABLE);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::SIGNAL>>(Variant::OP_EQUAL, Variant::NIL, Variant::SIGNAL);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::DICTIONARY>>(Variant::OP_EQUAL, Variant::NIL, Variant::DICTIONARY);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::ARRAY);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_BYTE_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_BYTE_ARRAY);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_INT32_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_INT32_ARRAY);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_INT64_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_INT64_ARRAY);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_FLOAT32_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_FLOAT32_ARRAY);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_FLOAT64_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_FLOAT64_ARRAY);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_STRING_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_STRING_ARRAY);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY);
+ register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY);
+
register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NIL);
register_op<OperatorEvaluatorNotEqual<bool, bool>>(Variant::OP_NOT_EQUAL, Variant::BOOL, Variant::BOOL);
register_op<OperatorEvaluatorNotEqual<int64_t, int64_t>>(Variant::OP_NOT_EQUAL, Variant::INT, Variant::INT);
@@ -360,6 +428,74 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorNotEqual<PackedVector3Array, PackedVector3Array>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::PACKED_VECTOR3_ARRAY);
register_op<OperatorEvaluatorNotEqual<PackedColorArray, PackedColorArray>>(Variant::OP_NOT_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::PACKED_COLOR_ARRAY);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::BOOL, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::BOOL, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::INT, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::INT, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::FLOAT, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::FLOAT, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::STRING, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::STRING, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR2, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR2, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR2I, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR2I, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::RECT2, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::RECT2, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::RECT2I, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::RECT2I, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR3, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR3, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR3I, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR3I, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::TRANSFORM2D, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::TRANSFORM2D, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PLANE, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PLANE, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::QUATERNION, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::QUATERNION, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::AABB, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::AABB, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::BASIS, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::BASIS, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::TRANSFORM3D, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::TRANSFORM3D, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::COLOR, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::COLOR, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::STRING_NAME, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::STRING_NAME, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NODE_PATH, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::NODE_PATH, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::RID, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::RID, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::CALLABLE, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::CALLABLE, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::SIGNAL, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::SIGNAL, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::DICTIONARY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::DICTIONARY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_BYTE_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_BYTE_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_INT32_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_INT32_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_INT64_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_INT64_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_FLOAT32_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_FLOAT32_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_FLOAT64_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_FLOAT64_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_STRING_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_STRING_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL);
+
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::BOOL>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::BOOL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::INT>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::INT);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::FLOAT>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::FLOAT);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::STRING>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::STRING);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR2>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR2);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR2I>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR2I);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RECT2>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RECT2);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RECT2I>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RECT2I);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3I>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3I);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::TRANSFORM2D>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::TRANSFORM2D);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PLANE>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PLANE);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::QUATERNION>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::QUATERNION);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::AABB>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::AABB);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::BASIS>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::BASIS);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::TRANSFORM3D>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::TRANSFORM3D);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::COLOR>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::COLOR);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::STRING_NAME>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::STRING_NAME);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NODE_PATH>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NODE_PATH);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RID>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RID);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::CALLABLE>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::CALLABLE);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::SIGNAL>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::SIGNAL);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::DICTIONARY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::DICTIONARY);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::ARRAY);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_BYTE_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_BYTE_ARRAY);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_INT32_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_INT32_ARRAY);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_INT64_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_INT64_ARRAY);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_FLOAT32_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_FLOAT32_ARRAY);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_FLOAT64_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_FLOAT64_ARRAY);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_STRING_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_STRING_ARRAY);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY);
+ register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY);
+
register_op<OperatorEvaluatorLess<bool, bool>>(Variant::OP_LESS, Variant::BOOL, Variant::BOOL);
register_op<OperatorEvaluatorLess<int64_t, int64_t>>(Variant::OP_LESS, Variant::INT, Variant::INT);
register_op<OperatorEvaluatorLess<int64_t, double>>(Variant::OP_LESS, Variant::INT, Variant::FLOAT);
diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h
index cbdd60f404..3c9f849a4f 100644
--- a/core/variant/variant_op.h
+++ b/core/variant/variant_op.h
@@ -168,6 +168,54 @@ public:
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:
@@ -209,6 +257,54 @@ public:
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:
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index 50c48fd386..221a8c4f98 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -90,6 +90,17 @@ const char *VariantParser::tk_name[TK_MAX] = {
"ERROR"
};
+static double stor_fix(const String &p_str) {
+ if (p_str == "inf") {
+ return INFINITY;
+ } else if (p_str == "inf_neg") {
+ return -INFINITY;
+ } else if (p_str == "nan") {
+ return NAN;
+ }
+ return -1;
+}
+
Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, String &r_err_str) {
bool string_name = false;
@@ -469,8 +480,19 @@ Error VariantParser::_parse_construct(Stream *p_stream, Vector<T> &r_construct,
if (first && token.type == TK_PARENTHESIS_CLOSE) {
break;
} else if (token.type != TK_NUMBER) {
- r_err_str = "Expected float in constructor";
- return ERR_PARSE_ERROR;
+ bool valid = false;
+ if (token.type == TK_IDENTIFIER) {
+ double real = stor_fix(token.value);
+ if (real != -1) {
+ token.type = TK_NUMBER;
+ token.value = real;
+ valid = true;
+ }
+ }
+ if (!valid) {
+ r_err_str = "Expected float in constructor";
+ return ERR_PARSE_ERROR;
+ }
}
r_construct.push_back(token.value);
@@ -507,6 +529,8 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
value = Variant();
} else if (id == "inf") {
value = INFINITY;
+ } else if (id == "inf_neg") {
+ value = -INFINITY;
} else if (id == "nan") {
value = NAN;
} else if (id == "Vector2") {
@@ -1401,11 +1425,19 @@ Error VariantParser::parse(Stream *p_stream, Variant &r_ret, String &r_err_str,
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////
-static String rtosfix(double p_value) {
+static String rtos_fix(double p_value) {
if (p_value == 0.0) {
return "0"; //avoid negative zero (-0) being written, which may annoy git, svn, etc. for changes when they don't exist.
+ } else if (isnan(p_value)) {
+ return "nan";
+ } else if (isinf(p_value)) {
+ if (p_value > 0) {
+ return "inf";
+ } else {
+ return "inf_neg";
+ }
} else {
return rtoss(p_value);
}
@@ -1423,8 +1455,8 @@ 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());
- if (s != "inf" && s != "nan") {
+ String s = rtos_fix(p_variant.operator double());
+ if (s != "inf" && s != "inf_neg" && s != "nan") {
if (s.find(".") == -1 && s.find("e") == -1) {
s += ".0";
}
@@ -1439,7 +1471,7 @@ 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(" + rtos_fix(v.x) + ", " + rtos_fix(v.y) + ")");
} break;
case Variant::VECTOR2I: {
Vector2i v = p_variant;
@@ -1447,7 +1479,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
} 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(" + rtos_fix(aabb.position.x) + ", " + rtos_fix(aabb.position.y) + ", " + rtos_fix(aabb.size.x) + ", " + rtos_fix(aabb.size.y) + ")");
} break;
case Variant::RECT2I: {
@@ -1457,7 +1489,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
} 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(" + rtos_fix(v.x) + ", " + rtos_fix(v.y) + ", " + rtos_fix(v.z) + ")");
} break;
case Variant::VECTOR3I: {
Vector3i v = p_variant;
@@ -1465,17 +1497,17 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
} 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(" + rtos_fix(p.normal.x) + ", " + rtos_fix(p.normal.y) + ", " + rtos_fix(p.normal.z) + ", " + rtos_fix(p.d) + ")");
} break;
case Variant::AABB: {
AABB aabb = p_variant;
- p_store_string_func(p_store_string_ud, "AABB(" + 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(" + rtos_fix(aabb.position.x) + ", " + rtos_fix(aabb.position.y) + ", " + rtos_fix(aabb.position.z) + ", " + rtos_fix(aabb.size.x) + ", " + rtos_fix(aabb.size.y) + ", " + rtos_fix(aabb.size.z) + ")");
} break;
case Variant::QUATERNION: {
Quaternion quaternion = p_variant;
- p_store_string_func(p_store_string_ud, "Quaternion(" + rtosfix(quaternion.x) + ", " + rtosfix(quaternion.y) + ", " + rtosfix(quaternion.z) + ", " + rtosfix(quaternion.w) + ")");
+ p_store_string_func(p_store_string_ud, "Quaternion(" + rtos_fix(quaternion.x) + ", " + rtos_fix(quaternion.y) + ", " + rtos_fix(quaternion.z) + ", " + rtos_fix(quaternion.w) + ")");
} break;
case Variant::TRANSFORM2D: {
@@ -1486,7 +1518,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
if (i != 0 || j != 0) {
s += ", ";
}
- s += rtosfix(m3.elements[i][j]);
+ s += rtos_fix(m3.elements[i][j]);
}
}
@@ -1501,7 +1533,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
if (i != 0 || j != 0) {
s += ", ";
}
- s += rtosfix(m3.elements[i][j]);
+ s += rtos_fix(m3.elements[i][j]);
}
}
@@ -1517,11 +1549,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
if (i != 0 || j != 0) {
s += ", ";
}
- s += rtosfix(m3.elements[i][j]);
+ s += rtos_fix(m3.elements[i][j]);
}
}
- s = s + ", " + rtosfix(t.origin.x) + ", " + rtosfix(t.origin.y) + ", " + rtosfix(t.origin.z);
+ s = s + ", " + rtos_fix(t.origin.x) + ", " + rtos_fix(t.origin.y) + ", " + rtos_fix(t.origin.z);
p_store_string_func(p_store_string_ud, s + ")");
} break;
@@ -1529,7 +1561,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
// 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(" + rtos_fix(c.r) + ", " + rtos_fix(c.g) + ", " + rtos_fix(c.b) + ", " + rtos_fix(c.a) + ")");
} break;
case Variant::STRING_NAME: {
@@ -1707,7 +1739,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
if (i > 0) {
p_store_string_func(p_store_string_ud, ", ");
}
- p_store_string_func(p_store_string_ud, rtosfix(ptr[i]));
+ p_store_string_func(p_store_string_ud, rtos_fix(ptr[i]));
}
p_store_string_func(p_store_string_ud, ")");
@@ -1723,7 +1755,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
if (i > 0) {
p_store_string_func(p_store_string_ud, ", ");
}
- p_store_string_func(p_store_string_ud, rtosfix(ptr[i]));
+ p_store_string_func(p_store_string_ud, rtos_fix(ptr[i]));
}
p_store_string_func(p_store_string_ud, ")");
@@ -1759,7 +1791,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
if (i > 0) {
p_store_string_func(p_store_string_ud, ", ");
}
- p_store_string_func(p_store_string_ud, rtosfix(ptr[i].x) + ", " + rtosfix(ptr[i].y));
+ p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].x) + ", " + rtos_fix(ptr[i].y));
}
p_store_string_func(p_store_string_ud, ")");
@@ -1775,7 +1807,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
if (i > 0) {
p_store_string_func(p_store_string_ud, ", ");
}
- 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, rtos_fix(ptr[i].x) + ", " + rtos_fix(ptr[i].y) + ", " + rtos_fix(ptr[i].z));
}
p_store_string_func(p_store_string_ud, ")");
@@ -1792,7 +1824,7 @@ 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, rtosfix(ptr[i].r) + ", " + rtosfix(ptr[i].g) + ", " + rtosfix(ptr[i].b) + ", " + rtosfix(ptr[i].a));
+ p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].r) + ", " + rtos_fix(ptr[i].g) + ", " + rtos_fix(ptr[i].b) + ", " + rtos_fix(ptr[i].a));
}
p_store_string_func(p_store_string_ud, ")");
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index ae3c7685fd..3bba68d75e 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -661,6 +661,91 @@ struct VariantIndexedSetGet_Array {
static uint64_t get_indexed_size(const Variant *base) { return 0; }
};
+struct VariantIndexedSetGet_String {
+ static void get(const Variant *base, int64_t index, Variant *value, bool *oob) {
+ int64_t length = VariantGetInternalPtr<String>::get_ptr(base)->length();
+ if (index < 0) {
+ index += length;
+ }
+ if (index < 0 || index >= length) {
+ *oob = true;
+ return;
+ }
+ char32_t result = (*VariantGetInternalPtr<String>::get_ptr(base))[index];
+ *value = String(&result, 1);
+ *oob = false;
+ }
+ static void ptr_get(const void *base, int64_t index, void *member) {
+ /* avoid ptrconvert for performance*/
+ const String &v = *reinterpret_cast<const String *>(base);
+ if (index < 0) {
+ index += v.length();
+ }
+ OOB_TEST(index, v.length());
+ char32_t c = v[index];
+ PtrToArg<String>::encode(String(&c, 1), member);
+ }
+ static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) {
+ if (value->get_type() != Variant::STRING) {
+ *oob = false;
+ *valid = false;
+ return;
+ }
+ int64_t length = VariantGetInternalPtr<String>::get_ptr(base)->length();
+ if (index < 0) {
+ index += length;
+ }
+ if (index < 0 || index >= length) {
+ *oob = true;
+ *valid = false;
+ return;
+ }
+ String *b = VariantGetInternalPtr<String>::get_ptr(base);
+ const String *v = VariantInternal::get_string(value);
+ if (v->length() == 0) {
+ b->remove(index);
+ } else {
+ b->set(index, v->get(0));
+ }
+ *oob = false;
+ *valid = true;
+ }
+ static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) {
+ int64_t length = VariantGetInternalPtr<String>::get_ptr(base)->length();
+ if (index < 0) {
+ index += length;
+ }
+ if (index < 0 || index >= length) {
+ *oob = true;
+ return;
+ }
+ String *b = VariantGetInternalPtr<String>::get_ptr(base);
+ const String *v = VariantInternal::get_string(value);
+ if (v->length() == 0) {
+ b->remove(index);
+ } else {
+ b->set(index, v->get(0));
+ }
+ *oob = false;
+ }
+ static void ptr_set(void *base, int64_t index, const void *member) {
+ /* avoid ptrconvert for performance*/
+ String &v = *reinterpret_cast<String *>(base);
+ if (index < 0) {
+ index += v.length();
+ }
+ OOB_TEST(index, v.length());
+ const String &m = *reinterpret_cast<const String *>(member);
+ if (unlikely(m.length() == 0)) {
+ v.remove(index);
+ } else {
+ v.set(index, m.unicode_at(0));
+ }
+ }
+ static Variant::Type get_index_type() { return Variant::STRING; }
+ static uint64_t get_indexed_size(const Variant *base) { return VariantInternal::get_string(base)->length(); }
+};
+
#define INDEXED_SETGET_STRUCT_DICT(m_base_type) \
struct VariantIndexedSetGet_##m_base_type { \
static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \
@@ -758,6 +843,7 @@ static void register_indexed_member(Variant::Type p_type) {
void register_indexed_setters_getters() {
#define REGISTER_INDEXED_MEMBER(m_base_type) register_indexed_member<VariantIndexedSetGet_##m_base_type>(GetTypeInfo<m_base_type>::VARIANT_TYPE)
+ REGISTER_INDEXED_MEMBER(String);
REGISTER_INDEXED_MEMBER(Vector2);
REGISTER_INDEXED_MEMBER(Vector2i);
REGISTER_INDEXED_MEMBER(Vector3);
diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index 34dbf130b6..55c1376031 100644
--- a/core/variant/variant_utility.cpp
+++ b/core/variant/variant_utility.cpp
@@ -35,6 +35,8 @@
#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"
@@ -265,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);
}
@@ -484,11 +478,15 @@ struct VariantUtilityFunctions {
return str;
}
- 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;
- r_error.argument = 1;
+ 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) {
String str;
for (int i = 0; i < p_arg_count; i++) {
String os = p_args[i]->operator String();
@@ -504,11 +502,29 @@ struct VariantUtilityFunctions {
r_error.error = Callable::CallError::CALL_OK;
}
- static inline void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
+ static inline void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ String str;
+ for (int i = 0; i < p_arg_count; i++) {
+ String os = p_args[i]->operator String();
+
+ if (i == 0) {
+ str = os;
+ } else {
+ str += os;
+ }
+ }
+
+ // No need to use `print_verbose()` as this call already only happens
+ // when verbose mode is enabled. This avoids performing string argument concatenation
+ // when not needed.
+ print_line(str);
}
+
+ r_error.error = Callable::CallError::CALL_OK;
+ }
+
+ static inline void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
String str;
for (int i = 0; i < p_arg_count; i++) {
String os = p_args[i]->operator String();
@@ -525,10 +541,6 @@ struct VariantUtilityFunctions {
}
static inline void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- }
String str;
for (int i = 0; i < p_arg_count; i++) {
if (i) {
@@ -542,10 +554,6 @@ struct VariantUtilityFunctions {
}
static inline void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- }
String str;
for (int i = 0; i < p_arg_count; i++) {
if (i) {
@@ -559,10 +567,6 @@ struct VariantUtilityFunctions {
}
static inline void printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- }
String str;
for (int i = 0; i < p_arg_count; i++) {
String os = p_args[i]->operator String();
@@ -720,6 +724,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
@@ -1197,9 +1208,6 @@ void Variant::_register_variant_utility_functions() {
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);
@@ -1234,11 +1242,13 @@ 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);
FUNCBINDVARARGV(prints, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDVARARGV(printraw, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);
+ FUNCBINDVARARGV(print_verbose, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDVARARGV(push_error, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDVARARGV(push_warning, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);
@@ -1256,6 +1266,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() {