diff options
Diffstat (limited to 'core')
72 files changed, 3283 insertions, 1473 deletions
diff --git a/core/config/engine.cpp b/core/config/engine.cpp index 495670bc88..d8fbb50a75 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -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 e6b5df2d5a..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()); }; @@ -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 7a2e6765b8..83a3b80803 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,31 +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); +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("get_resource_uid", "path"), &_ResourceLoader::get_resource_uid); + 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); @@ -134,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); @@ -152,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); @@ -167,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; @@ -233,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); @@ -280,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; @@ -368,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(); @@ -388,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), @@ -398,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."); @@ -440,38 +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_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_resources_in_use(bool p_short) { + ::OS::get_singleton()->print_resources_in_use(p_short); } -void _OS::dump_resources_to_file(const String &p_file) { - OS::get_singleton()->dump_resources_to_file(p_file.utf8().get_data()); +void OS::dump_resources_to_file(const String &p_file) { + ::OS::get_singleton()->dump_resources_to_file(p_file.utf8().get_data()); } -String _OS::get_user_data_dir() const { - return OS::get_singleton()->get_user_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 @@ -479,113 +481,113 @@ bool _OS::is_debug_build() const { #endif } -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_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_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("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,12 +1497,12 @@ 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()) { DirAccess *d = DirAccess::create_for_path(p_dir); @@ -1511,7 +1513,7 @@ 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()) { DirAccess *d = DirAccess::create_for_path(p_dir); @@ -1522,7 +1524,7 @@ 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()) { return FileAccess::exists(p_file); @@ -1531,7 +1533,7 @@ bool _Directory::file_exists(String 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()) { DirAccess *d = DirAccess::create_for_path(p_dir); @@ -1543,17 +1545,17 @@ 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."); ERR_FAIL_COND_V_MSG(p_from.is_empty() || p_from == "." || p_from == "..", ERR_INVALID_PARAMETER, "Invalid path to rename."); @@ -1569,7 +1571,7 @@ Error _Directory::rename(String p_from, String p_to) { 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()) { DirAccess *d = DirAccess::create_for_path(p_name); @@ -1581,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."); @@ -1639,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(); @@ -1657,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(); @@ -1680,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(); @@ -1704,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 }; @@ -1794,7 +1796,7 @@ void _Thread::_start_func(void *ud) { } } - 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) { @@ -1820,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); @@ -1832,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; @@ -1861,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 { + +////// ClassDB ////// -PackedStringArray _ClassDB::get_class_list() const { +PackedStringArray ClassDB::get_class_list() const { List<StringName> classes; - ClassDB::get_class_list(&classes); + ::ClassDB::get_class_list(&classes); PackedStringArray ret; ret.resize(classes.size()); @@ -1888,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()); @@ -1902,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(); } @@ -1932,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) { @@ -1957,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()); @@ -1968,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; @@ -1985,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) { @@ -2007,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()); @@ -2021,204 +2025,237 @@ 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 -void _Engine::set_physics_ticks_per_second(int p_ips) { - Engine::get_singleton()->set_physics_ticks_per_second(p_ips); +////// Engine ////// + +void Engine::set_physics_ticks_per_second(int p_ips) { + ::Engine::get_singleton()->set_physics_ticks_per_second(p_ips); } -int _Engine::get_physics_ticks_per_second() const { - return Engine::get_singleton()->get_physics_ticks_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(double 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); } -double _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(); } -double _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(); } -double _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(double 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); } -double _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::set_editor_hint(bool p_enabled) { - Engine::get_singleton()->set_editor_hint(p_enabled); +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); } -bool _Engine::is_editor_hint() const { - return Engine::get_singleton()->is_editor_hint(); +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_print_error_messages(bool p_enabled) { - Engine::get_singleton()->set_print_error_messages(p_enabled); +void Engine::set_editor_hint(bool p_enabled) { + ::Engine::get_singleton()->set_editor_hint(p_enabled); } -bool _Engine::is_printing_error_messages() const { - return Engine::get_singleton()->is_printing_error_messages(); +bool Engine::is_editor_hint() const { + return ::Engine::get_singleton()->is_editor_hint(); } -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); +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(); +} - 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); +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("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("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_main_loop"), &_Engine::get_main_loop); + 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_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_main_loop"), &Engine::get_main_loop); - ClassDB::bind_method(D_METHOD("is_in_physics_frame"), &_Engine::is_in_physics_frame); + 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("has_singleton", "name"), &_Engine::has_singleton); - ClassDB::bind_method(D_METHOD("get_singleton", "name"), &_Engine::get_singleton_object); + ClassDB::bind_method(D_METHOD("is_in_physics_frame"), &Engine::is_in_physics_frame); - 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("has_singleton", "name"), &Engine::has_singleton); + ClassDB::bind_method(D_METHOD("get_singleton", "name"), &Engine::get_singleton_object); - 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("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_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"); @@ -2228,92 +2265,74 @@ void _Engine::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "physics_jitter_fix"), "set_physics_jitter_fix", "get_physics_jitter_fix"); } -_Engine *_Engine::singleton = nullptr; - -////// _EngineDebugger ////// +Engine *Engine::singleton = nullptr; -void _EngineDebugger::_bind_methods() { - ClassDB::bind_method(D_METHOD("is_active"), &_EngineDebugger::is_active); +////// EngineDebugger ////// - 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); +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; @@ -2326,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; @@ -2339,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, double p_frame_time, double p_idle_time, double p_physics_time, double 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; @@ -2352,7 +2371,7 @@ void _EngineDebugger::call_tick(void *p_user, double p_frame_time, double p_idle 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; @@ -2368,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 94e8ab2a34..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()); @@ -77,18 +81,15 @@ public: 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 { @@ -101,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 { @@ -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,19 +594,21 @@ 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; } + static Engine *get_singleton() { return singleton; } void set_physics_ticks_per_second(int p_ips); int get_physics_ticks_per_second() 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(); @@ -714,8 +704,29 @@ public: 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 5791f79053..4f3f1fd16e 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -558,6 +558,7 @@ void register_global_constants() { 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); @@ -605,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/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 index a8355065fe..e1e94dd65d 100644 --- a/core/error/error_list.cpp +++ b/core/error/error_list.cpp @@ -31,55 +31,55 @@ #include "error_list.h" const char *error_names[] = { - "No error", - "Generic error", - "Requested operation is unsupported/unavailable", - "The object hasn't been set up properly", - "Missing credentials for requested resource", - "Parameter out of range", - "Out of memory", - "File not found", - "Bad drive", - "Bad path", - "Permission denied", - "Already in use", - "Can't open file", - "Can't write file", - "Can't read file", - "File unrecognized", - "File corrupt", - "Missing dependencies for file", - "Unexpected eof", - "Can't open resource/socket/file", // File too? What's the difference to ERR_FILE_CANT_OPEN - "Can't create", // What can't be created, - "Query failed", // What query, - "Already in use", - "Resource is locked", - "Timeout", - "Can't connect", - "Can't resolve hostname", // I guessed it's the hostname here. - "Connection error", - "Can't acquire resource", - "Can't fork", - "Invalid data", - "Invalid parameter", - "Item already exists", - "Item does not exist", - "Can't read from database", // Comments say, it's full? Is that correct? - "Can't write to database", // Is the database always full when this is raised? - "Compilation failed", - "Method not found", - "Link failed", - "Script failed", - "Cyclic link detected", - "Invalid declaration", - "Duplicate symbol", - "Parse error", - "Resource is busy", - "Skip error", // ???? What's this? String taken from the docs - "Help error", // More specific? - "Bug", - "Printer on fire", + "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 e7c7f10265..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 { diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index 49570cd1c1..a8547a0090 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -39,6 +39,13 @@ #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); } @@ -653,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; @@ -831,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/native_extension.cpp b/core/extension/native_extension.cpp index 0556c9c84d..a3cd7ca14c 100644 --- a/core/extension/native_extension.cpp +++ b/core/extension/native_extension.cpp @@ -35,6 +35,8 @@ #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; diff --git a/core/extension/native_extension.h b/core/extension/native_extension.h index a961b21cc9..b661381d64 100644 --- a/core/extension/native_extension.h +++ b/core/extension/native_extension.h @@ -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 72563cc40a..9195f7d8b5 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -240,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; @@ -269,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; @@ -290,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; diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index 325cdf2127..50b2099236 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -31,8 +31,8 @@ #include "input_event.h" #include "core/input/input_map.h" +#include "core/input/shortcut.h" #include "core/os/keyboard.h" -#include "scene/gui/shortcut.h" const int InputEvent::DEVICE_ID_TOUCH_MOUSE = -1; const int InputEvent::DEVICE_ID_INTERNAL = -2; @@ -1545,6 +1545,13 @@ 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; } diff --git a/core/input/input_event.h b/core/input/input_event.h index 517d63eb40..3fc8078a09 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -548,6 +548,9 @@ class InputEventShortcut : public InputEvent { Ref<Shortcut> shortcut; +protected: + static void _bind_methods(); + public: void set_shortcut(Ref<Shortcut> p_shortcut); Ref<Shortcut> get_shortcut(); diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index 83ec70757e..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(); } @@ -218,7 +218,7 @@ 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()) { 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/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 f792dc6cb4..c145225751 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -32,15 +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 "scene/resources/packed_scene.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 @@ -100,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; } @@ -144,10 +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() { - replicated_nodes.clear(); + replicator->clear(); connected_peers.clear(); path_get_cache.clear(); path_send_cache.clear(); @@ -325,10 +321,13 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ _process_raw(p_from, p_packet, p_packet_len); } break; case NETWORK_COMMAND_SPAWN: { - _process_spawn_despawn(p_from, p_packet, p_packet_len, true); + replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, true); } break; case NETWORK_COMMAND_DESPAWN: { - _process_spawn_despawn(p_from, p_packet, p_packet_len, false); + 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; } } @@ -338,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."); @@ -353,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) { @@ -382,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; @@ -425,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]; @@ -514,64 +498,6 @@ void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, E->get() = true; } -void MultiplayerAPI::_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 < 18, "Invalid spawn packet received"); - int ofs = 1; - ResourceUID::ID id = decode_uint64(&p_packet[ofs]); - ofs += 8; - ERR_FAIL_COND_MSG(!spawnables.has(id), "Invalid spawn ID received " + itos(id)); - - uint32_t node_target = decode_uint32(&p_packet[ofs]); - Node *parent = _process_get_node(p_from, nullptr, node_target, 0); - 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; - PackedByteArray data; - if (p_packet_len > ofs) { - data.resize(p_packet_len - ofs); - memcpy(data.ptrw(), &p_packet[ofs], data.size()); - } - - SpawnMode mode = spawnables[id]; - if (mode == SPAWN_MODE_SERVER && p_from == 1) { - String scene_path = ResourceUID::get_singleton()->get_id_path(id); - if (p_spawn) { - 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()] = id; - parent->_add_child_nocheck(node, name); - emit_signal(SNAME("network_spawn"), p_from, id, node, data); - } 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("network_despawn"), p_from, id, node, data); - replicated_nodes.erase(node->get_instance_id()); - node->queue_delete(); - } - } else { - if (p_spawn) { - emit_signal(SNAME("network_spawn_request"), p_from, id, parent, name, data); - } else { - emit_signal(SNAME("network_despawn_request"), p_from, id, parent, name, data); - } - } -} - bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) { bool has_all_peers = true; List<int> peers_to_add; // If one is missing, take note to add it. @@ -647,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); @@ -722,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; @@ -906,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; } } @@ -976,14 +902,7 @@ void MultiplayerAPI::_add_peer(int p_id) { connected_peers.insert(p_id); path_get_cache.insert(p_id, PathGetCache()); if (is_network_server()) { - 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); - _send_spawn_despawn(p_id, E.value, node->get_path(), nullptr, 0, true); - } + replicator->spawn_all(p_id); } emit_signal(SNAME("network_peer_connected"), p_id); } @@ -1000,10 +919,6 @@ void MultiplayerAPI::_del_peer(int p_id) { PathSentCache *psc = path_send_cache.getptr(E); psc->confirmed_peers.erase(p_id); } - if (p_id == 1) { - // Erase server replicated nodes, but do not queue them for deletion. - replicated_nodes.clear(); - } emit_signal(SNAME("network_peer_disconnected"), p_id); } @@ -1109,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(); @@ -1147,97 +1092,12 @@ bool MultiplayerAPI::is_object_decoding_allowed() const { return allow_object_decoding; } -Error MultiplayerAPI::spawnable_config(const ResourceUID::ID &p_id, SpawnMode p_mode) { - ERR_FAIL_COND_V(p_mode < SPAWN_MODE_NONE || p_mode > SPAWN_MODE_CUSTOM, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!ResourceUID::get_singleton()->has_id(p_id), ERR_INVALID_PARAMETER); -#ifdef TOOLS_ENABLED - 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 == SPAWN_MODE_NONE) { - if (spawnables.has(p_id)) { - spawnables.erase(p_id); - } - } else { - spawnables[p_id] = p_mode; - } - return OK; -} - -Error MultiplayerAPI::send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const PackedByteArray &p_data) { - return _send_spawn_despawn(p_peer_id, p_scene_id, p_path, p_data.ptr(), p_data.size(), false); +MultiplayerReplicator *MultiplayerAPI::get_replicator() const { + return replicator; } -Error MultiplayerAPI::send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const PackedByteArray &p_data) { - return _send_spawn_despawn(p_peer_id, p_scene_id, p_path, p_data.ptr(), p_data.size(), true); -} - -Error MultiplayerAPI::_send_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const uint8_t *p_data, int p_data_len, bool p_spawn) { - ERR_FAIL_COND_V(!root_node, ERR_UNCONFIGURED); - ERR_FAIL_COND_V_MSG(!spawnables.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id)); - - 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); - - // See if the path is cached. - PathSentCache *psc = path_send_cache.getptr(parent); - if (!psc) { - // Path is not cached, create. - path_send_cache[parent] = PathSentCache(); - psc = path_send_cache.getptr(parent); - psc->id = last_send_cache_id++; - } - _send_confirm_path(root_node->get_node(parent), parent, psc, p_peer_id); - - const CharString cname = String(names[names.size() - 1]).utf8(); - int nlen = encode_cstring(cname.get_data(), nullptr); - MAKE_ROOM(1 + 8 + 4 + 4 + nlen + p_data_len); - uint8_t *ptr = packet_cache.ptrw(); - ptr[0] = p_spawn ? NETWORK_COMMAND_SPAWN : NETWORK_COMMAND_DESPAWN; - int ofs = 1; - ofs += encode_uint64(p_scene_id, &ptr[ofs]); - ofs += encode_uint32(psc->id, &ptr[ofs]); - ofs += encode_uint32(nlen, &ptr[ofs]); - ofs += encode_cstring(cname.get_data(), &ptr[ofs]); - memcpy(&ptr[ofs], p_data, p_data_len); - 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 + p_data_len); -} - -void MultiplayerAPI::scene_enter_exit_notify(const String &p_scene, const Node *p_node, bool p_enter) { - if (!has_network_peer()) { - return; - } - 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 (!spawnables.has(id)) { - return; - } - SpawnMode mode = spawnables[id]; - if (p_enter) { - if (mode == SPAWN_MODE_SERVER && is_network_server()) { - replicated_nodes[p_node->get_instance_id()] = id; - _send_spawn_despawn(0, id, p_node->get_path(), nullptr, 0, true); - } - emit_signal(SNAME("network_spawnable_added"), id, p_node); - } else { - if (mode == SPAWN_MODE_SERVER && is_network_server() && replicated_nodes.has(p_node->get_instance_id())) { - replicated_nodes.erase(p_node->get_instance_id()); - _send_spawn_despawn(0, id, p_node->get_path(), nullptr, 0, false); - } - emit_signal(SNAME("network_spawnable_removed"), id, p_node); - } +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() { @@ -1258,15 +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("spawnable_config", "scene_id", "spawn_mode"), &MultiplayerAPI::spawnable_config); - ClassDB::bind_method(D_METHOD("send_despawn", "peer_id", "scene_id", "path", "data"), &MultiplayerAPI::send_despawn, DEFVAL(PackedByteArray())); - ClassDB::bind_method(D_METHOD("send_spawn", "peer_id", "scene_id", "path", "data"), &MultiplayerAPI::send_spawn, DEFVAL(PackedByteArray())); + 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"))); @@ -1274,27 +1133,18 @@ void MultiplayerAPI::_bind_methods() { ADD_SIGNAL(MethodInfo("connected_to_server")); ADD_SIGNAL(MethodInfo("connection_failed")); ADD_SIGNAL(MethodInfo("server_disconnected")); - ADD_SIGNAL(MethodInfo("network_despawn", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data"))); - ADD_SIGNAL(MethodInfo("network_spawn", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data"))); - ADD_SIGNAL(MethodInfo("network_despawn_request", 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("network_spawn_request", 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("network_spawnable_added", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("network_spawnable_removed", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); 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(SPAWN_MODE_NONE); - BIND_ENUM_CONSTANT(SPAWN_MODE_SERVER); - BIND_ENUM_CONSTANT(SPAWN_MODE_CUSTOM); + 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 9552a0bf35..3c96a3eed1 100644 --- a/core/io/multiplayer_api.h +++ b/core/io/multiplayer_api.h @@ -35,21 +35,16 @@ #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 - }; - - enum SpawnMode { - SPAWN_MODE_NONE, - SPAWN_MODE_SERVER, - SPAWN_MODE_CUSTOM, + 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 { @@ -71,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 { @@ -89,16 +111,15 @@ private: }; Ref<MultiplayerPeer> network_peer; - Map<ResourceUID::ID, SpawnMode> spawnables; int rpc_sender_id = 0; Set<int> connected_peers; HashMap<NodePath, PathSentCache> path_send_cache; Map<int, PathGetCache> path_get_cache; - Map<ObjectID, ResourceUID::ID> replicated_nodes; int last_send_cache_id; Vector<uint8_t> packet_cache; Node *root_node = nullptr; bool allow_object_decoding = false; + MultiplayerReplicator *replicator = nullptr; protected: static void _bind_methods(); @@ -106,7 +127,6 @@ protected: void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len); void _process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len); void _process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len); - void _process_spawn_despawn(int p_from, const uint8_t *p_packet, int p_packet_len, bool p_spawn); Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len); void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len); @@ -114,30 +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, - NETWORK_COMMAND_SPAWN, - NETWORK_COMMAND_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, - }; - void poll(); void clear(); void set_root_node(Node *p_node); @@ -146,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); @@ -166,17 +171,12 @@ public: void set_allow_object_decoding(bool p_enable); bool is_object_decoding_allowed() const; - Error spawnable_config(const ResourceUID::ID &p_id, SpawnMode p_mode); - Error send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const PackedByteArray &p_data = PackedByteArray()); - Error send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const PackedByteArray &p_data = PackedByteArray()); - Error _send_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const uint8_t *p_data, int p_data_len, bool p_spawn); - void scene_enter_exit_notify(const String &p_scene, const Node *p_node, bool p_enter); + MultiplayerReplicator *get_replicator() const; MultiplayerAPI(); ~MultiplayerAPI(); }; VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode); -VARIANT_ENUM_CAST(MultiplayerAPI::SpawnMode); #endif // MULTIPLAYER_API_H 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_loader.cpp b/core/io/resource_loader.cpp index d02d827443..64237f3b15 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"); } /////////////////////////////////// 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/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 e1a5bfe6f2..8e5830f9b3 100644 --- a/core/math/geometry_2d.h +++ b/core/math/geometry_2d.h @@ -182,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; } @@ -193,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; } @@ -353,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/transform_3d.h b/core/math/transform_3d.h index cadfdc13d1..345e0fade0 100644 --- a/core/math/transform_3d.h +++ b/core/math/transform_3d.h @@ -155,7 +155,7 @@ _FORCE_INLINE_ Plane Transform3D::xform_inv(const Plane &p_plane) const { } _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; 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/object/class_db.cpp b/core/object/class_db.cpp index c58fe7bc24..e268a8d292 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -892,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; @@ -1068,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; @@ -1407,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; @@ -1417,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; diff --git a/core/object/class_db.h b/core/object/class_db.h index bfc9d6f283..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; @@ -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 9948620c73..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) \\ 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;\\ +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 2bb4b981b9..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); diff --git a/core/object/object.h b/core/object/object.h index 6523105820..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; @@ -277,7 +283,14 @@ struct ObjectNativeExtension { }; #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. @@ -299,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 { \ @@ -372,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(); \ } \ @@ -415,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); \ 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/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/register_core_types.cpp b/core/register_core_types.cpp index f801be3db3..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" @@ -84,18 +86,18 @@ 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; @@ -144,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); @@ -193,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); @@ -203,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); @@ -240,16 +245,16 @@ void register_core_types() { 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() { @@ -266,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())); @@ -303,13 +308,7 @@ void register_core_singletons() { void register_core_extensions() { // Hardcoded for now. NativeExtension::initialize_native_extensions(); - 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]); - } - } + native_extension_manager->load_extensions(); native_extension_manager->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_CORE); } diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 2fdf6e84e5..a30d6b9102 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 @@ -1393,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; @@ -1460,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) { @@ -1625,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; } @@ -1720,7 +1648,6 @@ String String::num_scientific(double p_num) { return "inf"; } } -#ifndef NO_USE_STDLIB char buf[256]; @@ -1743,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) { 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/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/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 d538b9faff..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: diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index c3481d4896..382b256c08 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1422,6 +1422,7 @@ static void _register_variant_builtin_methods() { bind_method(String, is_absolute_path, sarray(), varray()); bind_method(String, is_rel_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)); @@ -1806,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_internal.h b/core/variant/variant_internal.h index 566b14736e..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(); @@ -1173,7 +1177,7 @@ struct VariantInitializer<PackedColorArray> { template <> struct VariantInitializer<Object *> { - static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::object_assign_null(v); } + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_object(v); } }; template <class T> @@ -1405,4 +1409,22 @@ struct VariantTypeConstructor { } }; +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_utility.cpp b/core/variant/variant_utility.cpp index 6c57d1de10..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" @@ -728,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 @@ -1265,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() { |