diff options
Diffstat (limited to 'core')
109 files changed, 4770 insertions, 1672 deletions
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..03892d1d4f 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1006,7 +1006,7 @@ bool ProjectSettings::has_custom_feature(const String &p_feature) const { return custom_features.has(p_feature); } -Map<StringName, ProjectSettings::AutoloadInfo> ProjectSettings::get_autoload_list() const { +OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> ProjectSettings::get_autoload_list() const { return autoloads; } diff --git a/core/config/project_settings.h b/core/config/project_settings.h index ed8fb19fa0..7e93f26f0d 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -33,6 +33,7 @@ #include "core/object/class_db.h" #include "core/os/thread_safe.h" +#include "core/templates/ordered_hash_map.h" #include "core/templates/set.h" class ProjectSettings : public Object { @@ -91,7 +92,7 @@ protected: Set<String> custom_features; Map<StringName, StringName> feature_overrides; - Map<StringName, AutoloadInfo> autoloads; + OrderedHashMap<StringName, AutoloadInfo> autoloads; bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -168,7 +169,7 @@ public: bool has_custom_feature(const String &p_feature) const; - Map<StringName, AutoloadInfo> get_autoload_list() const; + OrderedHashMap<StringName, AutoloadInfo> get_autoload_list() const; void add_autoload(const AutoloadInfo &p_autoload); void remove_autoload(const StringName &p_autoload); bool has_autoload(const StringName &p_autoload) const; diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 6f6c4056a9..fd5b3bb731 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,81 +282,81 @@ 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_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; @@ -363,7 +370,7 @@ void _OS::print_all_textures_by_size() { 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(); @@ -383,7 +390,7 @@ void _OS::print_all_textures_by_size() { "Path - VRAM usage (Dimensions)"); } - for (const _OSCoreBindImg &img : imgs) { + for (const OSCoreBindImg &img : imgs) { print_line(vformat("%s - %s %s", img.path, String::humanize_size(img.vram), @@ -393,7 +400,7 @@ void _OS::print_all_textures_by_size() { print_line(vformat("Total VRAM usage: %s.", String::humanize_size(total))); } -void _OS::print_resources_by_type(const Vector<String> &p_types) { +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."); @@ -435,42 +442,38 @@ void _OS::print_resources_by_type(const Vector<String> &p_types) { } } -void _OS::print_all_resources(const String &p_to_file) { - OS::get_singleton()->print_all_resources(p_to_file); -} - -void _OS::print_resources_in_use(bool p_short) { - OS::get_singleton()->print_resources_in_use(p_short); +void OS::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 @@ -478,114 +481,113 @@ bool _OS::is_debug_build() const { #endif } -String _OS::get_system_dir(SystemDir p_dir) const { - return OS::get_singleton()->get_system_dir(OS::SystemDir(p_dir)); +String OS::get_system_dir(SystemDir p_dir, bool p_shared_storage) const { + return ::OS::get_singleton()->get_system_dir(::OS::SystemDir(p_dir), p_shared_storage); } -String _OS::get_keycode_string(uint32_t p_code) const { - return keycode_get_string(p_code); +String OS::get_keycode_string(uint32_t p_code) const { + return ::keycode_get_string(p_code); } -bool _OS::is_keycode_unicode(uint32_t p_unicode) const { - return keycode_has_unicode(p_unicode); +bool OS::is_keycode_unicode(uint32_t p_unicode) const { + return ::keycode_has_unicode(p_unicode); } -int _OS::find_keycode_from_string(const String &p_code) const { +int OS::find_keycode_from_string(const String &p_code) const { return find_keycode(p_code); } -bool _OS::request_permission(const String &p_name) { - return OS::get_singleton()->request_permission(p_name); +bool OS::request_permission(const String &p_name) { + return ::OS::get_singleton()->request_permission(p_name); } -bool _OS::request_permissions() { - return OS::get_singleton()->request_permissions(); +bool OS::request_permissions() { + return ::OS::get_singleton()->request_permissions(); } -Vector<String> _OS::get_granted_permissions() const { - return OS::get_singleton()->get_granted_permissions(); +Vector<String> OS::get_granted_permissions() const { + return ::OS::get_singleton()->get_granted_permissions(); } -String _OS::get_unique_id() const { - return OS::get_singleton()->get_unique_id(); +String OS::get_unique_id() const { + return ::OS::get_singleton()->get_unique_id(); } -_OS *_OS::singleton = nullptr; +OS *OS::singleton = nullptr; -void _OS::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_connected_midi_inputs"), &_OS::get_connected_midi_inputs); - ClassDB::bind_method(D_METHOD("open_midi_inputs"), &_OS::open_midi_inputs); - ClassDB::bind_method(D_METHOD("close_midi_inputs"), &_OS::close_midi_inputs); +void OS::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_connected_midi_inputs"), &OS::get_connected_midi_inputs); + ClassDB::bind_method(D_METHOD("open_midi_inputs"), &OS::open_midi_inputs); + ClassDB::bind_method(D_METHOD("close_midi_inputs"), &OS::close_midi_inputs); - ClassDB::bind_method(D_METHOD("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_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"); @@ -630,43 +632,43 @@ void _OS::_bind_methods() { BIND_ENUM_CONSTANT(SYSTEM_DIR_RINGTONES); } -////// _Geometry2D ////// +////// Geometry2D ////// -_Geometry2D *_Geometry2D::singleton = nullptr; +Geometry2D *Geometry2D::singleton = nullptr; -_Geometry2D *_Geometry2D::get_singleton() { +Geometry2D *Geometry2D::get_singleton() { return singleton; } -bool _Geometry2D::is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius) { - return Geometry2D::is_point_in_circle(p_point, p_circle_pos, p_circle_radius); +bool Geometry2D::is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius) { + return ::Geometry2D::is_point_in_circle(p_point, p_circle_pos, p_circle_radius); } -real_t _Geometry2D::segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) { - return Geometry2D::segment_intersects_circle(p_from, p_to, p_circle_pos, p_circle_radius); +real_t Geometry2D::segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) { + return ::Geometry2D::segment_intersects_circle(p_from, p_to, p_circle_pos, p_circle_radius); } -Variant _Geometry2D::segment_intersects_segment(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b) { +Variant Geometry2D::segment_intersects_segment(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b) { Vector2 result; - if (Geometry2D::segment_intersects_segment(p_from_a, p_to_a, p_from_b, p_to_b, &result)) { + if (::Geometry2D::segment_intersects_segment(p_from_a, p_to_a, p_from_b, p_to_b, &result)) { return result; } else { return Variant(); } } -Variant _Geometry2D::line_intersects_line(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b) { +Variant Geometry2D::line_intersects_line(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b) { Vector2 result; - if (Geometry2D::line_intersects_line(p_from_a, p_dir_a, p_from_b, p_dir_b, result)) { + if (::Geometry2D::line_intersects_line(p_from_a, p_dir_a, p_from_b, p_dir_b, result)) { return result; } else { return Variant(); } } -Vector<Vector2> _Geometry2D::get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2) { +Vector<Vector2> Geometry2D::get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2) { Vector2 r1, r2; - Geometry2D::get_closest_points_between_segments(p1, q1, p2, q2, r1, r2); + ::Geometry2D::get_closest_points_between_segments(p1, q1, p2, q2, r1, r2); Vector<Vector2> r; r.resize(2); r.set(0, r1); @@ -674,42 +676,42 @@ Vector<Vector2> _Geometry2D::get_closest_points_between_segments(const Vector2 & return r; } -Vector2 _Geometry2D::get_closest_point_to_segment(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b) { +Vector2 Geometry2D::get_closest_point_to_segment(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b) { Vector2 s[2] = { p_a, p_b }; - return Geometry2D::get_closest_point_to_segment(p_point, s); + return ::Geometry2D::get_closest_point_to_segment(p_point, s); } -Vector2 _Geometry2D::get_closest_point_to_segment_uncapped(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b) { +Vector2 Geometry2D::get_closest_point_to_segment_uncapped(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b) { Vector2 s[2] = { p_a, p_b }; - return Geometry2D::get_closest_point_to_segment_uncapped(p_point, s); + return ::Geometry2D::get_closest_point_to_segment_uncapped(p_point, s); } -bool _Geometry2D::point_is_inside_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) const { - return Geometry2D::is_point_in_triangle(s, a, b, c); +bool Geometry2D::point_is_inside_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) const { + return ::Geometry2D::is_point_in_triangle(s, a, b, c); } -bool _Geometry2D::is_polygon_clockwise(const Vector<Vector2> &p_polygon) { - return Geometry2D::is_polygon_clockwise(p_polygon); +bool Geometry2D::is_polygon_clockwise(const Vector<Vector2> &p_polygon) { + return ::Geometry2D::is_polygon_clockwise(p_polygon); } -bool _Geometry2D::is_point_in_polygon(const Point2 &p_point, const Vector<Vector2> &p_polygon) { - return Geometry2D::is_point_in_polygon(p_point, p_polygon); +bool Geometry2D::is_point_in_polygon(const Point2 &p_point, const Vector<Vector2> &p_polygon) { + return ::Geometry2D::is_point_in_polygon(p_point, p_polygon); } -Vector<int> _Geometry2D::triangulate_polygon(const Vector<Vector2> &p_polygon) { - return Geometry2D::triangulate_polygon(p_polygon); +Vector<int> Geometry2D::triangulate_polygon(const Vector<Vector2> &p_polygon) { + return ::Geometry2D::triangulate_polygon(p_polygon); } -Vector<int> _Geometry2D::triangulate_delaunay(const Vector<Vector2> &p_points) { - return Geometry2D::triangulate_delaunay(p_points); +Vector<int> Geometry2D::triangulate_delaunay(const Vector<Vector2> &p_points) { + return ::Geometry2D::triangulate_delaunay(p_points); } -Vector<Point2> _Geometry2D::convex_hull(const Vector<Point2> &p_points) { - return Geometry2D::convex_hull(p_points); +Vector<Point2> Geometry2D::convex_hull(const Vector<Point2> &p_points) { + return ::Geometry2D::convex_hull(p_points); } -Array _Geometry2D::merge_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { - Vector<Vector<Point2>> polys = Geometry2D::merge_polygons(p_polygon_a, p_polygon_b); +Array Geometry2D::merge_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { + Vector<Vector<Point2>> polys = ::Geometry2D::merge_polygons(p_polygon_a, p_polygon_b); Array ret; @@ -719,8 +721,8 @@ Array _Geometry2D::merge_polygons(const Vector<Vector2> &p_polygon_a, const Vect return ret; } -Array _Geometry2D::clip_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { - Vector<Vector<Point2>> polys = Geometry2D::clip_polygons(p_polygon_a, p_polygon_b); +Array Geometry2D::clip_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { + Vector<Vector<Point2>> polys = ::Geometry2D::clip_polygons(p_polygon_a, p_polygon_b); Array ret; @@ -730,8 +732,8 @@ Array _Geometry2D::clip_polygons(const Vector<Vector2> &p_polygon_a, const Vecto return ret; } -Array _Geometry2D::intersect_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { - Vector<Vector<Point2>> polys = Geometry2D::intersect_polygons(p_polygon_a, p_polygon_b); +Array Geometry2D::intersect_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { + Vector<Vector<Point2>> polys = ::Geometry2D::intersect_polygons(p_polygon_a, p_polygon_b); Array ret; @@ -741,8 +743,8 @@ Array _Geometry2D::intersect_polygons(const Vector<Vector2> &p_polygon_a, const return ret; } -Array _Geometry2D::exclude_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { - Vector<Vector<Point2>> polys = Geometry2D::exclude_polygons(p_polygon_a, p_polygon_b); +Array Geometry2D::exclude_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { + Vector<Vector<Point2>> polys = ::Geometry2D::exclude_polygons(p_polygon_a, p_polygon_b); Array ret; @@ -752,8 +754,8 @@ Array _Geometry2D::exclude_polygons(const Vector<Vector2> &p_polygon_a, const Ve return ret; } -Array _Geometry2D::clip_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { - Vector<Vector<Point2>> polys = Geometry2D::clip_polyline_with_polygon(p_polyline, p_polygon); +Array Geometry2D::clip_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { + Vector<Vector<Point2>> polys = ::Geometry2D::clip_polyline_with_polygon(p_polyline, p_polygon); Array ret; @@ -763,8 +765,8 @@ Array _Geometry2D::clip_polyline_with_polygon(const Vector<Vector2> &p_polyline, return ret; } -Array _Geometry2D::intersect_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { - Vector<Vector<Point2>> polys = Geometry2D::intersect_polyline_with_polygon(p_polyline, p_polygon); +Array Geometry2D::intersect_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { + Vector<Vector<Point2>> polys = ::Geometry2D::intersect_polyline_with_polygon(p_polyline, p_polygon); Array ret; @@ -774,8 +776,8 @@ Array _Geometry2D::intersect_polyline_with_polygon(const Vector<Vector2> &p_poly return ret; } -Array _Geometry2D::offset_polygon(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) { - Vector<Vector<Point2>> polys = Geometry2D::offset_polygon(p_polygon, p_delta, Geometry2D::PolyJoinType(p_join_type)); +Array Geometry2D::offset_polygon(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) { + Vector<Vector<Point2>> polys = ::Geometry2D::offset_polygon(p_polygon, p_delta, ::Geometry2D::PolyJoinType(p_join_type)); Array ret; @@ -785,8 +787,8 @@ Array _Geometry2D::offset_polygon(const Vector<Vector2> &p_polygon, real_t p_del return ret; } -Array _Geometry2D::offset_polyline(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { - Vector<Vector<Point2>> polys = Geometry2D::offset_polyline(p_polygon, p_delta, Geometry2D::PolyJoinType(p_join_type), Geometry2D::PolyEndType(p_end_type)); +Array Geometry2D::offset_polyline(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { + Vector<Vector<Point2>> polys = ::Geometry2D::offset_polyline(p_polygon, p_delta, ::Geometry2D::PolyJoinType(p_join_type), ::Geometry2D::PolyEndType(p_end_type)); Array ret; @@ -796,7 +798,7 @@ Array _Geometry2D::offset_polyline(const Vector<Vector2> &p_polygon, real_t p_de return ret; } -Dictionary _Geometry2D::make_atlas(const Vector<Size2> &p_rects) { +Dictionary Geometry2D::make_atlas(const Vector<Size2> &p_rects) { Dictionary ret; Vector<Size2i> rects; @@ -807,7 +809,7 @@ Dictionary _Geometry2D::make_atlas(const Vector<Size2> &p_rects) { Vector<Point2i> result; Size2i size; - Geometry2D::make_atlas(rects, result, size); + ::Geometry2D::make_atlas(rects, result, size); Size2 r_size = size; Vector<Point2> r_result; @@ -821,37 +823,37 @@ Dictionary _Geometry2D::make_atlas(const Vector<Size2> &p_rects) { return ret; } -void _Geometry2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("is_point_in_circle", "point", "circle_position", "circle_radius"), &_Geometry2D::is_point_in_circle); - ClassDB::bind_method(D_METHOD("segment_intersects_segment", "from_a", "to_a", "from_b", "to_b"), &_Geometry2D::segment_intersects_segment); - ClassDB::bind_method(D_METHOD("line_intersects_line", "from_a", "dir_a", "from_b", "dir_b"), &_Geometry2D::line_intersects_line); +void Geometry2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("is_point_in_circle", "point", "circle_position", "circle_radius"), &Geometry2D::is_point_in_circle); + ClassDB::bind_method(D_METHOD("segment_intersects_segment", "from_a", "to_a", "from_b", "to_b"), &Geometry2D::segment_intersects_segment); + ClassDB::bind_method(D_METHOD("line_intersects_line", "from_a", "dir_a", "from_b", "dir_b"), &Geometry2D::line_intersects_line); - ClassDB::bind_method(D_METHOD("get_closest_points_between_segments", "p1", "q1", "p2", "q2"), &_Geometry2D::get_closest_points_between_segments); + ClassDB::bind_method(D_METHOD("get_closest_points_between_segments", "p1", "q1", "p2", "q2"), &Geometry2D::get_closest_points_between_segments); - ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "point", "s1", "s2"), &_Geometry2D::get_closest_point_to_segment); + ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "point", "s1", "s2"), &Geometry2D::get_closest_point_to_segment); - ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &_Geometry2D::get_closest_point_to_segment_uncapped); + ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &Geometry2D::get_closest_point_to_segment_uncapped); - ClassDB::bind_method(D_METHOD("point_is_inside_triangle", "point", "a", "b", "c"), &_Geometry2D::point_is_inside_triangle); + ClassDB::bind_method(D_METHOD("point_is_inside_triangle", "point", "a", "b", "c"), &Geometry2D::point_is_inside_triangle); - ClassDB::bind_method(D_METHOD("is_polygon_clockwise", "polygon"), &_Geometry2D::is_polygon_clockwise); - ClassDB::bind_method(D_METHOD("is_point_in_polygon", "point", "polygon"), &_Geometry2D::is_point_in_polygon); - ClassDB::bind_method(D_METHOD("triangulate_polygon", "polygon"), &_Geometry2D::triangulate_polygon); - ClassDB::bind_method(D_METHOD("triangulate_delaunay", "points"), &_Geometry2D::triangulate_delaunay); - ClassDB::bind_method(D_METHOD("convex_hull", "points"), &_Geometry2D::convex_hull); + ClassDB::bind_method(D_METHOD("is_polygon_clockwise", "polygon"), &Geometry2D::is_polygon_clockwise); + ClassDB::bind_method(D_METHOD("is_point_in_polygon", "point", "polygon"), &Geometry2D::is_point_in_polygon); + ClassDB::bind_method(D_METHOD("triangulate_polygon", "polygon"), &Geometry2D::triangulate_polygon); + ClassDB::bind_method(D_METHOD("triangulate_delaunay", "points"), &Geometry2D::triangulate_delaunay); + ClassDB::bind_method(D_METHOD("convex_hull", "points"), &Geometry2D::convex_hull); - ClassDB::bind_method(D_METHOD("merge_polygons", "polygon_a", "polygon_b"), &_Geometry2D::merge_polygons); - ClassDB::bind_method(D_METHOD("clip_polygons", "polygon_a", "polygon_b"), &_Geometry2D::clip_polygons); - ClassDB::bind_method(D_METHOD("intersect_polygons", "polygon_a", "polygon_b"), &_Geometry2D::intersect_polygons); - ClassDB::bind_method(D_METHOD("exclude_polygons", "polygon_a", "polygon_b"), &_Geometry2D::exclude_polygons); + ClassDB::bind_method(D_METHOD("merge_polygons", "polygon_a", "polygon_b"), &Geometry2D::merge_polygons); + ClassDB::bind_method(D_METHOD("clip_polygons", "polygon_a", "polygon_b"), &Geometry2D::clip_polygons); + ClassDB::bind_method(D_METHOD("intersect_polygons", "polygon_a", "polygon_b"), &Geometry2D::intersect_polygons); + ClassDB::bind_method(D_METHOD("exclude_polygons", "polygon_a", "polygon_b"), &Geometry2D::exclude_polygons); - ClassDB::bind_method(D_METHOD("clip_polyline_with_polygon", "polyline", "polygon"), &_Geometry2D::clip_polyline_with_polygon); - ClassDB::bind_method(D_METHOD("intersect_polyline_with_polygon", "polyline", "polygon"), &_Geometry2D::intersect_polyline_with_polygon); + ClassDB::bind_method(D_METHOD("clip_polyline_with_polygon", "polyline", "polygon"), &Geometry2D::clip_polyline_with_polygon); + ClassDB::bind_method(D_METHOD("intersect_polyline_with_polygon", "polyline", "polygon"), &Geometry2D::intersect_polyline_with_polygon); - ClassDB::bind_method(D_METHOD("offset_polygon", "polygon", "delta", "join_type"), &_Geometry2D::offset_polygon, DEFVAL(JOIN_SQUARE)); - ClassDB::bind_method(D_METHOD("offset_polyline", "polyline", "delta", "join_type", "end_type"), &_Geometry2D::offset_polyline, DEFVAL(JOIN_SQUARE), DEFVAL(END_SQUARE)); + ClassDB::bind_method(D_METHOD("offset_polygon", "polygon", "delta", "join_type"), &Geometry2D::offset_polygon, DEFVAL(JOIN_SQUARE)); + ClassDB::bind_method(D_METHOD("offset_polyline", "polyline", "delta", "join_type", "end_type"), &Geometry2D::offset_polyline, DEFVAL(JOIN_SQUARE), DEFVAL(END_SQUARE)); - ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &_Geometry2D::make_atlas); + ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &Geometry2D::make_atlas); BIND_ENUM_CONSTANT(OPERATION_UNION); BIND_ENUM_CONSTANT(OPERATION_DIFFERENCE); @@ -869,29 +871,29 @@ void _Geometry2D::_bind_methods() { BIND_ENUM_CONSTANT(END_ROUND); } -////// _Geometry3D ////// +////// Geometry3D ////// -_Geometry3D *_Geometry3D::singleton = nullptr; +Geometry3D *Geometry3D::singleton = nullptr; -_Geometry3D *_Geometry3D::get_singleton() { +Geometry3D *Geometry3D::get_singleton() { return singleton; } -Vector<Plane> _Geometry3D::build_box_planes(const Vector3 &p_extents) { - return Geometry3D::build_box_planes(p_extents); +Vector<Plane> Geometry3D::build_box_planes(const Vector3 &p_extents) { + return ::Geometry3D::build_box_planes(p_extents); } -Vector<Plane> _Geometry3D::build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis) { - return Geometry3D::build_cylinder_planes(p_radius, p_height, p_sides, p_axis); +Vector<Plane> Geometry3D::build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis) { + return ::Geometry3D::build_cylinder_planes(p_radius, p_height, p_sides, p_axis); } -Vector<Plane> _Geometry3D::build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis) { - return Geometry3D::build_capsule_planes(p_radius, p_height, p_sides, p_lats, p_axis); +Vector<Plane> Geometry3D::build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis) { + return ::Geometry3D::build_capsule_planes(p_radius, p_height, p_sides, p_lats, p_axis); } -Vector<Vector3> _Geometry3D::get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2) { +Vector<Vector3> Geometry3D::get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2) { Vector3 r1, r2; - Geometry3D::get_closest_points_between_segments(p1, p2, q1, q2, r1, r2); + ::Geometry3D::get_closest_points_between_segments(p1, p2, q1, q2, r1, r2); Vector<Vector3> r; r.resize(2); r.set(0, r1); @@ -899,38 +901,38 @@ Vector<Vector3> _Geometry3D::get_closest_points_between_segments(const Vector3 & return r; } -Vector3 _Geometry3D::get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b) { +Vector3 Geometry3D::get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b) { Vector3 s[2] = { p_a, p_b }; - return Geometry3D::get_closest_point_to_segment(p_point, s); + return ::Geometry3D::get_closest_point_to_segment(p_point, s); } -Vector3 _Geometry3D::get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b) { +Vector3 Geometry3D::get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b) { Vector3 s[2] = { p_a, p_b }; - return Geometry3D::get_closest_point_to_segment_uncapped(p_point, s); + return ::Geometry3D::get_closest_point_to_segment_uncapped(p_point, s); } -Variant _Geometry3D::ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) { +Variant Geometry3D::ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) { Vector3 res; - if (Geometry3D::ray_intersects_triangle(p_from, p_dir, p_v0, p_v1, p_v2, &res)) { + if (::Geometry3D::ray_intersects_triangle(p_from, p_dir, p_v0, p_v1, p_v2, &res)) { return res; } else { return Variant(); } } -Variant _Geometry3D::segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) { +Variant Geometry3D::segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) { Vector3 res; - if (Geometry3D::segment_intersects_triangle(p_from, p_to, p_v0, p_v1, p_v2, &res)) { + if (::Geometry3D::segment_intersects_triangle(p_from, p_to, p_v0, p_v1, p_v2, &res)) { return res; } else { return Variant(); } } -Vector<Vector3> _Geometry3D::segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius) { +Vector<Vector3> Geometry3D::segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius) { Vector<Vector3> r; Vector3 res, norm; - if (!Geometry3D::segment_intersects_sphere(p_from, p_to, p_sphere_pos, p_sphere_radius, &res, &norm)) { + if (!::Geometry3D::segment_intersects_sphere(p_from, p_to, p_sphere_pos, p_sphere_radius, &res, &norm)) { return r; } @@ -940,10 +942,10 @@ Vector<Vector3> _Geometry3D::segment_intersects_sphere(const Vector3 &p_from, co return r; } -Vector<Vector3> _Geometry3D::segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius) { +Vector<Vector3> Geometry3D::segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius) { Vector<Vector3> r; Vector3 res, norm; - if (!Geometry3D::segment_intersects_cylinder(p_from, p_to, p_height, p_radius, &res, &norm)) { + if (!::Geometry3D::segment_intersects_cylinder(p_from, p_to, p_height, p_radius, &res, &norm)) { return r; } @@ -953,10 +955,10 @@ Vector<Vector3> _Geometry3D::segment_intersects_cylinder(const Vector3 &p_from, return r; } -Vector<Vector3> _Geometry3D::segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector<Plane> &p_planes) { +Vector<Vector3> Geometry3D::segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector<Plane> &p_planes) { Vector<Vector3> r; Vector3 res, norm; - if (!Geometry3D::segment_intersects_convex(p_from, p_to, p_planes.ptr(), p_planes.size(), &res, &norm)) { + if (!::Geometry3D::segment_intersects_convex(p_from, p_to, p_planes.ptr(), p_planes.size(), &res, &norm)) { return r; } @@ -966,33 +968,33 @@ Vector<Vector3> _Geometry3D::segment_intersects_convex(const Vector3 &p_from, co return r; } -Vector<Vector3> _Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane) { - return Geometry3D::clip_polygon(p_points, p_plane); +Vector<Vector3> Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane) { + return ::Geometry3D::clip_polygon(p_points, p_plane); } -void _Geometry3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &_Geometry3D::build_box_planes); - ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &_Geometry3D::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z)); - ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &_Geometry3D::build_capsule_planes, DEFVAL(Vector3::AXIS_Z)); +void Geometry3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &Geometry3D::build_box_planes); + ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &Geometry3D::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z)); + ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &Geometry3D::build_capsule_planes, DEFVAL(Vector3::AXIS_Z)); - ClassDB::bind_method(D_METHOD("get_closest_points_between_segments", "p1", "p2", "q1", "q2"), &_Geometry3D::get_closest_points_between_segments); + ClassDB::bind_method(D_METHOD("get_closest_points_between_segments", "p1", "p2", "q1", "q2"), &Geometry3D::get_closest_points_between_segments); - ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "point", "s1", "s2"), &_Geometry3D::get_closest_point_to_segment); + ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "point", "s1", "s2"), &Geometry3D::get_closest_point_to_segment); - ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &_Geometry3D::get_closest_point_to_segment_uncapped); + ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &Geometry3D::get_closest_point_to_segment_uncapped); - ClassDB::bind_method(D_METHOD("ray_intersects_triangle", "from", "dir", "a", "b", "c"), &_Geometry3D::ray_intersects_triangle); - ClassDB::bind_method(D_METHOD("segment_intersects_triangle", "from", "to", "a", "b", "c"), &_Geometry3D::segment_intersects_triangle); - ClassDB::bind_method(D_METHOD("segment_intersects_sphere", "from", "to", "sphere_position", "sphere_radius"), &_Geometry3D::segment_intersects_sphere); - ClassDB::bind_method(D_METHOD("segment_intersects_cylinder", "from", "to", "height", "radius"), &_Geometry3D::segment_intersects_cylinder); - ClassDB::bind_method(D_METHOD("segment_intersects_convex", "from", "to", "planes"), &_Geometry3D::segment_intersects_convex); + ClassDB::bind_method(D_METHOD("ray_intersects_triangle", "from", "dir", "a", "b", "c"), &Geometry3D::ray_intersects_triangle); + ClassDB::bind_method(D_METHOD("segment_intersects_triangle", "from", "to", "a", "b", "c"), &Geometry3D::segment_intersects_triangle); + ClassDB::bind_method(D_METHOD("segment_intersects_sphere", "from", "to", "sphere_position", "sphere_radius"), &Geometry3D::segment_intersects_sphere); + ClassDB::bind_method(D_METHOD("segment_intersects_cylinder", "from", "to", "height", "radius"), &Geometry3D::segment_intersects_cylinder); + ClassDB::bind_method(D_METHOD("segment_intersects_convex", "from", "to", "planes"), &Geometry3D::segment_intersects_convex); - ClassDB::bind_method(D_METHOD("clip_polygon", "points", "plane"), &_Geometry3D::clip_polygon); + ClassDB::bind_method(D_METHOD("clip_polygon", "points", "plane"), &Geometry3D::clip_polygon); } -////// _File ////// +////// File ////// -Error _File::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) { +Error File::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) { Error err = open(p_path, p_mode_flags); if (err) { return err; @@ -1009,7 +1011,7 @@ Error _File::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const return OK; } -Error _File::open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass) { +Error File::open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass) { Error err = open(p_path, p_mode_flags); if (err) { return err; @@ -1027,7 +1029,7 @@ Error _File::open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, c return OK; } -Error _File::open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode) { +Error File::open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode) { FileAccessCompressed *fac = memnew(FileAccessCompressed); fac->configure("GCPF", (Compression::Mode)p_compress_mode); @@ -1043,7 +1045,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); @@ -1053,94 +1055,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."); @@ -1162,7 +1164,7 @@ Vector<uint8_t> _File::get_buffer(int64_t p_length) const { return data; } -String _File::get_as_text() const { +String File::get_as_text() const { ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use."); String text; @@ -1181,20 +1183,20 @@ String _File::get_as_text() const { return text; } -String _File::get_md5(const String &p_path) const { +String File::get_md5(const String &p_path) const { return FileAccess::get_md5(p_path); } -String _File::get_sha256(const String &p_path) const { +String File::get_sha256(const String &p_path) const { return FileAccess::get_sha256(p_path); } -String _File::get_line() const { +String File::get_line() const { ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use."); return f->get_line(); } -Vector<String> _File::get_csv_line(const String &p_delim) const { +Vector<String> File::get_csv_line(const String &p_delim) const { ERR_FAIL_COND_V_MSG(!f, Vector<String>(), "File must be opened before use."); return f->get_csv_line(p_delim); } @@ -1204,95 +1206,95 @@ Vector<String> _File::get_csv_line(const String &p_delim) const { * These flags get reset to false (little endian) on each open */ -void _File::set_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(); @@ -1305,11 +1307,11 @@ void _File::store_buffer(const Vector<uint8_t> &p_buffer) { f->store_buffer(&r[0], len); } -bool _File::file_exists(const String &p_name) const { +bool File::file_exists(const String &p_name) const { return FileAccess::exists(p_name); } -void _File::store_var(const Variant &p_var, bool p_full_objects) { +void File::store_var(const Variant &p_var, bool p_full_objects) { ERR_FAIL_COND_MSG(!f, "File must be opened before use."); int len; Error err = encode_variant(p_var, nullptr, len, p_full_objects); @@ -1326,7 +1328,7 @@ void _File::store_var(const Variant &p_var, bool p_full_objects) { store_buffer(buff); } -Variant _File::get_var(bool p_allow_objects) const { +Variant File::get_var(bool p_allow_objects) const { ERR_FAIL_COND_V_MSG(!f, Variant(), "File must be opened before use."); uint32_t len = get_32(); Vector<uint8_t> buff = get_buffer(len); @@ -1341,62 +1343,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"); @@ -1411,15 +1413,15 @@ void _File::_bind_methods() { BIND_ENUM_CONSTANT(COMPRESSION_GZIP); } -_File::~_File() { +File::~File() { if (f) { memdelete(f); } } -////// _Directory ////// +////// Directory ////// -Error _Directory::open(const String &p_path) { +Error Directory::open(const String &p_path) { Error err; DirAccess *alt = DirAccess::open(p_path, &err); @@ -1435,11 +1437,11 @@ Error _Directory::open(const String &p_path) { return OK; } -bool _Directory::is_open() const { +bool Directory::is_open() const { return d && dir_open; } -Error _Directory::list_dir_begin(bool p_show_navigational, bool p_show_hidden) { +Error Directory::list_dir_begin(bool p_show_navigational, bool p_show_hidden) { ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); _list_skip_navigational = !p_show_navigational; @@ -1448,7 +1450,7 @@ Error _Directory::list_dir_begin(bool p_show_navigational, bool p_show_hidden) { return d->list_dir_begin(); } -String _Directory::get_next() { +String Directory::get_next() { ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use."); String next = d->get_next(); @@ -1458,32 +1460,32 @@ String _Directory::get_next() { return next; } -bool _Directory::current_is_dir() const { +bool Directory::current_is_dir() const { ERR_FAIL_COND_V_MSG(!is_open(), false, "Directory must be opened before use."); return d->current_is_dir(); } -void _Directory::list_dir_end() { +void Directory::list_dir_end() { ERR_FAIL_COND_MSG(!is_open(), "Directory must be opened before use."); d->list_dir_end(); } -int _Directory::get_drive_count() { +int Directory::get_drive_count() { ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use."); return d->get_drive_count(); } -String _Directory::get_drive(int p_drive) { +String Directory::get_drive(int p_drive) { ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use."); return d->get_drive(p_drive); } -int _Directory::get_current_drive() { +int Directory::get_current_drive() { ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use."); return d->get_current_drive(); } -Error _Directory::change_dir(String p_dir) { +Error Directory::change_dir(String p_dir) { ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly."); Error err = d->change_dir(p_dir); @@ -1495,14 +1497,14 @@ Error _Directory::change_dir(String p_dir) { return OK; } -String _Directory::get_current_dir() { +String Directory::get_current_dir() { ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use."); return d->get_current_dir(); } -Error _Directory::make_dir(String p_dir) { +Error Directory::make_dir(String p_dir) { ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly."); - if (!p_dir.is_rel_path()) { + if (!p_dir.is_relative_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); Error err = d->make_dir(p_dir); memdelete(d); @@ -1511,9 +1513,9 @@ Error _Directory::make_dir(String p_dir) { return d->make_dir(p_dir); } -Error _Directory::make_dir_recursive(String p_dir) { +Error Directory::make_dir_recursive(String p_dir) { ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly."); - if (!p_dir.is_rel_path()) { + if (!p_dir.is_relative_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); Error err = d->make_dir_recursive(p_dir); memdelete(d); @@ -1522,18 +1524,18 @@ Error _Directory::make_dir_recursive(String p_dir) { return d->make_dir_recursive(p_dir); } -bool _Directory::file_exists(String p_file) { +bool Directory::file_exists(String p_file) { ERR_FAIL_COND_V_MSG(!d, false, "Directory is not configured properly."); - if (!p_file.is_rel_path()) { + if (!p_file.is_relative_path()) { return FileAccess::exists(p_file); } return d->file_exists(p_file); } -bool _Directory::dir_exists(String p_dir) { +bool Directory::dir_exists(String p_dir) { ERR_FAIL_COND_V_MSG(!d, false, "Directory is not configured properly."); - if (!p_dir.is_rel_path()) { + if (!p_dir.is_relative_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); bool exists = d->dir_exists(p_dir); memdelete(d); @@ -1543,33 +1545,35 @@ bool _Directory::dir_exists(String p_dir) { return d->dir_exists(p_dir); } -uint64_t _Directory::get_space_left() { +uint64_t Directory::get_space_left() { ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use."); return d->get_space_left() / 1024 * 1024; // Truncate to closest MiB. } -Error _Directory::copy(String p_from, String p_to) { +Error Directory::copy(String p_from, String p_to) { ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); return d->copy(p_from, p_to); } -Error _Directory::rename(String p_from, String p_to) { +Error Directory::rename(String p_from, String p_to) { ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); - if (!p_from.is_rel_path()) { + ERR_FAIL_COND_V_MSG(p_from.is_empty() || p_from == "." || p_from == "..", ERR_INVALID_PARAMETER, "Invalid path to rename."); + + if (!p_from.is_relative_path()) { DirAccess *d = DirAccess::create_for_path(p_from); - ERR_FAIL_COND_V_MSG(!d->file_exists(p_from), ERR_DOES_NOT_EXIST, "File does not exist."); + ERR_FAIL_COND_V_MSG(!d->file_exists(p_from) && !d->dir_exists(p_from), ERR_DOES_NOT_EXIST, "File or directory does not exist."); Error err = d->rename(p_from, p_to); memdelete(d); return err; } - ERR_FAIL_COND_V_MSG(!d->file_exists(p_from), ERR_DOES_NOT_EXIST, "File does not exist."); + ERR_FAIL_COND_V_MSG(!d->file_exists(p_from) && !d->dir_exists(p_from), ERR_DOES_NOT_EXIST, "File or directory does not exist."); return d->rename(p_from, p_to); } -Error _Directory::remove(String p_name) { +Error Directory::remove(String p_name) { ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); - if (!p_name.is_rel_path()) { + if (!p_name.is_relative_path()) { DirAccess *d = DirAccess::create_for_path(p_name); Error err = d->remove(p_name); memdelete(d); @@ -1579,47 +1583,47 @@ Error _Directory::remove(String p_name) { return d->remove(p_name); } -void _Directory::_bind_methods() { - ClassDB::bind_method(D_METHOD("open", "path"), &_Directory::open); - ClassDB::bind_method(D_METHOD("list_dir_begin", "show_navigational", "show_hidden"), &_Directory::list_dir_begin, DEFVAL(false), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_next"), &_Directory::get_next); - ClassDB::bind_method(D_METHOD("current_is_dir"), &_Directory::current_is_dir); - ClassDB::bind_method(D_METHOD("list_dir_end"), &_Directory::list_dir_end); - ClassDB::bind_method(D_METHOD("get_drive_count"), &_Directory::get_drive_count); - ClassDB::bind_method(D_METHOD("get_drive", "idx"), &_Directory::get_drive); - ClassDB::bind_method(D_METHOD("get_current_drive"), &_Directory::get_current_drive); - ClassDB::bind_method(D_METHOD("change_dir", "todir"), &_Directory::change_dir); - ClassDB::bind_method(D_METHOD("get_current_dir"), &_Directory::get_current_dir); - ClassDB::bind_method(D_METHOD("make_dir", "path"), &_Directory::make_dir); - ClassDB::bind_method(D_METHOD("make_dir_recursive", "path"), &_Directory::make_dir_recursive); - ClassDB::bind_method(D_METHOD("file_exists", "path"), &_Directory::file_exists); - ClassDB::bind_method(D_METHOD("dir_exists", "path"), &_Directory::dir_exists); - //ClassDB::bind_method(D_METHOD("get_modified_time","file"),&_Directory::get_modified_time); - ClassDB::bind_method(D_METHOD("get_space_left"), &_Directory::get_space_left); - ClassDB::bind_method(D_METHOD("copy", "from", "to"), &_Directory::copy); - ClassDB::bind_method(D_METHOD("rename", "from", "to"), &_Directory::rename); - ClassDB::bind_method(D_METHOD("remove", "path"), &_Directory::remove); -} - -_Directory::_Directory() { +void Directory::_bind_methods() { + ClassDB::bind_method(D_METHOD("open", "path"), &Directory::open); + ClassDB::bind_method(D_METHOD("list_dir_begin", "show_navigational", "show_hidden"), &Directory::list_dir_begin, DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_next"), &Directory::get_next); + ClassDB::bind_method(D_METHOD("current_is_dir"), &Directory::current_is_dir); + ClassDB::bind_method(D_METHOD("list_dir_end"), &Directory::list_dir_end); + ClassDB::bind_method(D_METHOD("get_drive_count"), &Directory::get_drive_count); + ClassDB::bind_method(D_METHOD("get_drive", "idx"), &Directory::get_drive); + ClassDB::bind_method(D_METHOD("get_current_drive"), &Directory::get_current_drive); + ClassDB::bind_method(D_METHOD("change_dir", "todir"), &Directory::change_dir); + ClassDB::bind_method(D_METHOD("get_current_dir"), &Directory::get_current_dir); + ClassDB::bind_method(D_METHOD("make_dir", "path"), &Directory::make_dir); + ClassDB::bind_method(D_METHOD("make_dir_recursive", "path"), &Directory::make_dir_recursive); + ClassDB::bind_method(D_METHOD("file_exists", "path"), &Directory::file_exists); + ClassDB::bind_method(D_METHOD("dir_exists", "path"), &Directory::dir_exists); + //ClassDB::bind_method(D_METHOD("get_modified_time","file"),&Directory::get_modified_time); + ClassDB::bind_method(D_METHOD("get_space_left"), &Directory::get_space_left); + ClassDB::bind_method(D_METHOD("copy", "from", "to"), &Directory::copy); + ClassDB::bind_method(D_METHOD("rename", "from", "to"), &Directory::rename); + ClassDB::bind_method(D_METHOD("remove", "path"), &Directory::remove); +} + +Directory::Directory() { d = DirAccess::create(DirAccess::ACCESS_RESOURCES); } -_Directory::~_Directory() { +Directory::~Directory() { if (d) { memdelete(d); } } -////// _Marshalls ////// +////// Marshalls ////// -_Marshalls *_Marshalls::singleton = nullptr; +Marshalls *Marshalls::singleton = nullptr; -_Marshalls *_Marshalls::get_singleton() { +Marshalls *Marshalls::get_singleton() { return singleton; } -String _Marshalls::variant_to_base64(const Variant &p_var, bool p_full_objects) { +String Marshalls::variant_to_base64(const Variant &p_var, bool p_full_objects) { int len; Error err = encode_variant(p_var, nullptr, len, p_full_objects); ERR_FAIL_COND_V_MSG(err != OK, "", "Error when trying to encode Variant."); @@ -1637,7 +1641,7 @@ String _Marshalls::variant_to_base64(const Variant &p_var, bool p_full_objects) return ret; } -Variant _Marshalls::base64_to_variant(const String &p_str, bool p_allow_objects) { +Variant Marshalls::base64_to_variant(const String &p_str, bool p_allow_objects) { int strlen = p_str.length(); CharString cstr = p_str.ascii(); @@ -1655,13 +1659,13 @@ Variant _Marshalls::base64_to_variant(const String &p_str, bool p_allow_objects) return v; } -String _Marshalls::raw_to_base64(const Vector<uint8_t> &p_arr) { +String Marshalls::raw_to_base64(const Vector<uint8_t> &p_arr) { String ret = CryptoCore::b64_encode_str(p_arr.ptr(), p_arr.size()); ERR_FAIL_COND_V(ret == "", ret); return ret; } -Vector<uint8_t> _Marshalls::base64_to_raw(const String &p_str) { +Vector<uint8_t> Marshalls::base64_to_raw(const String &p_str) { int strlen = p_str.length(); CharString cstr = p_str.ascii(); @@ -1678,14 +1682,14 @@ Vector<uint8_t> _Marshalls::base64_to_raw(const String &p_str) { return buf; } -String _Marshalls::utf8_to_base64(const String &p_str) { +String Marshalls::utf8_to_base64(const String &p_str) { CharString cstr = p_str.utf8(); String ret = CryptoCore::b64_encode_str((unsigned char *)cstr.get_data(), cstr.length()); ERR_FAIL_COND_V(ret == "", ret); return ret; } -String _Marshalls::base64_to_utf8(const String &p_str) { +String Marshalls::base64_to_utf8(const String &p_str) { int strlen = p_str.length(); CharString cstr = p_str.ascii(); @@ -1702,62 +1706,62 @@ 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 }; @@ -1787,12 +1791,12 @@ void _Thread::_start_func(void *ud) { 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) { + if (target_param_count >= 1 && target_default_arg_count < target_param_count) { argc = 1; } } - Thread::set_name(t->target_method); + ::Thread::set_name(t->target_method); t->ret = t->target_instance->call(t->target_method, arg, argc, ce); if (ce.error != Callable::CallError::CALL_OK) { @@ -1818,7 +1822,7 @@ void _Thread::_start_func(void *ud) { } } -Error _Thread::start(Object *p_instance, const StringName &p_method, const Variant &p_userdata, Priority p_priority) { +Error Thread::start(Object *p_instance, const StringName &p_method, const Variant &p_userdata, Priority p_priority) { ERR_FAIL_COND_V_MSG(active.is_set(), ERR_ALREADY_IN_USE, "Thread already started."); ERR_FAIL_COND_V(!p_instance, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_method == StringName(), ERR_INVALID_PARAMETER); @@ -1830,24 +1834,24 @@ Error _Thread::start(Object *p_instance, const StringName &p_method, const Varia userdata = p_userdata; active.set(); - Ref<_Thread> *ud = memnew(Ref<_Thread>(this)); + Ref<Thread> *ud = memnew(Ref<Thread>(this)); - Thread::Settings s; - s.priority = (Thread::Priority)p_priority; + ::Thread::Settings s; + s.priority = (::Thread::Priority)p_priority; thread.start(_start_func, ud, s); return OK; } -String _Thread::get_id() const { +String Thread::get_id() const { return itos(thread.get_id()); } -bool _Thread::is_active() const { +bool Thread::is_active() const { return active.is_set(); } -Variant _Thread::wait_to_finish() { +Variant Thread::wait_to_finish() { ERR_FAIL_COND_V_MSG(!active.is_set(), Variant(), "Thread must be active to wait for its completion."); thread.wait_to_finish(); Variant r = ret; @@ -1859,22 +1863,24 @@ Variant _Thread::wait_to_finish() { return r; } -void _Thread::_bind_methods() { - ClassDB::bind_method(D_METHOD("start", "instance", "method", "userdata", "priority"), &_Thread::start, DEFVAL(Variant()), DEFVAL(PRIORITY_NORMAL)); - ClassDB::bind_method(D_METHOD("get_id"), &_Thread::get_id); - ClassDB::bind_method(D_METHOD("is_active"), &_Thread::is_active); - ClassDB::bind_method(D_METHOD("wait_to_finish"), &_Thread::wait_to_finish); +void Thread::_bind_methods() { + ClassDB::bind_method(D_METHOD("start", "instance", "method", "userdata", "priority"), &Thread::start, DEFVAL(Variant()), DEFVAL(PRIORITY_NORMAL)); + ClassDB::bind_method(D_METHOD("get_id"), &Thread::get_id); + ClassDB::bind_method(D_METHOD("is_active"), &Thread::is_active); + ClassDB::bind_method(D_METHOD("wait_to_finish"), &Thread::wait_to_finish); BIND_ENUM_CONSTANT(PRIORITY_LOW); BIND_ENUM_CONSTANT(PRIORITY_NORMAL); BIND_ENUM_CONSTANT(PRIORITY_HIGH); } -////// _ClassDB ////// +namespace special { -PackedStringArray _ClassDB::get_class_list() const { +////// ClassDB ////// + +PackedStringArray ClassDB::get_class_list() const { List<StringName> classes; - ClassDB::get_class_list(&classes); + ::ClassDB::get_class_list(&classes); PackedStringArray ret; ret.resize(classes.size()); @@ -1886,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()); @@ -1900,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(); } @@ -1930,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) { @@ -1955,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()); @@ -1966,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; @@ -1983,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) { @@ -2005,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()); @@ -2019,299 +2025,314 @@ 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::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_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_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_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_get_method_list", "class", "no_inheritance"), &ClassDB::get_method_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_integer_constant_list", "class", "no_inheritance"), &ClassDB::get_integer_constant_list, DEFVAL(false)); - 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_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_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_category", "class"), &ClassDB::get_category); + ::ClassDB::bind_method(D_METHOD("is_class_enabled", "class"), &ClassDB::is_class_enabled); } -////// _Engine ////// +} // namespace special + +////// Engine ////// -void _Engine::set_iterations_per_second(int p_ips) { - Engine::get_singleton()->set_iterations_per_second(p_ips); +void Engine::set_physics_ticks_per_second(int p_ips) { + ::Engine::get_singleton()->set_physics_ticks_per_second(p_ips); } -int _Engine::get_iterations_per_second() const { - return Engine::get_singleton()->get_iterations_per_second(); +int Engine::get_physics_ticks_per_second() const { + return ::Engine::get_singleton()->get_physics_ticks_per_second(); } -void _Engine::set_physics_jitter_fix(float p_threshold) { - Engine::get_singleton()->set_physics_jitter_fix(p_threshold); +void Engine::set_physics_jitter_fix(double p_threshold) { + ::Engine::get_singleton()->set_physics_jitter_fix(p_threshold); } -float _Engine::get_physics_jitter_fix() const { - return Engine::get_singleton()->get_physics_jitter_fix(); +double Engine::get_physics_jitter_fix() const { + return ::Engine::get_singleton()->get_physics_jitter_fix(); } -float _Engine::get_physics_interpolation_fraction() const { - return Engine::get_singleton()->get_physics_interpolation_fraction(); +double Engine::get_physics_interpolation_fraction() const { + return ::Engine::get_singleton()->get_physics_interpolation_fraction(); } -void _Engine::set_target_fps(int p_fps) { - Engine::get_singleton()->set_target_fps(p_fps); +void Engine::set_target_fps(int p_fps) { + ::Engine::get_singleton()->set_target_fps(p_fps); } -int _Engine::get_target_fps() const { - return Engine::get_singleton()->get_target_fps(); +int Engine::get_target_fps() const { + return ::Engine::get_singleton()->get_target_fps(); } -float _Engine::get_frames_per_second() const { - return Engine::get_singleton()->get_frames_per_second(); +double Engine::get_frames_per_second() const { + return ::Engine::get_singleton()->get_frames_per_second(); } -uint64_t _Engine::get_physics_frames() const { - return Engine::get_singleton()->get_physics_frames(); +uint64_t Engine::get_physics_frames() const { + return ::Engine::get_singleton()->get_physics_frames(); } -uint64_t _Engine::get_process_frames() const { - return Engine::get_singleton()->get_process_frames(); +uint64_t Engine::get_process_frames() const { + return ::Engine::get_singleton()->get_process_frames(); } -void _Engine::set_time_scale(float p_scale) { - Engine::get_singleton()->set_time_scale(p_scale); +void Engine::set_time_scale(double p_scale) { + ::Engine::get_singleton()->set_time_scale(p_scale); } -float _Engine::get_time_scale() { - return Engine::get_singleton()->get_time_scale(); +double Engine::get_time_scale() { + return ::Engine::get_singleton()->get_time_scale(); } -int _Engine::get_frames_drawn() { - return Engine::get_singleton()->get_frames_drawn(); +int Engine::get_frames_drawn() { + return ::Engine::get_singleton()->get_frames_drawn(); } -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(); +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_version_info() const { - return Engine::get_singleton()->get_version_info(); +Dictionary Engine::get_version_info() const { + return ::Engine::get_singleton()->get_version_info(); } -Dictionary _Engine::get_author_info() const { - return Engine::get_singleton()->get_author_info(); +Dictionary Engine::get_author_info() const { + return ::Engine::get_singleton()->get_author_info(); } -Array _Engine::get_copyright_info() const { - return Engine::get_singleton()->get_copyright_info(); +Array Engine::get_copyright_info() const { + return ::Engine::get_singleton()->get_copyright_info(); } -Dictionary _Engine::get_donor_info() const { - return Engine::get_singleton()->get_donor_info(); +Dictionary Engine::get_donor_info() const { + return ::Engine::get_singleton()->get_donor_info(); } -Dictionary _Engine::get_license_info() const { - return Engine::get_singleton()->get_license_info(); +Dictionary Engine::get_license_info() const { + return ::Engine::get_singleton()->get_license_info(); } -String _Engine::get_license_text() const { - return Engine::get_singleton()->get_license_text(); +String Engine::get_license_text() const { + return ::Engine::get_singleton()->get_license_text(); } -bool _Engine::is_in_physics_frame() const { - return Engine::get_singleton()->is_in_physics_frame(); +bool Engine::is_in_physics_frame() const { + return ::Engine::get_singleton()->is_in_physics_frame(); } -bool _Engine::has_singleton(const String &p_name) const { - return Engine::get_singleton()->has_singleton(p_name); +bool Engine::has_singleton(const StringName &p_name) const { + return ::Engine::get_singleton()->has_singleton(p_name); } -Object *_Engine::get_singleton_object(const String &p_name) const { - return Engine::get_singleton()->get_singleton_object(p_name); +Object *Engine::get_singleton_object(const StringName &p_name) const { + return ::Engine::get_singleton()->get_singleton_object(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(p_name.operator String().is_valid_identifier(), "Singleton name is not a valid identifier: " + String(p_name)); + ::Engine::Singleton s; + s.class_name = p_name; + s.name = p_name; + s.ptr = p_object; + s.user_created = true; + ::Engine::get_singleton()->add_singleton(s); + ; +} +void Engine::unregister_singleton(const StringName &p_name) { + ERR_FAIL_COND_MSG(!has_singleton(p_name), "Attempt to remove unregisteres singleton: " + String(p_name)); + ERR_FAIL_COND_MSG(!::Engine::get_singleton()->is_singleton_user_created(p_name), "Attempt to remove non-user created singleton: " + String(p_name)); + ::Engine::get_singleton()->remove_singleton(p_name); +} + +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("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("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); +Engine *Engine::singleton = nullptr; - 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); -} +////// EngineDebugger ////// -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; @@ -2324,7 +2345,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; @@ -2337,7 +2358,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; @@ -2350,7 +2371,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; @@ -2366,15 +2387,35 @@ Error _EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Arr return OK; } -_EngineDebugger::~_EngineDebugger() { +EngineDebugger::~EngineDebugger() { for (Map<StringName, Callable>::Element *E = captures.front(); E; E = E->next()) { - EngineDebugger::unregister_message_capture(E->key()); + ::EngineDebugger::unregister_message_capture(E->key()); } captures.clear(); for (Map<StringName, ProfilerCallable>::Element *E = profilers.front(); E; E = E->next()) { - EngineDebugger::unregister_profiler(E->key()); + ::EngineDebugger::unregister_profiler(E->key()); } profilers.clear(); } -_EngineDebugger *_EngineDebugger::singleton = nullptr; +EngineDebugger *EngineDebugger::singleton = nullptr; + +void EngineDebugger::_bind_methods() { + ClassDB::bind_method(D_METHOD("is_active"), &EngineDebugger::is_active); + + ClassDB::bind_method(D_METHOD("register_profiler", "name", "toggle", "add", "tick"), &EngineDebugger::register_profiler); + ClassDB::bind_method(D_METHOD("unregister_profiler", "name"), &EngineDebugger::unregister_profiler); + ClassDB::bind_method(D_METHOD("is_profiling", "name"), &EngineDebugger::is_profiling); + ClassDB::bind_method(D_METHOD("has_profiler", "name"), &EngineDebugger::has_profiler); + + ClassDB::bind_method(D_METHOD("profiler_add_frame_data", "name", "data"), &EngineDebugger::profiler_add_frame_data); + ClassDB::bind_method(D_METHOD("profiler_enable", "name", "enable", "arguments"), &EngineDebugger::profiler_enable, DEFVAL(Array())); + + ClassDB::bind_method(D_METHOD("register_message_capture", "name", "callable"), &EngineDebugger::register_message_capture); + ClassDB::bind_method(D_METHOD("unregister_message_capture", "name"), &EngineDebugger::unregister_message_capture); + ClassDB::bind_method(D_METHOD("has_capture", "name"), &EngineDebugger::has_capture); + + ClassDB::bind_method(D_METHOD("send_message", "message", "data"), &EngineDebugger::send_message); +} + +} // namespace core_bind diff --git a/core/core_bind.h b/core/core_bind.h index 1574c36d3c..a6fac63edd 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 { @@ -228,10 +226,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 +242,21 @@ public: bool request_permissions(); Vector<String> get_granted_permissions() const; - static _OS *get_singleton() { return singleton; } + static OS *get_singleton() { return singleton; } - _OS() { singleton = this; } + OS() { singleton = this; } }; -VARIANT_ENUM_CAST(_OS::VideoDriver); -VARIANT_ENUM_CAST(_OS::Weekday); -VARIANT_ENUM_CAST(_OS::Month); -VARIANT_ENUM_CAST(_OS::SystemDir); +class Geometry2D : public Object { + GDCLASS(Geometry2D, Object); -class _Geometry2D : public Object { - GDCLASS(_Geometry2D, Object); - - static _Geometry2D *singleton; + static Geometry2D *singleton; protected: static void _bind_methods(); public: - static _Geometry2D *get_singleton(); + static Geometry2D *get_singleton(); Variant segment_intersects_segment(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b); Variant line_intersects_line(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b); Vector<Vector2> get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2); @@ -315,23 +307,19 @@ public: Dictionary make_atlas(const Vector<Size2> &p_rects); - _Geometry2D() { singleton = this; } + Geometry2D() { singleton = this; } }; -VARIANT_ENUM_CAST(_Geometry2D::PolyBooleanOperation); -VARIANT_ENUM_CAST(_Geometry2D::PolyJoinType); -VARIANT_ENUM_CAST(_Geometry2D::PolyEndType); - -class _Geometry3D : public Object { - GDCLASS(_Geometry3D, Object); +class Geometry3D : public Object { + GDCLASS(Geometry3D, Object); - static _Geometry3D *singleton; + static Geometry3D *singleton; protected: static void _bind_methods(); public: - static _Geometry3D *get_singleton(); + static Geometry3D *get_singleton(); Vector<Plane> build_box_planes(const Vector3 &p_extents); Vector<Plane> build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z); Vector<Plane> build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z); @@ -347,11 +335,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 +433,12 @@ public: uint64_t get_modified_time(const String &p_file) const; - _File() {} - virtual ~_File(); + File() {} + virtual ~File(); }; -VARIANT_ENUM_CAST(_File::ModeFlags); -VARIANT_ENUM_CAST(_File::CompressionMode); - -class _Directory : public RefCounted { - GDCLASS(_Directory, RefCounted); +class Directory : public RefCounted { + GDCLASS(Directory, RefCounted); DirAccess *d; bool dir_open = false; @@ -490,24 +475,24 @@ public: Error rename(String p_from, String p_to); Error remove(String p_name); - _Directory(); - virtual ~_Directory(); + Directory(); + virtual ~Directory(); private: bool _list_skip_navigational = false; bool _list_skip_hidden = false; }; -class _Marshalls : public Object { - GDCLASS(_Marshalls, Object); +class Marshalls : public Object { + GDCLASS(Marshalls, Object); - static _Marshalls *singleton; + static Marshalls *singleton; protected: static void _bind_methods(); public: - static _Marshalls *get_singleton(); + static Marshalls *get_singleton(); String variant_to_base64(const Variant &p_var, bool p_full_objects = false); Variant base64_to_variant(const String &p_str, bool p_allow_objects = false); @@ -518,13 +503,13 @@ public: String utf8_to_base64(const String &p_str); String base64_to_utf8(const String &p_str); - _Marshalls() { singleton = this; } - ~_Marshalls() { singleton = nullptr; } + Marshalls() { singleton = this; } + ~Marshalls() { singleton = nullptr; } }; -class _Mutex : public RefCounted { - GDCLASS(_Mutex, RefCounted); - Mutex mutex; +class Mutex : public RefCounted { + GDCLASS(Mutex, RefCounted); + ::Mutex mutex; static void _bind_methods(); @@ -534,9 +519,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,8 +531,8 @@ public: void post(); }; -class _Thread : public RefCounted { - GDCLASS(_Thread, RefCounted); +class Thread : public RefCounted { + GDCLASS(Thread, RefCounted); protected: Variant ret; @@ -555,7 +540,7 @@ protected: SafeFlag active; Object *target_instance = nullptr; StringName target_method; - Thread thread; + ::Thread thread; static void _bind_methods(); static void _start_func(void *ud); @@ -573,10 +558,10 @@ public: Variant wait_to_finish(); }; -VARIANT_ENUM_CAST(_Thread::Priority); +namespace special { -class _ClassDB : public Object { - GDCLASS(_ClassDB, Object); +class ClassDB : public Object { + GDCLASS(ClassDB, Object); protected: static void _bind_methods(); @@ -609,37 +594,39 @@ public: 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 +639,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 +651,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 +679,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 +701,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..4f3f1fd16e 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -104,9 +104,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 +130,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 +542,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 +578,22 @@ 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_DEFAULT); BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT_INTL); BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NOEDITOR); @@ -561,6 +606,7 @@ 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); BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NIL", Variant::NIL); diff --git a/core/debugger/debugger_marshalls.h b/core/debugger/debugger_marshalls.h index 3e8c34d84b..98ad2b98d1 100644 --- a/core/debugger/debugger_marshalls.h +++ b/core/debugger/debugger_marshalls.h @@ -83,14 +83,14 @@ struct DebuggerMarshalls { StringName name; int sig_id = -1; int call_count = 0; - float self_time = 0; - float total_time = 0; + double self_time = 0; + double total_time = 0; }; // Servers profiler struct ServerFunctionInfo { StringName name; - float time = 0; + double time = 0; }; struct ServerInfo { @@ -100,11 +100,11 @@ struct DebuggerMarshalls { struct ServersProfilerFrame { int frame_number = 0; - float frame_time = 0; - float idle_time = 0; - float physics_time = 0; - float physics_frame_time = 0; - float script_time = 0; + double frame_time = 0; + double idle_time = 0; + double physics_time = 0; + double physics_frame_time = 0; + double script_time = 0; List<ServerInfo> servers; Vector<ScriptFunctionInfo> script_functions; diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp index e5dba029c9..a522b1310f 100644 --- a/core/debugger/engine_debugger.cpp +++ b/core/debugger/engine_debugger.cpp @@ -117,7 +117,7 @@ void EngineDebugger::line_poll() { poll_every++; } -void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, float p_physics_frame_time) { +void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, double p_physics_frame_time) { frame_time = USEC_TO_SEC(p_frame_ticks); process_time = USEC_TO_SEC(p_process_ticks); physics_time = USEC_TO_SEC(p_physics_ticks); diff --git a/core/debugger/engine_debugger.h b/core/debugger/engine_debugger.h index c6daea6e2f..22c6ef943e 100644 --- a/core/debugger/engine_debugger.h +++ b/core/debugger/engine_debugger.h @@ -44,7 +44,7 @@ class ScriptDebugger; class EngineDebugger { public: typedef void (*ProfilingToggle)(void *p_user, bool p_enable, const Array &p_opts); - typedef void (*ProfilingTick)(void *p_user, float p_frame_time, float p_process_time, float p_physics_time, float p_physics_frame_time); + typedef void (*ProfilingTick)(void *p_user, double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time); typedef void (*ProfilingAdd)(void *p_user, const Array &p_arr); typedef Error (*CaptureFunc)(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured); @@ -85,10 +85,10 @@ public: }; private: - float frame_time = 0.0; - float process_time = 0.0; - float physics_time = 0.0; - float physics_frame_time = 0.0; + double frame_time = 0.0; + double process_time = 0.0; + double physics_time = 0.0; + double physics_frame_time = 0.0; uint32_t poll_every = 0; @@ -120,7 +120,7 @@ public: static void register_uri_handler(const String &p_protocol, CreatePeerFunc p_func); - void iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, float p_physics_frame_time); + void iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, double p_physics_frame_time); void profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts = Array()); Error capture_parse(const StringName &p_name, const String &p_msg, const Array &p_args, bool &r_captured); diff --git a/core/debugger/local_debugger.cpp b/core/debugger/local_debugger.cpp index 24833711d5..b0b3f11424 100644 --- a/core/debugger/local_debugger.cpp +++ b/core/debugger/local_debugger.cpp @@ -41,7 +41,7 @@ struct LocalDebugger::ScriptsProfiler { } }; - float frame_time = 0; + double frame_time = 0; uint64_t idle_accum = 0; Vector<ScriptLanguage::ProfilingInfo> pinfo; @@ -61,7 +61,7 @@ struct LocalDebugger::ScriptsProfiler { } } - void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { + void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { frame_time = p_frame_time; _print_frame_data(false); } @@ -92,8 +92,8 @@ struct LocalDebugger::ScriptsProfiler { for (int i = 0; i < ofs; i++) { script_time_us += pinfo[i].self_time; } - float script_time = USEC_TO_SEC(script_time_us); - float total_time = p_accumulated ? script_time : frame_time; + double script_time = USEC_TO_SEC(script_time_us); + double total_time = p_accumulated ? script_time : frame_time; if (!p_accumulated) { print_line("FRAME: total: " + rtos(total_time) + " script: " + rtos(script_time) + "/" + itos(script_time * 100 / total_time) + " %"); @@ -103,8 +103,8 @@ struct LocalDebugger::ScriptsProfiler { for (int i = 0; i < ofs; i++) { print_line(itos(i) + ":" + pinfo[i].signature); - float tt = USEC_TO_SEC(pinfo[i].total_time); - float st = USEC_TO_SEC(pinfo[i].self_time); + double tt = USEC_TO_SEC(pinfo[i].total_time); + double st = USEC_TO_SEC(pinfo[i].self_time); print_line("\ttotal: " + rtos(tt) + "/" + itos(tt * 100 / total_time) + " % \tself: " + rtos(st) + "/" + itos(st * 100 / total_time) + " % tcalls: " + itos(pinfo[i].call_count)); } } @@ -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 62d5126e57..f865dfe102 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -50,7 +50,7 @@ void RemoteDebugger::_bind_profiler(const String &p_name, T *p_prof) { [](void *p_user, const Array &p_data) { ((T *)p_user)->add(p_data); }, - [](void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { + [](void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { ((T *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time); }); EngineDebugger::register_profiler(p_name, prof); @@ -164,7 +164,7 @@ public: } } - void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { + void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { uint64_t pt = OS::get_singleton()->get_ticks_msec(); if (pt - last_bandwidth_time > 200) { last_bandwidth_time = pt; @@ -278,10 +278,10 @@ struct RemoteDebugger::ServersProfiler { Map<StringName, ServerInfo> server_data; ScriptsProfiler scripts_profiler; - float frame_time = 0; - float idle_time = 0; - float physics_time = 0; - float physics_frame_time = 0; + double frame_time = 0; + double idle_time = 0; + double physics_time = 0; + double physics_frame_time = 0; void toggle(bool p_enable, const Array &p_opts) { skip_profile_frame = false; @@ -308,7 +308,7 @@ struct RemoteDebugger::ServersProfiler { srv.functions.push_back(fi); } - void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { + void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { frame_time = p_frame_time; idle_time = p_idle_time; physics_time = p_physics_time; @@ -358,7 +358,7 @@ struct RemoteDebugger::VisualProfiler { void add(const Array &p_data) {} - void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { + void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { Vector<RS::FrameProfileArea> profile_areas = RS::get_singleton()->get_frame_profile(); DebuggerMarshalls::VisualProfilerFrame frame; if (!profile_areas.size()) { @@ -378,7 +378,7 @@ struct RemoteDebugger::PerformanceProfiler { void toggle(bool p_enable, const Array &p_opts) {} void add(const Array &p_data) {} - void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { + void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { if (!performance) { return; } 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 a3011fe275..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. diff --git a/core/error/error_list.cpp b/core/error/error_list.cpp new file mode 100644 index 0000000000..e1e94dd65d --- /dev/null +++ b/core/error/error_list.cpp @@ -0,0 +1,85 @@ +/*************************************************************************/ +/* error_list.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "error_list.h" + +const char *error_names[] = { + "OK", // OK + "Failed", // FAILED + "Unavailable", // ERR_UNAVAILABLE + "Unconfigured", // ERR_UNCONFIGURED + "Unauthorized", // ERR_UNAUTHORIZED + "Parameter out of range", // ERR_PARAMETER_RANGE_ERROR + "Out of memory", // ERR_OUT_OF_MEMORY + "File not found", // ERR_FILE_NOT_FOUND + "File: Bad drive", // ERR_FILE_BAD_DRIVE + "File: Bad path", // ERR_FILE_BAD_PATH + "File: Permission denied", // ERR_FILE_NO_PERMISSION + "File already in use", // ERR_FILE_ALREADY_IN_USE + "Can't open file", // ERR_FILE_CANT_OPEN + "Can't write file", // ERR_FILE_CANT_WRITE + "Can't read file", // ERR_FILE_CANT_READ + "File unrecognized", // ERR_FILE_UNRECOGNIZED + "File corrupt", // ERR_FILE_CORRUPT + "Missing dependencies for file", // ERR_FILE_MISSING_DEPENDENCIES + "End of file", // ERR_FILE_EOF + "Can't open", // ERR_CANT_OPEN + "Can't create", // ERR_CANT_CREATE + "Query failed", // ERR_QUERY_FAILED + "Already in use", // ERR_ALREADY_IN_USE + "Locked", // ERR_LOCKED + "Timeout", // ERR_TIMEOUT + "Can't connect", // ERR_CANT_CONNECT + "Can't resolve", // ERR_CANT_RESOLVE + "Connection error", // ERR_CONNECTION_ERROR + "Can't acquire resource", // ERR_CANT_ACQUIRE_RESOURCE + "Can't fork", // ERR_CANT_FORK + "Invalid data", // ERR_INVALID_DATA + "Invalid parameter", // ERR_INVALID_PARAMETER + "Already exists", // ERR_ALREADY_EXISTS + "Does not exist", // ERR_DOES_NOT_EXIST + "Can't read database", // ERR_DATABASE_CANT_READ + "Can't write database", // ERR_DATABASE_CANT_WRITE + "Compilation failed", // ERR_COMPILATION_FAILED + "Method not found", // ERR_METHOD_NOT_FOUND + "Link failed", // ERR_LINK_FAILED + "Script failed", // ERR_SCRIPT_FAILED + "Cyclic link detected", // ERR_CYCLIC_LINK + "Invalid declaration", // ERR_INVALID_DECLARATION + "Duplicate symbol", // ERR_DUPLICATE_SYMBOL + "Parse error", // ERR_PARSE_ERROR + "Busy", // ERR_BUSY + "Skip", // ERR_SKIP + "Help", // ERR_HELP + "Bug", // ERR_BUG + "Printer on fire", // ERR_PRINTER_ON_FIRE +}; + +static_assert(sizeof(error_names) / sizeof(*error_names) == ERR_MAX); diff --git a/core/error/error_list.h b/core/error/error_list.h index f032f44c1f..852825dda5 100644 --- a/core/error/error_list.h +++ b/core/error/error_list.h @@ -36,6 +36,11 @@ * values can be more detailed in the future. * * This is a generic error list, mainly for organizing a language of returning errors. + * + * Errors: + * - Are added to the Error enum in core/error/error_list.h + * - Have a description added to error_names in core/error/error_list.cpp + * - Are bound with BIND_CORE_ENUM_CONSTANT() in core/core_constants.cpp */ enum Error { @@ -88,6 +93,9 @@ enum Error { ERR_HELP, ///< user requested help!! ERR_BUG, ///< a bug in the software certainly happened, due to a double check failing or unexpected behavior. ERR_PRINTER_ON_FIRE, /// the parallel port printer is engulfed in flames + ERR_MAX, // Not being returned, value represents the number of errors }; +extern const char *error_names[]; + #endif // ERROR_LIST_H diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index 660e215478..a8547a0090 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; @@ -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,20 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { } } + { + Array native_structures; + + { + Dictionary d; + d["name"] = "AudioFrame"; + d["format"] = "float left,float right"; + + native_structures.push_back(d); + } + + api_dump["native_structures"] = native_structures; + } + return api_dump; } diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp index 88fff342ee..de107b4156 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; @@ -507,7 +663,23 @@ static const char32_t *gdnative_string_operator_index_const(const GDNativeString /* 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 +693,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)); } @@ -626,15 +803,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; @@ -668,10 +845,12 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) { /* 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; diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h index 3e69a28d59..3a5b04429c 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 @@ -140,12 +145,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 +165,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 +179,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 +212,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 +226,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 +264,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 +347,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); @@ -380,10 +389,12 @@ typedef struct { /* 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); @@ -399,7 +410,7 @@ 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_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. */ diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp index 16bc28e0a2..a3cd7ca14c 100644 --- a/core/extension/native_extension.cpp +++ b/core/extension/native_extension.cpp @@ -29,11 +29,14 @@ /*************************************************************************/ #include "native_extension.h" +#include "core/config/project_settings.h" #include "core/io/config_file.h" #include "core/object/class_db.h" #include "core/object/method_bind.h" #include "core/os/os.h" +const char *NativeExtension::EXTENSION_LIST_CONFIG_FILE = "res://.godot/extension_list.cfg"; + class NativeExtensionMethodBind : public MethodBind { GDNativeExtensionClassMethodCall call_func; GDNativeExtensionClassMethodPtrCall ptrcall_func; @@ -68,8 +71,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 +81,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 +94,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 +113,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 +154,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,19 +165,20 @@ 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]; @@ -181,7 +188,7 @@ void NativeExtension::_register_extension_class_property(const GDNativeExtension 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; @@ -199,7 +206,7 @@ void NativeExtension::_register_extension_class_signal(const GDNativeExtensionCl 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 +227,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."); @@ -368,7 +375,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 +388,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..b661381d64 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,7 +48,7 @@ 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_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 +60,8 @@ protected: static void _bind_methods(); public: + static const char *EXTENSION_LIST_CONFIG_FILE; + Error open_library(const String &p_path, const String &p_entry_symbol); void close_library(); diff --git a/core/extension/native_extension_manager.cpp b/core/extension/native_extension_manager.cpp index 7be2593845..8b7a9df4f1 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,6 +77,11 @@ 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()) { @@ -105,6 +111,17 @@ void NativeExtensionManager::deinitialize_extensions(NativeExtension::Initializa level = int32_t(p_level) - 1; } +void NativeExtensionManager::load_extensions() { + FileAccessRef f = FileAccess::open(NativeExtension::EXTENSION_LIST_CONFIG_FILE, FileAccess::READ); + while (f && !f->eof_reached()) { + String s = f->get_line().strip_edges(); + if (s != String()) { + LoadStatus err = load_extension(s); + ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, "Error loading extension: " + s); + } + } +} + NativeExtensionManager *NativeExtensionManager::get_singleton() { return singleton; } @@ -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..9195f7d8b5 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -163,8 +163,7 @@ void Input::_bind_methods() { void Input::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { #ifdef TOOLS_ENABLED - - const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\""; + const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; String pf = p_function; if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || @@ -180,7 +179,7 @@ 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(quote_style)); } } #endif @@ -221,7 +220,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 +240,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 +263,7 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con } bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const { -#ifdef DEBUG_ENABLED - bool has_action = InputMap::get_singleton()->has_action(p_action); - ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); -#endif + ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return false; @@ -291,10 +281,7 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co } float Input::get_action_strength(const StringName &p_action, bool p_exact) const { -#ifdef DEBUG_ENABLED - bool has_action = InputMap::get_singleton()->has_action(p_action); - ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); -#endif + ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action)); const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return 0.0f; @@ -460,10 +447,6 @@ Vector3 Input::get_gyroscope() const { return gyroscope; } -void Input::parse_input_event(const Ref<InputEvent> &p_event) { - _parse_input_event_impl(p_event, false); -} - void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) { // Notes on mouse-touch emulation: // - Emulated mouse events are parsed, that is, re-routed to this method, so they make the same effects @@ -472,8 +455,6 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em // - Emulated touch events are handed right to the main loop (i.e., the SceneTree) because they don't // require additional handling by this class. - _THREAD_SAFE_METHOD_ - Ref<InputEventKey> k = p_event; if (k.is_valid() && !k->is_echo() && k->get_keycode() != 0) { if (k->is_pressed()) { @@ -838,25 +819,37 @@ void Input::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, co set_custom_mouse_cursor_func(p_cursor, p_shape, p_hotspot); } -void Input::accumulate_input_event(const Ref<InputEvent> &p_event) { +void Input::parse_input_event(const Ref<InputEvent> &p_event) { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND(p_event.is_null()); - if (!use_accumulated_input) { - parse_input_event(p_event); - return; + if (use_accumulated_input) { + if (buffered_events.is_empty() || !buffered_events.back()->get()->accumulate(p_event)) { + buffered_events.push_back(p_event); + } + } else if (use_input_buffering) { + buffered_events.push_back(p_event); + } else { + _parse_input_event_impl(p_event, false); } - if (!accumulated_events.is_empty() && accumulated_events.back()->get()->accumulate(p_event)) { - return; //event was accumulated, exit +} + +void Input::flush_buffered_events() { + _THREAD_SAFE_METHOD_ + + while (buffered_events.front()) { + _parse_input_event_impl(buffered_events.front()->get(), false); + buffered_events.pop_front(); } +} - accumulated_events.push_back(p_event); +bool Input::is_using_input_buffering() { + return use_input_buffering; } -void Input::flush_accumulated_events() { - while (accumulated_events.front()) { - parse_input_event(accumulated_events.front()->get()); - accumulated_events.pop_front(); - } +void Input::set_use_input_buffering(bool p_enable) { + use_input_buffering = p_enable; } void Input::set_use_accumulated_input(bool p_enable) { @@ -864,7 +857,7 @@ void Input::set_use_accumulated_input(bool p_enable) { } void Input::release_pressed_events() { - flush_accumulated_events(); // this is needed to release actions strengths + flush_buffered_events(); // this is needed to release actions strengths keys_pressed.clear(); joy_buttons_pressed.clear(); 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..50b2099236 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -31,6 +31,7 @@ #include "input_event.h" #include "core/input/input_map.h" +#include "core/input/shortcut.h" #include "core/os/keyboard.h" const int InputEvent::DEVICE_ID_TOUCH_MOUSE = -1; @@ -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; } @@ -997,10 +1000,11 @@ bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool * bool match = button_index == jb->button_index; if (match) { + bool pressed = jb->is_pressed(); if (p_pressed != nullptr) { - *p_pressed = jb->is_pressed(); + *p_pressed = pressed; } - float strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f; + float strength = pressed ? 1.0f : 0.0f; if (p_strength != nullptr) { *p_strength = strength; } @@ -1209,6 +1213,22 @@ String InputEventScreenDrag::to_string() { return vformat("InputEventScreenDrag: index=%d, position=(%s), relative=(%s), speed=(%s)", index, String(get_position()), String(get_relative()), String(get_speed())); } +bool InputEventScreenDrag::accumulate(const Ref<InputEvent> &p_event) { + Ref<InputEventScreenDrag> drag = p_event; + if (drag.is_null()) + return false; + + if (get_index() != drag->get_index()) { + return false; + } + + set_position(drag->get_position()); + set_speed(drag->get_speed()); + relative += drag->get_relative(); + + return true; +} + void InputEventScreenDrag::_bind_methods() { ClassDB::bind_method(D_METHOD("set_index", "index"), &InputEventScreenDrag::set_index); ClassDB::bind_method(D_METHOD("get_index"), &InputEventScreenDrag::get_index); @@ -1274,10 +1294,11 @@ bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool *p_pres bool match = action == act->action; if (match) { + bool pressed = act->pressed; if (p_pressed != nullptr) { - *p_pressed = act->pressed; + *p_pressed = pressed; } - float strength = (p_pressed != nullptr && *p_pressed) ? 1.0f : 0.0f; + float strength = pressed ? 1.0f : 0.0f; if (p_strength != nullptr) { *p_strength = strength; } @@ -1512,3 +1533,33 @@ void InputEventMIDI::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_number"), "set_controller_number", "get_controller_number"); ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_value"), "set_controller_value", "get_controller_value"); } + +/////////////////////////////////// + +void InputEventShortcut::set_shortcut(Ref<Shortcut> p_shortcut) { + shortcut = p_shortcut; + emit_changed(); +} + +Ref<Shortcut> InputEventShortcut::get_shortcut() { + return shortcut; +} + +void InputEventShortcut::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_shortcut", "shortcut"), &InputEventShortcut::set_shortcut); + ClassDB::bind_method(D_METHOD("get_shortcut"), &InputEventShortcut::get_shortcut); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "Shortcut"), "set_shortcut", "get_shortcut"); +} + +bool InputEventShortcut::is_pressed() const { + return true; +} + +String InputEventShortcut::as_text() const { + return vformat(RTR("Input Event with Shortcut=%s"), shortcut->get_as_text()); +} + +String InputEventShortcut::to_string() { + return vformat("InputEventShortcut: shortcut=%s", shortcut->get_as_text()); +} diff --git a/core/input/input_event.h b/core/input/input_event.h index 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..c7ca65f61a 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -59,7 +59,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 +93,7 @@ void InputMap::add_action(const StringName &p_action, float p_deadzone) { } void InputMap::erase_action(const StringName &p_action) { - ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); + ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action)); input_map.erase(p_action); } @@ -147,20 +147,20 @@ bool InputMap::has_action(const StringName &p_action) const { } float InputMap::action_get_deadzone(const StringName &p_action) { - ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, _suggest_actions(p_action)); + ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, suggest_actions(p_action)); return input_map[p_action].deadzone; } void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) { - ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); + ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action)); input_map[p_action].deadzone = p_deadzone; } void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event) { ERR_FAIL_COND_MSG(p_event.is_null(), "It's not a reference to a valid InputEvent object."); - ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); + ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action)); if (_find_event(input_map[p_action], p_event, true)) { return; // Already added. } @@ -169,12 +169,12 @@ void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent } bool InputMap::action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event) { - ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, _suggest_actions(p_action)); + ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, suggest_actions(p_action)); return (_find_event(input_map[p_action], p_event, true) != nullptr); } void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) { - ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); + ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action)); List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event, true); if (E) { @@ -186,7 +186,7 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEve } void InputMap::action_erase_events(const StringName &p_action) { - ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); + ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action)); input_map[p_action].inputs.clear(); } @@ -196,7 +196,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 +218,16 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const { OrderedHashMap<StringName, Action>::Element E = input_map.find(p_action); - ERR_FAIL_COND_V_MSG(!E, false, _suggest_actions(p_action)); + ERR_FAIL_COND_V_MSG(!E, false, suggest_actions(p_action)); Ref<InputEventAction> input_event_action = p_event; if (input_event_action.is_valid()) { + bool pressed = input_event_action->is_pressed(); if (p_pressed != nullptr) { - *p_pressed = input_event_action->is_pressed(); + *p_pressed = pressed; } if (p_strength != nullptr) { - *p_strength = (p_pressed != nullptr && *p_pressed) ? input_event_action->get_strength() : 0.0f; + *p_strength = pressed ? input_event_action->get_strength() : 0.0f; } return input_event_action->get_action() == p_action; } diff --git a/core/input/input_map.h b/core/input/input_map.h index 0e0567464a..a2d3952f94 100644 --- a/core/input/input_map.h +++ b/core/input/input_map.h @@ -61,7 +61,6 @@ private: Array _action_get_events(const StringName &p_action); Array _get_actions(); - String _suggest_actions(const StringName &p_action) const; protected: static void _bind_methods(); @@ -89,6 +88,8 @@ 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(); diff --git a/core/input/shortcut.cpp b/core/input/shortcut.cpp new file mode 100644 index 0000000000..d0cb08724e --- /dev/null +++ b/core/input/shortcut.cpp @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* shortcut.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "shortcut.h" +#include "core/os/keyboard.h" + +void Shortcut::set_event(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND_MSG(Object::cast_to<InputEventShortcut>(*p_event), "Cannot set a shortcut event to an instance of InputEventShortcut."); + event = p_event; + emit_changed(); +} + +Ref<InputEvent> Shortcut::get_event() const { + return event; +} + +bool Shortcut::matches_event(const Ref<InputEvent> &p_event) const { + Ref<InputEventShortcut> ies = p_event; + if (ies.is_valid()) { + if (ies->get_shortcut().ptr() == this) { + return true; + } + } + return event.is_valid() && event->is_match(p_event, true); +} + +String Shortcut::get_as_text() const { + if (event.is_valid()) { + return event->as_text(); + } else { + return "None"; + } +} + +bool Shortcut::has_valid_event() const { + return event.is_valid(); +} + +void Shortcut::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_event", "event"), &Shortcut::set_event); + ClassDB::bind_method(D_METHOD("get_event"), &Shortcut::get_event); + + ClassDB::bind_method(D_METHOD("has_valid_event"), &Shortcut::has_valid_event); + + ClassDB::bind_method(D_METHOD("matches_event", "event"), &Shortcut::matches_event); + ClassDB::bind_method(D_METHOD("get_as_text"), &Shortcut::get_as_text); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), "set_event", "get_event"); +} diff --git a/core/input/shortcut.h b/core/input/shortcut.h new file mode 100644 index 0000000000..249dd1971f --- /dev/null +++ b/core/input/shortcut.h @@ -0,0 +1,54 @@ +/*************************************************************************/ +/* shortcut.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SHORTCUT_H +#define SHORTCUT_H + +#include "core/input/input_event.h" +#include "core/io/resource.h" + +class Shortcut : public Resource { + GDCLASS(Shortcut, Resource); + + Ref<InputEvent> event; + +protected: + static void _bind_methods(); + +public: + void set_event(const Ref<InputEvent> &p_shortcut); + Ref<InputEvent> get_event() const; + bool matches_event(const Ref<InputEvent> &p_event) const; + bool has_valid_event() const; + + String get_as_text() const; +}; + +#endif // SHORTCUT_H diff --git a/core/io/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/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/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 1c3f231170..c145225751 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -32,14 +32,11 @@ #include "core/debugger/engine_debugger.h" #include "core/io/marshalls.h" +#include "core/io/multiplayer_replicator.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 @@ -99,14 +96,11 @@ _FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, i case MultiplayerAPI::RPC_MODE_DISABLED: { return false; } break; - case MultiplayerAPI::RPC_MODE_REMOTE: { + case MultiplayerAPI::RPC_MODE_ANY: { 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(); + case MultiplayerAPI::RPC_MODE_AUTHORITY: { + return !p_node->is_network_authority() && p_remote_id == p_node->get_network_authority(); } break; } @@ -143,9 +137,13 @@ void MultiplayerAPI::poll() { break; // It's also possible that a packet or RPC caused a disconnection, so also check here. } } + if (network_peer.is_valid() && network_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(); @@ -322,6 +320,15 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ 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; } } @@ -330,7 +337,6 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uin 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."); @@ -345,25 +351,11 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uin if (!node) { ERR_PRINT("Failed to get path from RPC: " + String(np) + "."); } + return node; } 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 get_cached_node(p_from, p_node_target); } - 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) { @@ -374,7 +366,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t 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()) + "."); + 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_network_authority()) + "."); int argc = 0; bool byte_only = false; @@ -417,7 +409,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, 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); + 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]; @@ -581,7 +573,7 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC #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) { +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); @@ -656,7 +648,7 @@ Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uin return OK; } -Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) { +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; @@ -840,10 +832,10 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const ofs += 1; for (int i = 0; i < p_argcount; i++) { int len(0); - Error err = _encode_and_compress_variant(*p_arg[i], nullptr, len); + 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); + encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len); ofs += len; } } @@ -909,6 +901,9 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const void MultiplayerAPI::_add_peer(int p_id) { connected_peers.insert(p_id); path_get_cache.insert(p_id, PathGetCache()); + if (is_network_server()) { + replicator->spawn_all(p_id); + } emit_signal(SNAME("network_peer_connected"), p_id); } @@ -1029,6 +1024,36 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac emit_signal(SNAME("network_peer_packet"), p_from, out); } +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_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(); @@ -1067,6 +1092,14 @@ bool MultiplayerAPI::is_object_decoding_allowed() const { return allow_object_decoding; } +MultiplayerReplicator *MultiplayerAPI::get_replicator() const { + return replicator; +} + +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::_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); @@ -1085,12 +1118,14 @@ void MultiplayerAPI::_bind_methods() { 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); + 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_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_PROPERTY(PropertyInfo(Variant::OBJECT, "replicator", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerReplicator", PROPERTY_USAGE_NONE), "", "get_replicator"); ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id"))); @@ -1100,15 +1135,16 @@ void MultiplayerAPI::_bind_methods() { ADD_SIGNAL(MethodInfo("server_disconnected")); BIND_ENUM_CONSTANT(RPC_MODE_DISABLED); - BIND_ENUM_CONSTANT(RPC_MODE_REMOTE); - BIND_ENUM_CONSTANT(RPC_MODE_MASTER); - BIND_ENUM_CONSTANT(RPC_MODE_PUPPET); + BIND_ENUM_CONSTANT(RPC_MODE_ANY); + BIND_ENUM_CONSTANT(RPC_MODE_AUTHORITY); } MultiplayerAPI::MultiplayerAPI() { + replicator = memnew(MultiplayerReplicator(this)); clear(); } MultiplayerAPI::~MultiplayerAPI() { clear(); + memdelete(replicator); } diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h index 011bc3dde9..3c96a3eed1 100644 --- a/core/io/multiplayer_api.h +++ b/core/io/multiplayer_api.h @@ -32,17 +32,19 @@ #define MULTIPLAYER_API_H #include "core/io/multiplayer_peer.h" +#include "core/io/resource_uid.h" #include "core/object/ref_counted.h" +class MultiplayerReplicator; + 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 + RPC_MODE_ANY, // Any peer can call this rpc() + RPC_MODE_AUTHORITY, // Only the node's network authority (server by default) can call this rpc() }; struct RPCConfig { @@ -64,6 +66,33 @@ public: } }; + enum NetworkCommands { + NETWORK_COMMAND_REMOTE_CALL = 0, + NETWORK_COMMAND_SIMPLIFY_PATH, + NETWORK_COMMAND_CONFIRM_PATH, + NETWORK_COMMAND_RAW, + NETWORK_COMMAND_SPAWN, + NETWORK_COMMAND_DESPAWN, + NETWORK_COMMAND_SYNC, // This is the max we can have. We should optmize simplify/confirm, possibly spawn/despawn. + }; + + enum NetworkNodeIdCompression { + NETWORK_NODE_ID_COMPRESSION_8 = 0, + NETWORK_NODE_ID_COMPRESSION_16, + NETWORK_NODE_ID_COMPRESSION_32, + }; + + enum NetworkNameIdCompression { + NETWORK_NAME_ID_COMPRESSION_8 = 0, + NETWORK_NAME_ID_COMPRESSION_16, + }; + + enum { + NODE_ID_COMPRESSION_SHIFT = 3, + NAME_ID_COMPRESSION_SHIFT = 5, + BYTE_ONLY_OR_NO_ARGS_SHIFT = 6, + }; + private: //path sent caches struct PathSentCache { @@ -90,6 +119,7 @@ private: Vector<uint8_t> packet_cache; Node *root_node = nullptr; bool allow_object_decoding = false; + MultiplayerReplicator *replicator = nullptr; protected: static void _bind_methods(); @@ -104,28 +134,7 @@ protected: 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); @@ -134,8 +143,16 @@ public: 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, int p_channel = 0); + Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len); + Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len); + // Called by Node.rpc void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount); + // Called by Node._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); void _add_peer(int p_id); void _del_peer(int p_id); @@ -154,6 +171,8 @@ public: void set_allow_object_decoding(bool p_enable); bool is_object_decoding_allowed() const; + MultiplayerReplicator *get_replicator() const; + MultiplayerAPI(); ~MultiplayerAPI(); }; diff --git a/core/io/multiplayer_replicator.cpp b/core/io/multiplayer_replicator.cpp new file mode 100644 index 0000000000..1642aab136 --- /dev/null +++ b/core/io/multiplayer_replicator.cpp @@ -0,0 +1,786 @@ +/*************************************************************************/ +/* 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/io/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 ? 1 : 0) << MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT); + 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> network_peer = multiplayer->get_network_peer(); + network_peer->set_target_peer(p_peer); + network_peer->set_transfer_channel(0); + network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_UNRELIABLE); + return network_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_network_server(), "The defualt implementation only allows sync packets from the server"); + const bool same_size = ((p_packet[0] & 64) >> MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT) == 1; + 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; + } 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 ? 1 : 0) << MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT); + 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> network_peer = multiplayer->get_network_peer(); + network_peer->set_target_peer(p_peer_id); + network_peer->set_transfer_channel(0); + network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); + return network_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] & 64) >> MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_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] & 64) >> MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_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 - SPAWN_CMD_OFFSET); + if (pba.size()) { + memcpy(pba.ptrw(), p_packet, p_packet_len - SPAWN_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(StringName(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) << MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_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> network_peer = multiplayer->get_network_peer(); + network_peer->set_target_peer(p_peer_id); + network_peer->set_transfer_channel(0); + network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); + return network_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_network_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_network_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_network_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_network_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_network_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_network_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_network_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_network_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, MultiplayerPeer::TransferMode p_transfer_mode, int p_channel) { + ERR_FAIL_COND_V(!multiplayer->has_network_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]); + Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer(); + network_peer->set_target_peer(p_peer_id); + network_peer->set_transfer_channel(p_channel); + network_peer->set_transfer_mode(p_transfer_mode); + return network_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(MultiplayerPeer::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/io/multiplayer_replicator.h b/core/io/multiplayer_replicator.h new file mode 100644 index 0000000000..2630ad7a8a --- /dev/null +++ b/core/io/multiplayer_replicator.h @@ -0,0 +1,129 @@ +/*************************************************************************/ +/* 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/io/multiplayer_api.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: + 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, MultiplayerPeer::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/io/resource.cpp b/core/io/resource.cpp index 0262655927..87b4d7195d 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() : 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 00d4d093da..84fd6496a7 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)); } diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index d02d827443..3026236f07 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 (Map<String, String>::Element *E = p_map.front(); E; E = E->next()) { + deps_dict[E->key()] = E->value(); + } - int64_t res = get_script_instance()->call("_rename_dependencies", deps_dict); - return (Error)res; + int64_t err; + if (GDVIRTUAL_CALL(_rename_dependencies, p_path, deps_dict, err)) { + return (Error)err; } return OK; } void ResourceFormatLoader::_bind_methods() { - { - MethodInfo info = MethodInfo(Variant::NIL, "_load", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "original_path"), PropertyInfo(Variant::BOOL, "use_sub_threads"), PropertyInfo(Variant::INT, "cache_mode")); - info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - 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/math/a_star.cpp b/core/math/a_star.cpp index 322eb7ac61..b380860522 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -382,8 +382,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { } real_t AStar::_estimate_cost(int p_from_id, int p_to_id) { - if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost)) { - return get_script_instance()->call(SceneStringNames::get_singleton()->_estimate_cost, p_from_id, p_to_id); + real_t scost; + if (GDVIRTUAL_CALL(_estimate_cost, p_from_id, p_to_id, scost)) { + return scost; } Point *from_point; @@ -398,8 +399,9 @@ real_t AStar::_estimate_cost(int p_from_id, int p_to_id) { } real_t AStar::_compute_cost(int p_from_id, int p_to_id) { - if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost)) { - return get_script_instance()->call(SceneStringNames::get_singleton()->_compute_cost, p_from_id, p_to_id); + real_t scost; + if (GDVIRTUAL_CALL(_compute_cost, p_from_id, p_to_id, scost)) { + return scost; } Point *from_point; @@ -557,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() { @@ -654,8 +656,9 @@ Vector2 AStar2D::get_closest_position_in_segment(const Vector2 &p_point) const { } real_t AStar2D::_estimate_cost(int p_from_id, int p_to_id) { - if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost)) { - return get_script_instance()->call(SceneStringNames::get_singleton()->_estimate_cost, p_from_id, p_to_id); + real_t scost; + if (GDVIRTUAL_CALL(_estimate_cost, p_from_id, p_to_id, scost)) { + return scost; } AStar::Point *from_point; @@ -670,8 +673,9 @@ real_t AStar2D::_estimate_cost(int p_from_id, int p_to_id) { } real_t AStar2D::_compute_cost(int p_from_id, int p_to_id) { - if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost)) { - return get_script_instance()->call(SceneStringNames::get_singleton()->_compute_cost, p_from_id, p_to_id); + real_t scost; + if (GDVIRTUAL_CALL(_compute_cost, p_from_id, p_to_id, scost)) { + return scost; } AStar::Point *from_point; @@ -875,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/basis.cpp b/core/math/basis.cpp index aa3831d4cf..5c42213e61 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -381,6 +381,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(). @@ -1129,3 +1141,21 @@ void Basis::rotate_sh(real_t *p_values) { p_values[7] = -d3; p_values[8] = d4 * s_scale_dst4; } + +Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up) { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(p_target.is_equal_approx(Vector3()), Basis(), "The target vector can't be zero."); + ERR_FAIL_COND_V_MSG(p_up.is_equal_approx(Vector3()), Basis(), "The up vector can't be zero."); +#endif + Vector3 v_z = -p_target.normalized(); + Vector3 v_x = p_up.cross(v_z); +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(v_x.is_equal_approx(Vector3()), Basis(), "The target vector and up vector can't be parallel to each other."); +#endif + v_x.normalize(); + Vector3 v_y = v_z.cross(v_x); + + Basis basis; + basis.set(v_x, v_y, v_z); + return basis; +} diff --git a/core/math/basis.h b/core/math/basis.h index 2889a4aa5e..9d8ed16e29 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -88,6 +88,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; @@ -242,6 +244,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); } 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..21cb0efe20 100644 --- a/core/math/convex_hull.cpp +++ b/core/math/convex_hull.cpp @@ -2278,9 +2278,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..a860d60b02 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/p/bullet/issues/detail?id=275 /// Ole Kniemeyer, MAXON Computer GmbH class ConvexHullComputer { public: diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h index 0b6286cd9d..d63132b4da 100644 --- a/core/math/dynamic_bvh.h +++ b/core/math/dynamic_bvh.h @@ -41,7 +41,7 @@ /* Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org +Copyright (c) 2003-2013 Erwin Coumans https://bulletphysics.org This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. 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/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/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..54abc1b7f2 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -102,7 +102,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 +145,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..330b4741b1 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; @@ -152,7 +152,7 @@ struct Vector2 { 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 +168,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 +250,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 +259,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 +357,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..401c3ccd9c 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; diff --git a/core/math/vector3.h b/core/math/vector3.h index d8d3cd3cc0..6a4c42f41b 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -56,18 +56,18 @@ struct Vector3 { real_t coord[3] = { 0 }; }; - _FORCE_INLINE_ const real_t &operator[](int p_axis) const { + _FORCE_INLINE_ const real_t &operator[](const int p_axis) const { return coord[p_axis]; } - _FORCE_INLINE_ real_t &operator[](int p_axis) { + _FORCE_INLINE_ real_t &operator[](const int p_axis) { return coord[p_axis]; } - void set_axis(int p_axis, real_t p_value); - real_t get_axis(int p_axis) const; + void set_axis(const int p_axis, const real_t p_value); + real_t get_axis(const int p_axis) const; - _FORCE_INLINE_ void set_all(real_t p_value) { + _FORCE_INLINE_ void set_all(const real_t p_value) { x = y = z = p_value; } @@ -90,17 +90,17 @@ 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; @@ -143,10 +143,10 @@ struct Vector3 { _FORCE_INLINE_ Vector3 &operator/=(const Vector3 &p_v); _FORCE_INLINE_ Vector3 operator/(const Vector3 &p_v) const; - _FORCE_INLINE_ Vector3 &operator*=(real_t p_scalar); - _FORCE_INLINE_ Vector3 operator*(real_t p_scalar) const; - _FORCE_INLINE_ Vector3 &operator/=(real_t p_scalar); - _FORCE_INLINE_ Vector3 operator/(real_t p_scalar) const; + _FORCE_INLINE_ Vector3 &operator*=(const real_t p_scalar); + _FORCE_INLINE_ Vector3 operator*(const real_t p_scalar) const; + _FORCE_INLINE_ Vector3 &operator/=(const real_t p_scalar); + _FORCE_INLINE_ Vector3 operator/(const real_t p_scalar) const; _FORCE_INLINE_ Vector3 operator-() const; @@ -168,7 +168,7 @@ struct Vector3 { y = p_ivec.y; z = p_ivec.z; } - _FORCE_INLINE_ Vector3(real_t p_x, real_t p_y, real_t p_z) { + _FORCE_INLINE_ Vector3(const real_t p_x, const real_t p_y, const real_t p_z) { x = p_x; y = p_y; z = p_z; @@ -208,14 +208,14 @@ Vector3 Vector3::round() const { return Vector3(Math::round(x), Math::round(y), Math::round(z)); } -Vector3 Vector3::lerp(const Vector3 &p_to, real_t p_weight) const { +Vector3 Vector3::lerp(const Vector3 &p_to, const real_t p_weight) const { return Vector3( x + (p_weight * (p_to.x - x)), y + (p_weight * (p_to.y - y)), z + (p_weight * (p_to.z - z))); } -Vector3 Vector3::slerp(const Vector3 &p_to, real_t p_weight) const { +Vector3 Vector3::slerp(const Vector3 &p_to, const real_t p_weight) const { real_t theta = angle_to(p_to); return rotated(cross(p_to).normalized(), theta * p_weight); } @@ -303,29 +303,41 @@ Vector3 Vector3::operator/(const Vector3 &p_v) const { return Vector3(x / p_v.x, y / p_v.y, z / p_v.z); } -Vector3 &Vector3::operator*=(real_t p_scalar) { +Vector3 &Vector3::operator*=(const real_t p_scalar) { x *= p_scalar; y *= p_scalar; z *= p_scalar; return *this; } -_FORCE_INLINE_ Vector3 operator*(real_t p_scalar, const Vector3 &p_vec) { +_FORCE_INLINE_ Vector3 operator*(const float p_scalar, const Vector3 &p_vec) { return p_vec * p_scalar; } -Vector3 Vector3::operator*(real_t p_scalar) const { +_FORCE_INLINE_ Vector3 operator*(const double p_scalar, const Vector3 &p_vec) { + return p_vec * p_scalar; +} + +_FORCE_INLINE_ Vector3 operator*(const int32_t p_scalar, const Vector3 &p_vec) { + return p_vec * p_scalar; +} + +_FORCE_INLINE_ Vector3 operator*(const int64_t p_scalar, const Vector3 &p_vec) { + return p_vec * p_scalar; +} + +Vector3 Vector3::operator*(const real_t p_scalar) const { return Vector3(x * p_scalar, y * p_scalar, z * p_scalar); } -Vector3 &Vector3::operator/=(real_t p_scalar) { +Vector3 &Vector3::operator/=(const real_t p_scalar) { x /= p_scalar; y /= p_scalar; z /= p_scalar; return *this; } -Vector3 Vector3::operator/(real_t p_scalar) const { +Vector3 Vector3::operator/(const real_t p_scalar) const { return Vector3(x / p_scalar, y / p_scalar, z / p_scalar); } diff --git a/core/math/vector3i.cpp b/core/math/vector3i.cpp index 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/object/class_db.cpp b/core/object/class_db.cpp index c6ba39be94..e268a8d292 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; @@ -891,6 +892,32 @@ void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_ } } +void ClassDB::set_method_error_return_values(const StringName &p_class, const StringName &p_method, const Vector<Error> &p_values) { + OBJTYPE_RLOCK; +#ifdef DEBUG_METHODS_ENABLED + ClassInfo *type = classes.getptr(p_class); + + ERR_FAIL_COND(!type); + + type->method_error_values[p_method] = p_values; +#endif +} + +Vector<Error> ClassDB::get_method_error_return_values(const StringName &p_class, const StringName &p_method) { +#ifdef DEBUG_METHODS_ENABLED + ClassInfo *type = classes.getptr(p_class); + + ERR_FAIL_COND_V(!type, Vector<Error>()); + + if (!type->method_error_values.has(p_method)) { + return Vector<Error>(); + } + return type->method_error_values[p_method]; +#else + return Vector<Error>(); +#endif +} + bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) { OBJTYPE_RLOCK; @@ -1067,6 +1094,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 +1447,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 +1457,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 +1646,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 +1658,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..166aa35469 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,7 @@ 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 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 +331,7 @@ public: if (type->method_map.has(p_name)) { memdelete(bind); - // overloading not supported + // Overloading not supported ERR_FAIL_V_MSG(nullptr, "Method already bound: " + instance_type + "::" + p_name + "."); } type->method_map[p_name] = bind; @@ -354,6 +355,7 @@ public: static void add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_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 +373,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 +386,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 +413,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/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..3335942fb3 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -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..102776a589 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 }; @@ -137,21 +138,26 @@ enum PropertyUsageFlags { 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) 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 +278,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 +312,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 +385,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 +428,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 +822,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/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 76a6da51e1..63390919f4 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -277,18 +277,13 @@ 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 "."; } diff --git a/core/os/os.h b/core/os/os.h index 0466d94acd..55b21266fc 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -250,7 +250,6 @@ public: virtual String get_bundle_resource_dir() const; virtual String get_user_data_dir() const; - virtual String get_external_data_dir() const; virtual String get_resource_dir() const; enum SystemDir { @@ -264,7 +263,7 @@ 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; } diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 3d1cb4a8e1..6c7d9cbd89 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -41,6 +41,7 @@ #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" @@ -49,6 +50,7 @@ #include "core/io/marshalls.h" #include "core/io/multiplayer_api.h" #include "core/io/multiplayer_peer.h" +#include "core/io/multiplayer_replicator.h" #include "core/io/packed_data_container.h" #include "core/io/packet_peer.h" #include "core/io/packet_peer_dtls.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); @@ -192,6 +197,7 @@ void register_core_types() { ResourceLoader::add_resource_format_loader(resource_format_loader_crypto); GDREGISTER_VIRTUAL_CLASS(MultiplayerPeer); + GDREGISTER_VIRTUAL_CLASS(MultiplayerReplicator); GDREGISTER_CLASS(MultiplayerAPI); GDREGISTER_CLASS(MainLoop); GDREGISTER_CLASS(Translation); @@ -202,11 +208,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 +240,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 +271,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 +307,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 +353,9 @@ void unregister_core_types() { memdelete(ip); } + ResourceLoader::remove_resource_format_loader(resource_loader_native_extension); + resource_loader_native_extension.unref(); + ResourceLoader::finalize(); ClassDB::cleanup_defaults(); diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 19d23fd375..cb7d924556 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -929,6 +929,66 @@ void Translation::_bind_methods() { /////////////////////////////////////////////// +struct _character_accent_pair { + const char32_t character; + const char32_t *accented_character; +}; + +static _character_accent_pair _character_to_accented[] = { + { 'A', U"Å" }, + { 'B', U"ß" }, + { 'C', U"Ç" }, + { 'D', U"Ð" }, + { 'E', U"É" }, + { 'F', U"F́" }, + { 'G', U"Ĝ" }, + { 'H', U"Ĥ" }, + { 'I', U"Ĩ" }, + { 'J', U"Ĵ" }, + { 'K', U"ĸ" }, + { 'L', U"Ł" }, + { 'M', U"Ḿ" }, + { 'N', U"й" }, + { 'O', U"Ö" }, + { 'P', U"Ṕ" }, + { 'Q', U"Q́" }, + { 'R', U"Ř" }, + { 'S', U"Ŝ" }, + { 'T', U"Ŧ" }, + { 'U', U"Ũ" }, + { 'V', U"Ṽ" }, + { 'W', U"Ŵ" }, + { 'X', U"X́" }, + { 'Y', U"Ÿ" }, + { 'Z', U"Ž" }, + { 'a', U"á" }, + { 'b', U"ḅ" }, + { 'c', U"ć" }, + { 'd', U"d́" }, + { 'e', U"é" }, + { 'f', U"f́" }, + { 'g', U"ǵ" }, + { 'h', U"h̀" }, + { 'i', U"í" }, + { 'j', U"ǰ" }, + { 'k', U"ḱ" }, + { 'l', U"ł" }, + { 'm', U"m̀" }, + { 'n', U"ή" }, + { 'o', U"ô" }, + { 'p', U"ṕ" }, + { 'q', U"q́" }, + { 'r', U"ŕ" }, + { 's', U"š" }, + { 't', U"ŧ" }, + { 'u', U"ü" }, + { 'v', U"ṽ" }, + { 'w', U"ŵ" }, + { 'x', U"x́" }, + { 'y', U"ý" }, + { 'z', U"ź" }, +}; + bool TranslationServer::is_locale_valid(const String &p_locale) { const char **ptr = locale_list; @@ -1101,10 +1161,10 @@ StringName TranslationServer::translate(const StringName &p_message, const Strin } if (!res) { - return p_message; + return pseudolocalization_enabled ? pseudolocalize(p_message) : p_message; } - return res; + return pseudolocalization_enabled ? pseudolocalize(res) : res; } StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { @@ -1217,7 +1277,18 @@ void TranslationServer::setup() { } else { set_locale(OS::get_singleton()->get_locale()); } + fallback = GLOBAL_DEF("internationalization/locale/fallback", "en"); + pseudolocalization_enabled = GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false); + pseudolocalization_accents_enabled = GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true); + pseudolocalization_double_vowels_enabled = GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false); + pseudolocalization_fake_bidi_enabled = GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false); + pseudolocalization_override_enabled = GLOBAL_DEF("internationalization/pseudolocalization/override", false); + expansion_ratio = GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0); + pseudolocalization_prefix = GLOBAL_DEF("internationalization/pseudolocalization/prefix", "["); + pseudolocalization_suffix = GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]"); + pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true); + #ifdef TOOLS_ENABLED { String options = ""; @@ -1258,10 +1329,10 @@ StringName TranslationServer::tool_translate(const StringName &p_message, const if (tool_translation.is_valid()) { StringName r = tool_translation->get_message(p_message, p_context); if (r) { - return r; + return editor_pseudolocalization ? tool_pseudolocalize(r) : r; } } - return p_message; + return editor_pseudolocalization ? tool_pseudolocalize(p_message) : p_message; } StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { @@ -1306,6 +1377,181 @@ StringName TranslationServer::doc_translate_plural(const StringName &p_message, return p_message_plural; } +bool TranslationServer::is_pseudolocalization_enabled() const { + return pseudolocalization_enabled; +} + +void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) { + pseudolocalization_enabled = p_enabled; + + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); + } + ResourceLoader::reload_translation_remaps(); +} + +void TranslationServer::set_editor_pseudolocalization(bool p_enabled) { + editor_pseudolocalization = p_enabled; +} + +void TranslationServer::reload_pseudolocalization() { + pseudolocalization_accents_enabled = GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents"); + pseudolocalization_double_vowels_enabled = GLOBAL_GET("internationalization/pseudolocalization/double_vowels"); + pseudolocalization_fake_bidi_enabled = GLOBAL_GET("internationalization/pseudolocalization/fake_bidi"); + pseudolocalization_override_enabled = GLOBAL_GET("internationalization/pseudolocalization/override"); + expansion_ratio = GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio"); + pseudolocalization_prefix = GLOBAL_GET("internationalization/pseudolocalization/prefix"); + pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix"); + pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders"); + + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); + } + ResourceLoader::reload_translation_remaps(); +} + +StringName TranslationServer::pseudolocalize(const StringName &p_message) const { + String message = p_message; + int length = message.length(); + if (pseudolocalization_override_enabled) { + message = get_override_string(message); + } + + if (pseudolocalization_double_vowels_enabled) { + message = double_vowels(message); + } + + if (pseudolocalization_accents_enabled) { + message = replace_with_accented_string(message); + } + + if (pseudolocalization_fake_bidi_enabled) { + message = wrap_with_fakebidi_characters(message); + } + + StringName res = add_padding(message, length); + return res; +} + +StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) const { + String message = p_message; + message = double_vowels(message); + message = replace_with_accented_string(message); + StringName res = "[!!! " + message + " !!!]"; + return res; +} + +String TranslationServer::get_override_string(String &p_message) const { + String res; + for (int i = 0; i < p_message.size(); i++) { + if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + res += '*'; + } + return res; +} + +String TranslationServer::double_vowels(String &p_message) const { + String res; + for (int i = 0; i < p_message.size(); i++) { + if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + res += p_message[i]; + if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' || + p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') { + res += p_message[i]; + } + } + return res; +}; + +String TranslationServer::replace_with_accented_string(String &p_message) const { + String res; + for (int i = 0; i < p_message.size(); i++) { + if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + const char32_t *accented = get_accented_version(p_message[i]); + if (accented) { + res += accented; + } else { + res += p_message[i]; + } + } + return res; +} + +String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const { + String res; + char32_t fakebidiprefix = U'\u202e'; + char32_t fakebidisuffix = U'\u202c'; + res += fakebidiprefix; + // The fake bidi unicode gets popped at every newline so pushing it back at every newline. + for (int i = 0; i < p_message.size(); i++) { + if (p_message[i] == '\n') { + res += fakebidisuffix; + res += p_message[i]; + res += fakebidiprefix; + } else if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += fakebidisuffix; + res += p_message[i]; + res += p_message[i + 1]; + res += fakebidiprefix; + i++; + } else { + res += p_message[i]; + } + } + res += fakebidisuffix; + return res; +} + +String TranslationServer::add_padding(String &p_message, int p_length) const { + String res; + String prefix = pseudolocalization_prefix; + String suffix; + for (int i = 0; i < p_length * expansion_ratio / 2; i++) { + prefix += "_"; + suffix += "_"; + } + suffix += pseudolocalization_suffix; + res += prefix; + res += p_message; + res += suffix; + return res; +} + +const char32_t *TranslationServer::get_accented_version(char32_t p_character) const { + if (!((p_character >= 'a' && p_character <= 'z') || (p_character >= 'A' && p_character <= 'Z'))) { + return nullptr; + } + + for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) { + if (_character_to_accented[i].character == p_character) { + return _character_to_accented[i].accented_character; + } + } + + return nullptr; +} + +bool TranslationServer::is_placeholder(String &p_message, int p_index) const { + return p_message[p_index] == '%' && p_index < p_message.size() - 1 && + (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' || + p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f'); +} + void TranslationServer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale); ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale); @@ -1322,6 +1568,12 @@ void TranslationServer::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear); ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales); + + ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationServer::is_pseudolocalization_enabled); + ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationServer::set_pseudolocalization_enabled); + ClassDB::bind_method(D_METHOD("reload_pseudolocalization"), &TranslationServer::reload_pseudolocalization); + ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationServer::pseudolocalize); + ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled"); } void TranslationServer::load_translations() { diff --git a/core/string/translation.h b/core/string/translation.h index 72a828227e..4f179ac0fe 100644 --- a/core/string/translation.h +++ b/core/string/translation.h @@ -77,6 +77,26 @@ class TranslationServer : public Object { bool enabled = true; + bool pseudolocalization_enabled = false; + bool pseudolocalization_accents_enabled = false; + bool pseudolocalization_double_vowels_enabled = false; + bool pseudolocalization_fake_bidi_enabled = false; + bool pseudolocalization_override_enabled = false; + bool pseudolocalization_skip_placeholders_enabled = false; + bool editor_pseudolocalization = false; + float expansion_ratio = 0.0; + String pseudolocalization_prefix; + String pseudolocalization_suffix; + + StringName tool_pseudolocalize(const StringName &p_message) const; + String get_override_string(String &p_message) const; + String double_vowels(String &p_message) const; + String replace_with_accented_string(String &p_message) const; + String wrap_with_fakebidi_characters(String &p_message) const; + String add_padding(String &p_message, int p_length) const; + const char32_t *get_accented_version(char32_t p_character) const; + bool is_placeholder(String &p_message, int p_index) const; + static TranslationServer *singleton; bool _load_translations(const String &p_from); @@ -104,6 +124,13 @@ public: StringName translate(const StringName &p_message, const StringName &p_context = "") const; StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; + StringName pseudolocalize(const StringName &p_message) const; + + bool is_pseudolocalization_enabled() const; + void set_pseudolocalization_enabled(bool p_enabled); + void set_editor_pseudolocalization(bool p_enabled); + void reload_pseudolocalization(); + static Vector<String> get_all_locales(); static Vector<String> get_all_locale_names(); static bool is_locale_valid(const String &p_locale); diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 7beecdb6b5..a3b5356b1d 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,24 +1546,31 @@ 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; - /* 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)); + int decimals = 14; #else - int decimals = 6 - (int)floor(log10(p_num)); + int decimals = 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; } @@ -1704,7 +1648,6 @@ String String::num_scientific(double p_num) { return "inf"; } } -#ifndef NO_USE_STDLIB char buf[256]; @@ -1727,10 +1670,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 +2137,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 +2178,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 +2208,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 +2237,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 +2268,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 +2388,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 +2463,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 +2553,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 +2570,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; @@ -3800,12 +3739,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 +3769,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 +3791,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 +3899,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 +4127,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 +4155,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,7 +4324,7 @@ 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(); } diff --git a/core/string/ustring.h b/core/string/ustring.h index ffb354d6e1..6a4b40da60 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -398,7 +398,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; diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h index c985593473..ba9babe0af 100644 --- a/core/templates/cowdata.h +++ b/core/templates/cowdata.h @@ -232,7 +232,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 +286,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 +323,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); } 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/rid_owner.h b/core/templates/rid_owner.h index 8d139551ef..e947d2211c 100644 --- a/core/templates/rid_owner.h +++ b/core/templates/rid_owner.h @@ -53,14 +53,16 @@ protected: return rid; } - static uint64_t _gen_id() { - return base_id.increment(); - } - static RID _gen_rid() { return _make_from_id(_gen_id()); } + friend struct VariantUtilityFunctions; + + static uint64_t _gen_id() { + return base_id.increment(); + } + public: virtual ~RID_AllocBase() {} }; diff --git a/core/templates/safe_list.h b/core/templates/safe_list.h new file mode 100644 index 0000000000..d8f010663b --- /dev/null +++ b/core/templates/safe_list.h @@ -0,0 +1,375 @@ +/*************************************************************************/ +/* safe_list.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SAFE_LIST_H +#define SAFE_LIST_H + +#include "core/os/memory.h" +#include "core/typedefs.h" +#include <functional> + +#if !defined(NO_THREADS) + +#include <atomic> +#include <type_traits> + +// Design goals for these classes: +// - Accessing this list with an iterator will never result in a use-after free, +// even if the element being accessed has been logically removed from the list on +// another thread. +// - Logical deletion from the list will not result in deallocation at that time, +// instead the node will be deallocated at a later time when it is safe to do so. +// - No blocking synchronization primitives will be used. + +// This is used in very specific areas of the engine where it's critical that these guarantees are held. + +template <class T, class A = DefaultAllocator> +class SafeList { + struct SafeListNode { + std::atomic<SafeListNode *> next = nullptr; + + // If the node is logically deleted, this pointer will typically point + // to the previous list item in time that was also logically deleted. + std::atomic<SafeListNode *> graveyard_next = nullptr; + + std::function<void(T)> deletion_fn = [](T t) { return; }; + + T val; + }; + + static_assert(std::atomic<T>::is_always_lock_free); + + std::atomic<SafeListNode *> head = nullptr; + std::atomic<SafeListNode *> graveyard_head = nullptr; + + std::atomic_uint active_iterator_count = 0; + +public: + class Iterator { + friend class SafeList; + + SafeListNode *cursor; + SafeList *list; + + Iterator(SafeListNode *p_cursor, SafeList *p_list) : + cursor(p_cursor), list(p_list) { + list->active_iterator_count++; + } + + public: + Iterator(const Iterator &p_other) : + cursor(p_other.cursor), list(p_other.list) { + list->active_iterator_count++; + } + + ~Iterator() { + list->active_iterator_count--; + } + + public: + T &operator*() { + return cursor->val; + } + + Iterator &operator++() { + cursor = cursor->next; + return *this; + } + + // These two operators are mostly useful for comparisons to nullptr. + bool operator==(const void *p_other) const { + return cursor == p_other; + } + + bool operator!=(const void *p_other) const { + return cursor != p_other; + } + + // These two allow easy range-based for loops. + bool operator==(const Iterator &p_other) const { + return cursor == p_other.cursor; + } + + bool operator!=(const Iterator &p_other) const { + return cursor != p_other.cursor; + } + }; + +public: + // Calling this will cause an allocation. + void insert(T p_value) { + SafeListNode *new_node = memnew_allocator(SafeListNode, A); + new_node->val = p_value; + SafeListNode *expected_head = nullptr; + do { + expected_head = head.load(); + new_node->next.store(expected_head); + } while (!head.compare_exchange_strong(/* expected= */ expected_head, /* new= */ new_node)); + } + + Iterator find(T p_value) { + for (Iterator it = begin(); it != end(); ++it) { + if (*it == p_value) { + return it; + } + } + return end(); + } + + void erase(T p_value, std::function<void(T)> p_deletion_fn) { + Iterator tmp = find(p_value); + erase(tmp, p_deletion_fn); + } + + void erase(T p_value) { + Iterator tmp = find(p_value); + erase(tmp, [](T t) { return; }); + } + + void erase(Iterator &p_iterator, std::function<void(T)> p_deletion_fn) { + p_iterator.cursor->deletion_fn = p_deletion_fn; + erase(p_iterator); + } + + void erase(Iterator &p_iterator) { + if (find(p_iterator.cursor->val) == nullptr) { + // Not in the list, nothing to do. + return; + } + // First, remove the node from the list. + while (true) { + Iterator prev = begin(); + SafeListNode *expected_head = prev.cursor; + for (; prev != end(); ++prev) { + if (prev.cursor && prev.cursor->next == p_iterator.cursor) { + break; + } + } + if (prev != end()) { + // There exists a node before this. + prev.cursor->next.store(p_iterator.cursor->next.load()); + // Done. + break; + } else { + if (head.compare_exchange_strong(/* expected= */ expected_head, /* new= */ p_iterator.cursor->next.load())) { + // Successfully reassigned the head pointer before another thread changed it to something else. + break; + } + // Fall through upon failure, try again. + } + } + // Then queue it for deletion by putting it in the node graveyard. + // Don't touch `next` because an iterator might still be pointing at this node. + SafeListNode *expected_head = nullptr; + do { + expected_head = graveyard_head.load(); + p_iterator.cursor->graveyard_next.store(expected_head); + } while (!graveyard_head.compare_exchange_strong(/* expected= */ expected_head, /* new= */ p_iterator.cursor)); + } + + Iterator begin() { + return Iterator(head.load(), this); + } + + Iterator end() { + return Iterator(nullptr, this); + } + + // Calling this will cause zero to many deallocations. + void maybe_cleanup() { + SafeListNode *cursor = nullptr; + SafeListNode *new_graveyard_head = nullptr; + do { + // The access order here is theoretically important. + cursor = graveyard_head.load(); + if (active_iterator_count.load() != 0) { + // It's not safe to clean up with an active iterator, because that iterator + // could be pointing to an element that we want to delete. + return; + } + // Any iterator created after this point will never point to a deleted node. + // Swap it out with the current graveyard head. + } while (!graveyard_head.compare_exchange_strong(/* expected= */ cursor, /* new= */ new_graveyard_head)); + // Our graveyard list is now unreachable by any active iterators, + // detached from the main graveyard head and ready for deletion. + while (cursor) { + SafeListNode *tmp = cursor; + cursor = cursor->graveyard_next; + tmp->deletion_fn(tmp->val); + memdelete_allocator<SafeListNode, A>(tmp); + } + } +}; + +#else // NO_THREADS + +// Effectively the same structure without the atomics. It's probably possible to simplify it but the semantics shouldn't differ greatly. +template <class T, class A = DefaultAllocator> +class SafeList { + struct SafeListNode { + SafeListNode *next = nullptr; + + // If the node is logically deleted, this pointer will typically point to the previous list item in time that was also logically deleted. + SafeListNode *graveyard_next = nullptr; + + std::function<void(T)> deletion_fn = [](T t) { return; }; + + T val; + }; + + SafeListNode *head = nullptr; + SafeListNode *graveyard_head = nullptr; + + unsigned int active_iterator_count = 0; + +public: + class Iterator { + friend class SafeList; + + SafeListNode *cursor; + SafeList *list; + + public: + Iterator(SafeListNode *p_cursor, SafeList *p_list) : + cursor(p_cursor), list(p_list) { + list->active_iterator_count++; + } + + ~Iterator() { + list->active_iterator_count--; + } + + T &operator*() { + return cursor->val; + } + + Iterator &operator++() { + cursor = cursor->next; + return *this; + } + + // These two operators are mostly useful for comparisons to nullptr. + bool operator==(const void *p_other) const { + return cursor == p_other; + } + + bool operator!=(const void *p_other) const { + return cursor != p_other; + } + + // These two allow easy range-based for loops. + bool operator==(const Iterator &p_other) const { + return cursor == p_other.cursor; + } + + bool operator!=(const Iterator &p_other) const { + return cursor != p_other.cursor; + } + }; + +public: + // Calling this will cause an allocation. + void insert(T p_value) { + SafeListNode *new_node = memnew_allocator(SafeListNode, A); + new_node->val = p_value; + new_node->next = head; + head = new_node; + } + + Iterator find(T p_value) { + for (Iterator it = begin(); it != end(); ++it) { + if (*it == p_value) { + return it; + } + } + return end(); + } + + void erase(T p_value, std::function<void(T)> p_deletion_fn) { + erase(find(p_value), p_deletion_fn); + } + + void erase(T p_value) { + erase(find(p_value), [](T t) { return; }); + } + + void erase(Iterator p_iterator, std::function<void(T)> p_deletion_fn) { + p_iterator.cursor->deletion_fn = p_deletion_fn; + erase(p_iterator); + } + + void erase(Iterator p_iterator) { + Iterator prev = begin(); + for (; prev != end(); ++prev) { + if (prev.cursor && prev.cursor->next == p_iterator.cursor) { + break; + } + } + if (prev == end()) { + // Not in the list, nothing to do. + return; + } + // First, remove the node from the list. + prev.cursor->next = p_iterator.cursor->next; + + // Then queue it for deletion by putting it in the node graveyard. Don't touch `next` because an iterator might still be pointing at this node. + p_iterator.cursor->graveyard_next = graveyard_head; + graveyard_head = p_iterator.cursor; + } + + Iterator begin() { + return Iterator(head, this); + } + + Iterator end() { + return Iterator(nullptr, this); + } + + // Calling this will cause zero to many deallocations. + void maybe_cleanup() { + SafeListNode *cursor = graveyard_head; + if (active_iterator_count != 0) { + // It's not safe to clean up with an active iterator, because that iterator could be pointing to an element that we want to delete. + return; + } + graveyard_head = nullptr; + // Our graveyard list is now unreachable by any active iterators, detached from the main graveyard head and ready for deletion. + while (cursor) { + SafeListNode *tmp = cursor; + cursor = cursor->next; + tmp->deletion_fn(tmp->val); + memdelete_allocator<SafeListNode, A>(tmp); + } + } +}; + +#endif + +#endif // SAFE_LIST_H diff --git a/core/templates/set.h b/core/templates/set.h index 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/variant/array.cpp b/core/variant/array.cpp index 09cf785390..8373cbd4e8 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -203,9 +203,9 @@ Error Array::resize(int p_new_size) { return _p->array.resize(p_new_size); } -void Array::insert(int p_pos, const Variant &p_value) { - ERR_FAIL_COND(!_p->typed.validate(p_value, "insert")); - _p->array.insert(p_pos, p_value); +Error Array::insert(int p_pos, const Variant &p_value) { + ERR_FAIL_COND_V(!_p->typed.validate(p_value, "insert"), ERR_INVALID_PARAMETER); + return _p->array.insert(p_pos, p_value); } void Array::fill(const Variant &p_value) { @@ -535,8 +535,8 @@ void Array::push_front(const Variant &p_value) { Variant Array::pop_back() { if (!_p->array.is_empty()) { - int n = _p->array.size() - 1; - Variant ret = _p->array.get(n); + const int n = _p->array.size() - 1; + const Variant ret = _p->array.get(n); _p->array.resize(n); return ret; } @@ -545,13 +545,38 @@ Variant Array::pop_back() { Variant Array::pop_front() { if (!_p->array.is_empty()) { - Variant ret = _p->array.get(0); + const Variant ret = _p->array.get(0); _p->array.remove(0); return ret; } return Variant(); } +Variant Array::pop_at(int p_pos) { + if (_p->array.is_empty()) { + // Return `null` without printing an error to mimic `pop_back()` and `pop_front()` behavior. + return Variant(); + } + + if (p_pos < 0) { + // Relative offset from the end + p_pos = _p->array.size() + p_pos; + } + + ERR_FAIL_INDEX_V_MSG( + p_pos, + _p->array.size(), + Variant(), + vformat( + "The calculated index %s is out of bounds (the array has %s elements). Leaving the array untouched and returning `null`.", + p_pos, + _p->array.size())); + + const Variant ret = _p->array.get(p_pos); + _p->array.remove(p_pos); + return ret; +} + Variant Array::min() const { Variant minval; for (int i = 0; i < size(); i++) { diff --git a/core/variant/array.h b/core/variant/array.h index 540dcb1f4e..4a1b25c4a9 100644 --- a/core/variant/array.h +++ b/core/variant/array.h @@ -72,7 +72,7 @@ public: void append_array(const Array &p_array); Error resize(int p_new_size); - void insert(int p_pos, const Variant &p_value); + Error insert(int p_pos, const Variant &p_value); void remove(int p_pos); void fill(const Variant &p_value); @@ -97,6 +97,7 @@ public: void push_front(const Variant &p_value); Variant pop_back(); Variant pop_front(); + Variant pop_at(int p_pos); Array duplicate(bool p_deep = false) const; diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index 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/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..b4ec0df7d6 --- /dev/null +++ b/core/variant/native_ptr.h @@ -0,0 +1,130 @@ +/*************************************************************************/ +/* native_ptr.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NATIVE_PTR_H +#define NATIVE_PTR_H + +#include "core/math/audio_frame.h" +#include "core/variant/method_ptrcall.h" +#include "core/variant/type_info.h" + +template <class T> +struct GDNativeConstPtr { + const T *data = nullptr; + GDNativeConstPtr(const T *p_assign) { data = p_assign; } + static const char *get_name() { return "const void"; } + operator const T *() const { return data; } + operator Variant() const { return uint64_t(data); } +}; + +template <class T> +struct GDNativePtr { + T *data = nullptr; + GDNativePtr(T *p_assign) { data = p_assign; } + static const char *get_name() { return "void"; } + operator T *() const { return data; } + operator Variant() const { return uint64_t(data); } +}; + +#define GDVIRTUAL_NATIVE_PTR(m_type) \ + template <> \ + struct GDNativeConstPtr<m_type> { \ + const m_type *data = nullptr; \ + GDNativeConstPtr(const m_type *p_assign) { data = p_assign; } \ + static const char *get_name() { return "const " #m_type; } \ + operator const m_type *() const { return data; } \ + operator Variant() const { return uint64_t(data); } \ + }; \ + template <> \ + struct GDNativePtr<m_type> { \ + m_type *data = nullptr; \ + GDNativePtr(m_type *p_assign) { data = p_assign; } \ + static const char *get_name() { return #m_type; } \ + operator m_type *() const { return data; } \ + operator Variant() const { return uint64_t(data); } \ + }; + +template <class T> +struct GetTypeInfo<GDNativeConstPtr<T>> { + static const Variant::Type VARIANT_TYPE = Variant::NIL; + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; + static inline PropertyInfo get_class_info() { + return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_INT_IS_POINTER, GDNativeConstPtr<T>::get_name()); + } +}; + +template <class T> +struct GetTypeInfo<GDNativePtr<T>> { + static const Variant::Type VARIANT_TYPE = Variant::NIL; + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; + static inline PropertyInfo get_class_info() { + return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_INT_IS_POINTER, GDNativePtr<T>::get_name()); + } +}; + +template <class T> +struct PtrToArg<GDNativeConstPtr<T>> { + _FORCE_INLINE_ static GDNativeConstPtr<T> convert(const void *p_ptr) { + return GDNativeConstPtr<T>(reinterpret_cast<const T *>(p_ptr)); + } + typedef const T *EncodeT; + _FORCE_INLINE_ static void encode(GDNativeConstPtr<T> p_val, void *p_ptr) { + *((const T **)p_ptr) = p_val.data; + } +}; +template <class T> +struct PtrToArg<GDNativePtr<T>> { + _FORCE_INLINE_ static GDNativePtr<T> convert(const void *p_ptr) { + return GDNativePtr<T>(reinterpret_cast<const T *>(p_ptr)); + } + typedef T *EncodeT; + _FORCE_INLINE_ static void encode(GDNativePtr<T> p_val, void *p_ptr) { + *((T **)p_ptr) = p_val.data; + } +}; + +GDVIRTUAL_NATIVE_PTR(AudioFrame) +GDVIRTUAL_NATIVE_PTR(bool) +GDVIRTUAL_NATIVE_PTR(char) +GDVIRTUAL_NATIVE_PTR(char16_t) +GDVIRTUAL_NATIVE_PTR(char32_t) +GDVIRTUAL_NATIVE_PTR(wchar_t) +GDVIRTUAL_NATIVE_PTR(uint8_t) +GDVIRTUAL_NATIVE_PTR(int8_t) +GDVIRTUAL_NATIVE_PTR(uint16_t) +GDVIRTUAL_NATIVE_PTR(int16_t) +GDVIRTUAL_NATIVE_PTR(uint32_t) +GDVIRTUAL_NATIVE_PTR(int32_t) +GDVIRTUAL_NATIVE_PTR(int64_t) +GDVIRTUAL_NATIVE_PTR(uint64_t) +GDVIRTUAL_NATIVE_PTR(float) +GDVIRTUAL_NATIVE_PTR(double) + +#endif // NATIVE_PTR_H diff --git a/core/variant/type_info.h b/core/variant/type_info.h index 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..c5cada674f 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: @@ -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..c9c7eca40d 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -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)); @@ -1709,7 +1710,7 @@ 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, looking_at, sarray("target"), varray(Vector2())); /* Basis */ @@ -1728,6 +1729,7 @@ 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))); /* AABB */ @@ -1805,6 +1807,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()); 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..a245aff35b 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); 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..a214a238a6 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -1423,7 +1423,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, itos(p_variant.operator int64_t())); } break; case Variant::FLOAT: { - String s = rtosfix(p_variant.operator real_t()); + String s = rtosfix(p_variant.operator double()); if (s != "inf" && s != "nan") { if (s.find(".") == -1 && s.find("e") == -1) { s += ".0"; diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 34dbf130b6..f5cb2d40d6 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" @@ -484,6 +486,14 @@ struct VariantUtilityFunctions { return str; } + static inline String error_string(Error error) { + if (error < 0 || error >= ERR_MAX) { + return String("(invalid error code)"); + } + + return String(error_names[error]); + } + static inline void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { if (p_arg_count < 1) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; @@ -720,6 +730,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 @@ -1234,6 +1251,7 @@ void Variant::_register_variant_utility_functions() { FUNCBINDVR(weakref, sarray("obj"), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDR(_typeof, sarray("variable"), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGS(str, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDR(error_string, sarray("error"), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(print, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(printerr, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(printt, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); @@ -1256,6 +1274,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() { |