diff options
Diffstat (limited to 'core')
131 files changed, 4041 insertions, 2235 deletions
diff --git a/core/SCsub b/core/SCsub index d9167b8f83..c12dd4e60e 100644 --- a/core/SCsub +++ b/core/SCsub @@ -146,6 +146,7 @@ env.core_sources += thirdparty_obj # Godot source files env.add_source_files(env.core_sources, "*.cpp") +env.add_source_files(env.core_sources, "script_encryption_key.gen.cpp") # Certificates env.Depends( @@ -183,6 +184,7 @@ SConscript("os/SCsub") SConscript("math/SCsub") SConscript("crypto/SCsub") SConscript("io/SCsub") +SConscript("multiplayer/SCsub") SConscript("debugger/SCsub") SConscript("input/SCsub") SConscript("variant/SCsub") diff --git a/core/config/engine.cpp b/core/config/engine.cpp index d8fbb50a75..dc5b3e25c6 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -111,7 +111,7 @@ Dictionary Engine::get_version_info() const { static Array array_from_info(const char *const *info_list) { Array arr; for (int i = 0; info_list[i] != nullptr; i++) { - arr.push_back(info_list[i]); + arr.push_back(String::utf8(info_list[i])); } return arr; } @@ -119,7 +119,7 @@ static Array array_from_info(const char *const *info_list) { static Array array_from_info_count(const char *const *info_list, int info_count) { Array arr; for (int i = 0; i < info_count; i++) { - arr.push_back(info_list[i]); + arr.push_back(String::utf8(info_list[i])); } return arr; } @@ -140,14 +140,14 @@ Array Engine::get_copyright_info() const { for (int component_index = 0; component_index < COPYRIGHT_INFO_COUNT; component_index++) { const ComponentCopyright &cp_info = COPYRIGHT_INFO[component_index]; Dictionary component_dict; - component_dict["name"] = cp_info.name; + component_dict["name"] = String::utf8(cp_info.name); Array parts; for (int i = 0; i < cp_info.part_count; i++) { const ComponentCopyrightPart &cp_part = cp_info.parts[i]; Dictionary part_dict; part_dict["files"] = array_from_info_count(cp_part.files, cp_part.file_count); part_dict["copyright"] = array_from_info_count(cp_part.copyright_statements, cp_part.copyright_count); - part_dict["license"] = cp_part.license; + part_dict["license"] = String::utf8(cp_part.license); parts.push_back(part_dict); } component_dict["parts"] = parts; diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 03892d1d4f..562cbbdd27 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -48,18 +48,24 @@ ProjectSettings *ProjectSettings::get_singleton() { return singleton; } +String ProjectSettings::get_project_data_dir_name() const { + return project_data_dir_name; +} + +String ProjectSettings::get_project_data_path() const { + return "res://" + get_project_data_dir_name(); +} + String ProjectSettings::get_resource_path() const { return resource_path; } -const String ProjectSettings::IMPORTED_FILES_PATH("res://.godot/imported"); +String ProjectSettings::get_imported_files_path() const { + return get_project_data_path().plus_file("imported"); +} String ProjectSettings::localize_path(const String &p_path) const { - if (resource_path == "") { - return p_path; //not initialized yet - } - - if (p_path.begins_with("res://") || p_path.begins_with("user://") || + if (resource_path.is_empty() || p_path.begins_with("res://") || p_path.begins_with("user://") || (p_path.is_absolute_path() && !p_path.begins_with(resource_path))) { return p_path.simplify_path(); } @@ -256,15 +262,15 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const { Set<_VCSort> vclist; - for (Map<StringName, VariantContainer>::Element *E = props.front(); E; E = E->next()) { - const VariantContainer *v = &E->get(); + for (const KeyValue<StringName, VariantContainer> &E : props) { + const VariantContainer *v = &E.value; if (v->hide_from_editor) { continue; } _VCSort vc; - vc.name = E->key(); + vc.name = E.key; vc.order = v->order; vc.type = v->variant.get_type(); if (vc.name.begins_with("input/") || vc.name.begins_with("import/") || vc.name.begins_with("export/") || vc.name.begins_with("/remap") || vc.name.begins_with("/locale") || vc.name.begins_with("/autoload")) { @@ -322,14 +328,14 @@ bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_f void ProjectSettings::_convert_to_last_version(int p_from_version) { if (p_from_version <= 3) { // Converts the actions from array to dictionary (array of events to dictionary with deadzone + events) - for (Map<StringName, ProjectSettings::VariantContainer>::Element *E = props.front(); E; E = E->next()) { - Variant value = E->get().variant; - if (String(E->key()).begins_with("input/") && value.get_type() == Variant::ARRAY) { + for (KeyValue<StringName, ProjectSettings::VariantContainer> &E : props) { + Variant value = E.value.variant; + if (String(E.key).begins_with("input/") && value.get_type() == Variant::ARRAY) { Array array = value; Dictionary action; action["deadzone"] = Variant(0.5f); action["events"] = array; - E->get().variant = action; + E.value.variant = action; } } } @@ -513,6 +519,10 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo _load_settings_text(custom_settings); } } + + // Updating the default value after the project settings have loaded. + project_data_dir_name = GLOBAL_GET("application/config/project_data_dir_name"); + // Using GLOBAL_GET on every block for compressing can be slow, so assigning here. Compression::zstd_long_distance_matching = GLOBAL_GET("compression/formats/zstd/long_distance_matching"); Compression::zstd_level = GLOBAL_GET("compression/formats/zstd/compression_level"); @@ -699,8 +709,8 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str int count = 0; - for (Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) { - count += E->get().size(); + for (const KeyValue<String, List<String>> &E : props) { + count += E.value.size(); } if (p_custom_features != String()) { @@ -792,7 +802,7 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const Map<Strin } file->store_string("\n"); - for (Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) { + for (const Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) { if (E != props.front()) { file->store_string("\n"); } @@ -835,19 +845,19 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust Set<_VCSort> vclist; if (p_merge_with_current) { - for (Map<StringName, VariantContainer>::Element *G = props.front(); G; G = G->next()) { - const VariantContainer *v = &G->get(); + for (const KeyValue<StringName, VariantContainer> &G : props) { + const VariantContainer *v = &G.value; if (v->hide_from_editor) { continue; } - if (p_custom.has(G->key())) { + if (p_custom.has(G.key)) { continue; } _VCSort vc; - vc.name = G->key(); //*k; + vc.name = G.key; //*k; vc.order = v->order; vc.type = v->variant.get_type(); vc.flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE; @@ -859,14 +869,14 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust } } - for (const Map<String, Variant>::Element *E = p_custom.front(); E; E = E->next()) { + for (const KeyValue<String, Variant> &E : p_custom) { // Lookup global prop to store in the same order - Map<StringName, VariantContainer>::Element *global_prop = props.find(E->key()); + Map<StringName, VariantContainer>::Element *global_prop = props.find(E.key); _VCSort vc; - vc.name = E->key(); + vc.name = E.key; vc.order = global_prop ? global_prop->get().order : 0xFFFFFFF; - vc.type = E->get().get_type(); + vc.type = E.value.get_type(); vc.flags = PROPERTY_USAGE_STORAGE; vclist.insert(vc); } @@ -1084,6 +1094,7 @@ ProjectSettings::ProjectSettings() { custom_prop_info["application/run/main_scene"] = PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "*.tscn,*.scn,*.res"); GLOBAL_DEF("application/run/disable_stdout", false); GLOBAL_DEF("application/run/disable_stderr", false); + project_data_dir_name = GLOBAL_DEF_RST("application/config/project_data_dir_name", ".godot"); GLOBAL_DEF("application/config/use_custom_user_dir", false); GLOBAL_DEF("application/config/custom_user_dir_name", ""); GLOBAL_DEF("application/config/project_settings_override", ""); diff --git a/core/config/project_settings.h b/core/config/project_settings.h index 7e93f26f0d..82f04b94df 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -42,7 +42,6 @@ class ProjectSettings : public Object { public: typedef Map<String, Variant> CustomMap; - static const String IMPORTED_FILES_PATH; enum { //properties that are not for built in values begin from this value, so builtin ones are displayed first @@ -94,6 +93,8 @@ protected: OrderedHashMap<StringName, AutoloadInfo> autoloads; + String project_data_dir_name; + bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; @@ -141,7 +142,10 @@ public: bool property_can_revert(const String &p_name); Variant property_get_revert(const String &p_name); + String get_project_data_dir_name() const; + String get_project_data_path() const; String get_resource_path() const; + String get_imported_files_path() const; static ProjectSettings *get_singleton(); diff --git a/core/core_bind.cpp b/core/core_bind.cpp index fd5b3bb731..3a4fddc670 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -286,6 +286,10 @@ String OS::get_locale() const { return ::OS::get_singleton()->get_locale(); } +String OS::get_locale_language() const { + return ::OS::get_singleton()->get_locale_language(); +} + String OS::get_model_name() const { return ::OS::get_singleton()->get_model_name(); } @@ -547,6 +551,7 @@ void OS::_bind_methods() { ClassDB::bind_method(D_METHOD("delay_usec", "usec"), &OS::delay_usec); ClassDB::bind_method(D_METHOD("delay_msec", "msec"), &OS::delay_msec); ClassDB::bind_method(D_METHOD("get_locale"), &OS::get_locale); + ClassDB::bind_method(D_METHOD("get_locale_language"), &OS::get_locale_language); ClassDB::bind_method(D_METHOD("get_model_name"), &OS::get_model_name); ClassDB::bind_method(D_METHOD("is_userfs_persistent"), &OS::is_userfs_persistent); @@ -1072,79 +1077,79 @@ bool File::is_open() const { } String File::get_path() const { - ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use, or is lacking read-write permission."); return f->get_path(); } String File::get_path_absolute() const { - ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use, or is lacking read-write permission."); return f->get_path_absolute(); } void File::seek(int64_t p_position) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); 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) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); f->seek_end(p_position); } uint64_t File::get_position() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission."); return f->get_position(); } uint64_t File::get_length() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission."); return f->get_length(); } bool File::eof_reached() const { - ERR_FAIL_COND_V_MSG(!f, false, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, false, "File must be opened before use, or is lacking read-write permission."); return f->eof_reached(); } uint8_t File::get_8() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission."); return f->get_8(); } uint16_t File::get_16() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission."); return f->get_16(); } uint32_t File::get_32() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission."); return f->get_32(); } uint64_t File::get_64() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission."); return f->get_64(); } float File::get_float() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission."); return f->get_float(); } double File::get_double() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission."); return f->get_double(); } real_t File::get_real() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission."); return f->get_real(); } 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."); + ERR_FAIL_COND_V_MSG(!f, data, "File must be opened before use, or is lacking read-write permission."); ERR_FAIL_COND_V_MSG(p_length < 0, data, "Length of buffer cannot be smaller than 0."); if (p_length == 0) { @@ -1165,7 +1170,7 @@ Vector<uint8_t> File::get_buffer(int64_t p_length) const { } String File::get_as_text() const { - ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use, or is lacking read-write permission."); String text; uint64_t original_pos = f->get_position(); @@ -1192,12 +1197,12 @@ String File::get_sha256(const String &p_path) const { } String File::get_line() const { - ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use, or is lacking read-write permission."); return f->get_line(); } 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."); + ERR_FAIL_COND_V_MSG(!f, Vector<String>(), "File must be opened before use, or is lacking read-write permission."); return f->get_csv_line(p_delim); } @@ -1225,77 +1230,77 @@ Error File::get_error() const { } void File::store_8(uint8_t p_dest) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); f->store_8(p_dest); } void File::store_16(uint16_t p_dest) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); f->store_16(p_dest); } void File::store_32(uint32_t p_dest) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); f->store_32(p_dest); } void File::store_64(uint64_t p_dest) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); f->store_64(p_dest); } void File::store_float(float p_dest) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); f->store_float(p_dest); } void File::store_double(double p_dest) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); f->store_double(p_dest); } void File::store_real(real_t p_real) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); f->store_real(p_real); } void File::store_string(const String &p_string) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); f->store_string(p_string); } void File::store_pascal_string(const String &p_string) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); f->store_pascal_string(p_string); } String File::get_pascal_string() { - ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use, or is lacking read-write permission."); return f->get_pascal_string(); } void File::store_line(const String &p_string) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); f->store_line(p_string); } 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."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); f->store_csv_line(p_values, p_delim); } void File::store_buffer(const Vector<uint8_t> &p_buffer) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); uint64_t len = p_buffer.size(); if (len == 0) { @@ -1312,7 +1317,7 @@ bool File::file_exists(const String &p_name) const { } void File::store_var(const Variant &p_var, bool p_full_objects) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission."); int len; Error err = encode_variant(p_var, nullptr, len, p_full_objects); ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant."); @@ -1329,7 +1334,7 @@ void File::store_var(const Variant &p_var, bool p_full_objects) { } Variant File::get_var(bool p_allow_objects) const { - ERR_FAIL_COND_V_MSG(!f, Variant(), "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!f, Variant(), "File must be opened before use, or is lacking read-write permission."); uint32_t len = get_32(); Vector<uint8_t> buff = get_buffer(len); ERR_FAIL_COND_V((uint32_t)buff.size() != len, Variant()); @@ -1763,6 +1768,13 @@ void Thread::_start_func(void *ud) { Ref<Thread> *tud = (Ref<Thread> *)ud; Ref<Thread> t = *tud; memdelete(tud); + + Object *target_instance = t->target_callable.get_object(); + if (!target_instance) { + t->running.clear(); + ERR_FAIL_MSG(vformat("Could not call function '%s' on previously freed instance to start thread %s.", t->target_callable.get_method(), t->get_id())); + } + Callable::CallError ce; const Variant *arg[1] = { &t->userdata }; int argc = 0; @@ -1781,58 +1793,43 @@ void Thread::_start_func(void *ud) { // We must check if we are in case b). int target_param_count = 0; int target_default_arg_count = 0; - Ref<Script> script = t->target_instance->get_script(); + Ref<Script> script = target_instance->get_script(); if (script.is_valid()) { - MethodInfo mi = script->get_method_info(t->target_method); + MethodInfo mi = script->get_method_info(t->target_callable.get_method()); target_param_count = mi.arguments.size(); target_default_arg_count = mi.default_arguments.size(); } else { - MethodBind *method = ClassDB::get_method(t->target_instance->get_class_name(), t->target_method); - target_param_count = method->get_argument_count(); - target_default_arg_count = method->get_default_argument_count(); + MethodBind *method = ClassDB::get_method(target_instance->get_class_name(), t->target_callable.get_method()); + if (method) { + target_param_count = method->get_argument_count(); + target_default_arg_count = method->get_default_argument_count(); + } } if (target_param_count >= 1 && target_default_arg_count < target_param_count) { argc = 1; } } - ::Thread::set_name(t->target_method); + ::Thread::set_name(t->target_callable.get_method()); - t->ret = t->target_instance->call(t->target_method, arg, argc, ce); + t->target_callable.call(arg, argc, t->ret, ce); if (ce.error != Callable::CallError::CALL_OK) { - String reason; - switch (ce.error) { - case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: { - reason = "Invalid Argument #" + itos(ce.argument); - } break; - case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: { - reason = "Too Many Arguments"; - } break; - case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: { - reason = "Too Few Arguments"; - } break; - case Callable::CallError::CALL_ERROR_INVALID_METHOD: { - reason = "Method Not Found"; - } break; - default: { - } - } - - ERR_FAIL_MSG("Could not call function '" + t->target_method.operator String() + "' to start thread " + t->get_id() + ": " + reason + "."); + t->running.clear(); + ERR_FAIL_MSG("Could not call function '" + t->target_callable.get_method().operator String() + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, arg, argc, ce) + "."); } + + t->running.clear(); } -Error Thread::start(Object *p_instance, const StringName &p_method, const Variant &p_userdata, Priority p_priority) { - ERR_FAIL_COND_V_MSG(active.is_set(), ERR_ALREADY_IN_USE, "Thread already started."); - ERR_FAIL_COND_V(!p_instance, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_method == StringName(), ERR_INVALID_PARAMETER); +Error Thread::start(const Callable &p_callable, const Variant &p_userdata, Priority p_priority) { + ERR_FAIL_COND_V_MSG(is_started(), ERR_ALREADY_IN_USE, "Thread already started."); + ERR_FAIL_COND_V(p_callable.is_null(), ERR_INVALID_PARAMETER); ERR_FAIL_INDEX_V(p_priority, PRIORITY_MAX, ERR_INVALID_PARAMETER); ret = Variant(); - target_method = p_method; - target_instance = p_instance; + target_callable = p_callable; userdata = p_userdata; - active.set(); + running.set(); Ref<Thread> *ud = memnew(Ref<Thread>(this)); @@ -1847,26 +1844,29 @@ String Thread::get_id() const { return itos(thread.get_id()); } -bool Thread::is_active() const { - return active.is_set(); +bool Thread::is_started() const { + return thread.is_started(); +} + +bool Thread::is_alive() const { + return running.is_set(); } Variant Thread::wait_to_finish() { - ERR_FAIL_COND_V_MSG(!active.is_set(), Variant(), "Thread must be active to wait for its completion."); + ERR_FAIL_COND_V_MSG(!is_started(), Variant(), "Thread must have been started to wait for its completion."); thread.wait_to_finish(); Variant r = ret; - active.clear(); - target_method = StringName(); - target_instance = nullptr; + target_callable = Callable(); userdata = Variant(); return r; } void Thread::_bind_methods() { - ClassDB::bind_method(D_METHOD("start", "instance", "method", "userdata", "priority"), &Thread::start, DEFVAL(Variant()), DEFVAL(PRIORITY_NORMAL)); + ClassDB::bind_method(D_METHOD("start", "callable", "userdata", "priority"), &Thread::start, DEFVAL(Variant()), DEFVAL(PRIORITY_NORMAL)); ClassDB::bind_method(D_METHOD("get_id"), &Thread::get_id); - ClassDB::bind_method(D_METHOD("is_active"), &Thread::is_active); + ClassDB::bind_method(D_METHOD("is_started"), &Thread::is_started); + ClassDB::bind_method(D_METHOD("is_alive"), &Thread::is_alive); ClassDB::bind_method(D_METHOD("wait_to_finish"), &Thread::wait_to_finish); BIND_ENUM_CONSTANT(PRIORITY_LOW); @@ -2042,6 +2042,42 @@ StringName ClassDB::get_category(const StringName &p_node) const { return ::ClassDB::get_category(p_node); } +bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const { + return ::ClassDB::has_enum(p_class, p_name, p_no_inheritance); +} + +PackedStringArray ClassDB::get_enum_list(const StringName &p_class, bool p_no_inheritance) const { + List<StringName> enums; + ::ClassDB::get_enum_list(p_class, &enums, p_no_inheritance); + + PackedStringArray ret; + ret.resize(enums.size()); + int idx = 0; + for (const StringName &E : enums) { + ret.set(idx++, E); + } + + return ret; +} + +PackedStringArray ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance) const { + List<StringName> constants; + ::ClassDB::get_enum_constants(p_class, p_enum, &constants, p_no_inheritance); + + PackedStringArray ret; + ret.resize(constants.size()); + int idx = 0; + for (const StringName &E : constants) { + ret.set(idx++, E); + } + + return ret; +} + +StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const { + return ::ClassDB::get_integer_constant_enum(p_class, p_name, p_no_inheritance); +} + bool ClassDB::is_class_enabled(StringName p_class) const { return ::ClassDB::is_class_enabled(p_class); } @@ -2072,6 +2108,11 @@ void ClassDB::_bind_methods() { ::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_enum", "class", "name", "no_inheritance"), &ClassDB::has_enum, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_enum_list", "class", "no_inheritance"), &ClassDB::get_enum_list, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_enum_constants", "class", "enum", "no_inheritance"), &ClassDB::get_enum_constants, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_enum", "class", "name", "no_inheritance"), &ClassDB::get_integer_constant_enum, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_category", "class"), &ClassDB::get_category); ::ClassDB::bind_method(D_METHOD("is_class_enabled", "class"), &ClassDB::is_class_enabled); } @@ -2175,7 +2216,7 @@ Object *Engine::get_singleton_object(const StringName &p_name) const { 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)); + ERR_FAIL_COND_MSG(!String(p_name).is_valid_identifier(), "Singleton name is not a valid identifier: " + p_name); ::Engine::Singleton s; s.class_name = p_name; s.name = p_name; @@ -2251,13 +2292,11 @@ void Engine::_bind_methods() { 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"); ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_ticks_per_second"), "set_physics_ticks_per_second", "get_physics_ticks_per_second"); ADD_PROPERTY(PropertyInfo(Variant::INT, "target_fps"), "set_target_fps", "get_target_fps"); @@ -2388,12 +2427,12 @@ Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Arra } EngineDebugger::~EngineDebugger() { - for (Map<StringName, Callable>::Element *E = captures.front(); E; E = E->next()) { - ::EngineDebugger::unregister_message_capture(E->key()); + for (const KeyValue<StringName, Callable> &E : captures) { + ::EngineDebugger::unregister_message_capture(E.key); } captures.clear(); - for (Map<StringName, ProfilerCallable>::Element *E = profilers.front(); E; E = E->next()) { - ::EngineDebugger::unregister_profiler(E->key()); + for (const KeyValue<StringName, ProfilerCallable> &E : profilers) { + ::EngineDebugger::unregister_profiler(E.key); } profilers.clear(); } diff --git a/core/core_bind.h b/core/core_bind.h index a6fac63edd..4eab085dda 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -178,6 +178,7 @@ public: Vector<String> get_cmdline_args(); String get_locale() const; + String get_locale_language() const; String get_model_name() const; @@ -537,9 +538,8 @@ class Thread : public RefCounted { protected: Variant ret; Variant userdata; - SafeFlag active; - Object *target_instance = nullptr; - StringName target_method; + SafeFlag running; + Callable target_callable; ::Thread thread; static void _bind_methods(); static void _start_func(void *ud); @@ -552,9 +552,10 @@ public: PRIORITY_MAX }; - Error start(Object *p_instance, const StringName &p_method, const Variant &p_userdata = Variant(), Priority p_priority = PRIORITY_NORMAL); + Error start(const Callable &p_callable, const Variant &p_userdata = Variant(), Priority p_priority = PRIORITY_NORMAL); String get_id() const; - bool is_active() const; + bool is_started() const; + bool is_alive() const; Variant wait_to_finish(); }; @@ -592,6 +593,11 @@ public: int get_integer_constant(const StringName &p_class, const StringName &p_name) const; StringName get_category(const StringName &p_node) const; + bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; + PackedStringArray get_enum_list(const StringName &p_class, bool p_no_inheritance = false) const; + PackedStringArray get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const; + StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; + bool is_class_enabled(StringName p_class) const; ClassDB() {} diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 4f3f1fd16e..b2d5a8fdf1 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -31,6 +31,7 @@ #include "core_constants.h" #include "core/input/input_event.h" +#include "core/multiplayer/multiplayer.h" #include "core/object/class_db.h" #include "core/os/keyboard.h" #include "core/variant/variant.h" @@ -593,6 +594,7 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFERRED_SET_RESOURCE); BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT); BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR_BASIC_SETTING); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_ARRAY); BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT); BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT_INTL); @@ -609,6 +611,15 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_OBJECT_CORE); BIND_CORE_ENUM_CONSTANT(METHOD_FLAGS_DEFAULT); + // rpc + BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_DISABLED", Multiplayer::RPC_MODE_DISABLED); + BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_ANY_PEER", Multiplayer::RPC_MODE_ANY_PEER); + BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_AUTH", Multiplayer::RPC_MODE_AUTHORITY); + + BIND_CORE_ENUM_CONSTANT_CUSTOM("TRANSFER_MODE_UNRELIABLE", Multiplayer::TRANSFER_MODE_UNRELIABLE); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TRANSFER_MODE_UNRELIABLE_ORDERED", Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TRANSFER_MODE_RELIABLE", Multiplayer::TRANSFER_MODE_RELIABLE); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NIL", Variant::NIL); BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_BOOL", Variant::BOOL); BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_INT", Variant::INT); diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp index a522b1310f..059025aa8f 100644 --- a/core/debugger/engine_debugger.cpp +++ b/core/debugger/engine_debugger.cpp @@ -123,8 +123,8 @@ void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, physics_time = USEC_TO_SEC(p_physics_ticks); physics_frame_time = p_physics_frame_time; // Notify tick to running profilers - for (Map<StringName, Profiler>::Element *E = profilers.front(); E; E = E->next()) { - Profiler &p = E->get(); + for (KeyValue<StringName, Profiler> &E : profilers) { + Profiler &p = E.value; if (!p.active || !p.tick) { continue; } @@ -179,9 +179,9 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, Ve void EngineDebugger::deinitialize() { if (singleton) { // Stop all profilers - for (Map<StringName, Profiler>::Element *E = profilers.front(); E; E = E->next()) { - if (E->get().active) { - singleton->profiler_enable(E->key(), false); + for (const KeyValue<StringName, Profiler> &E : profilers) { + if (E.value.active) { + singleton->profiler_enable(E.key, false); } } diff --git a/core/debugger/engine_debugger.h b/core/debugger/engine_debugger.h index 22c6ef943e..41142bf305 100644 --- a/core/debugger/engine_debugger.h +++ b/core/debugger/engine_debugger.h @@ -128,7 +128,7 @@ public: virtual void poll_events(bool p_is_idle) {} virtual void send_message(const String &p_msg, const Array &p_data) = 0; - virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) = 0; + virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type) = 0; virtual void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false) = 0; virtual ~EngineDebugger(); diff --git a/core/debugger/local_debugger.cpp b/core/debugger/local_debugger.cpp index b0b3f11424..60aa3e6be7 100644 --- a/core/debugger/local_debugger.cpp +++ b/core/debugger/local_debugger.cpp @@ -166,8 +166,8 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } else if (line.begins_with("set")) { if (line.get_slice_count(" ") == 1) { - for (Map<String, String>::Element *E = options.front(); E; E = E->next()) { - print_line("\t" + E->key() + "=" + E->value()); + for (const KeyValue<String, String> &E : options) { + print_line("\t" + E.key + "=" + E.value); } } else { @@ -249,8 +249,8 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } print_line("Breakpoint(s): " + itos(breakpoints.size())); - for (Map<int, Set<StringName>>::Element *E = breakpoints.front(); E; E = E->next()) { - print_line("\t" + String(E->value().front()->get()) + ":" + itos(E->key())); + for (const KeyValue<int, Set<StringName>> &E : breakpoints) { + print_line("\t" + String(E.value.front()->get()) + ":" + itos(E.key)); } } else { @@ -358,7 +358,7 @@ void LocalDebugger::send_message(const String &p_message, const Array &p_args) { // print_line("MESSAGE: '" + p_message + "' - " + String(Variant(p_args))); } -void LocalDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) { +void LocalDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type) { print_line("ERROR: '" + (p_descr.is_empty() ? p_err : p_descr) + "'"); } diff --git a/core/debugger/local_debugger.h b/core/debugger/local_debugger.h index e793b2a859..cb59eb82e9 100644 --- a/core/debugger/local_debugger.h +++ b/core/debugger/local_debugger.h @@ -50,7 +50,7 @@ private: public: void debug(bool p_can_continue, bool p_is_error_breakpoint); void send_message(const String &p_message, const Array &p_args); - void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type); + void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type); LocalDebugger(); ~LocalDebugger(); diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index f865dfe102..9967d1e361 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -179,8 +179,8 @@ public: if (pt - last_profile_time > 100) { last_profile_time = pt; DebuggerMarshalls::NetworkProfilerFrame frame; - for (Map<ObjectID, NodeInfo>::Element *E = multiplayer_node_data.front(); E; E = E->next()) { - frame.infos.push_back(E->get()); + for (const KeyValue<ObjectID, NodeInfo> &E : multiplayer_node_data) { + frame.infos.push_back(E.value); } multiplayer_node_data.clear(); EngineDebugger::get_singleton()->send_message("network:profile_frame", frame.serialize()); @@ -455,7 +455,7 @@ Error RemoteDebugger::_put_msg(String p_message, Array p_data) { return err; } -void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type) { +void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, bool p_editor_notify, ErrorHandlerType p_type) { if (p_type == ERR_HANDLER_SCRIPT) { return; //ignore script errors, those go through debugger } @@ -475,7 +475,7 @@ void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char * } // send_error will lock internally. - rd->script_debugger->send_error(p_func, p_file, p_line, p_err, p_descr, p_type, si); + rd->script_debugger->send_error(p_func, p_file, p_line, p_err, p_descr, p_editor_notify, p_type, si); } void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error) { @@ -605,7 +605,7 @@ void RemoteDebugger::send_message(const String &p_message, const Array &p_args) } } -void RemoteDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) { +void RemoteDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type) { ErrorMessage oe; oe.error = p_err; oe.error_descr = p_descr; diff --git a/core/debugger/remote_debugger.h b/core/debugger/remote_debugger.h index 28e670747e..73799e3f81 100644 --- a/core/debugger/remote_debugger.h +++ b/core/debugger/remote_debugger.h @@ -89,7 +89,7 @@ private: PrintHandlerList phl; static void _print_handler(void *p_this, const String &p_string, bool p_error); ErrorHandlerList eh; - static void _err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type); + static void _err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, bool p_editor_notify, ErrorHandlerType p_type); ErrorMessage _create_overflow_error(const String &p_what, const String &p_descr); Error _put_msg(String p_message, Array p_data); @@ -111,7 +111,7 @@ public: // Overrides void poll_events(bool p_is_idle); void send_message(const String &p_message, const Array &p_args); - void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type); + void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type); void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false); RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer); diff --git a/core/debugger/script_debugger.cpp b/core/debugger/script_debugger.cpp index 6d1e4ed101..70ec101a03 100644 --- a/core/debugger/script_debugger.cpp +++ b/core/debugger/script_debugger.cpp @@ -100,10 +100,10 @@ void ScriptDebugger::debug(ScriptLanguage *p_lang, bool p_can_continue, bool p_i break_lang = prev; } -void ScriptDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info) { +void ScriptDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info) { // Store stack info, this is ugly, but allows us to separate EngineDebugger and ScriptDebugger. There might be a better way. error_stack_info.append_array(p_stack_info); - EngineDebugger::get_singleton()->send_error(p_func, p_file, p_line, p_err, p_descr, p_type); + EngineDebugger::get_singleton()->send_error(p_func, p_file, p_line, p_err, p_descr, p_editor_notify, p_type); error_stack_info.resize(0); } diff --git a/core/debugger/script_debugger.h b/core/debugger/script_debugger.h index 9f034a5e5d..c1d0170334 100644 --- a/core/debugger/script_debugger.h +++ b/core/debugger/script_debugger.h @@ -71,7 +71,7 @@ public: void debug(ScriptLanguage *p_lang, bool p_can_continue = true, bool p_is_error_breakpoint = false); ScriptLanguage *get_break_language() const; - void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info); + void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info); Vector<StackInfo> get_error_stack_info() const; ScriptDebugger() {} }; diff --git a/core/error/error_macros.cpp b/core/error/error_macros.cpp index 272dda97d8..719ea8afb5 100644 --- a/core/error/error_macros.cpp +++ b/core/error/error_macros.cpp @@ -65,45 +65,49 @@ void remove_error_handler(ErrorHandlerList *p_handler) { _global_unlock(); } -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type) { - _err_print_error(p_function, p_file, p_line, p_error, "", p_type); +// Errors without messages. +void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, bool p_editor_notify, ErrorHandlerType p_type) { + _err_print_error(p_function, p_file, p_line, p_error, "", p_editor_notify, p_type); } -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type) { - _err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), "", p_type); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, bool p_editor_notify, ErrorHandlerType p_type) { + _err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), "", p_editor_notify, p_type); } -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type) { +// Main error printing function. +void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) { OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, p_message, (Logger::ErrorType)p_type); _global_lock(); ErrorHandlerList *l = error_handler_list; while (l) { - l->errfunc(l->userdata, p_function, p_file, p_line, p_error, p_message, p_type); + l->errfunc(l->userdata, p_function, p_file, p_line, p_error, p_message, p_editor_notify, p_type); l = l->next; } _global_unlock(); } -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, ErrorHandlerType p_type) { - _err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message, p_type); +// Errors with message. (All combinations of p_error and p_message as String or char*.) +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) { + _err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message, p_editor_notify, p_type); } -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, ErrorHandlerType p_type) { - _err_print_error(p_function, p_file, p_line, p_error, p_message.utf8().get_data(), p_type); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, bool p_editor_notify, ErrorHandlerType p_type) { + _err_print_error(p_function, p_file, p_line, p_error, p_message.utf8().get_data(), p_editor_notify, p_type); } -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, ErrorHandlerType p_type) { - _err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message.utf8().get_data(), p_type); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, bool p_editor_notify, ErrorHandlerType p_type) { + _err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message.utf8().get_data(), p_editor_notify, p_type); } -void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message, bool fatal) { - String fstr(fatal ? "FATAL: " : ""); +// Index errors. (All combinations of p_message as String or char*.) +void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message, bool p_editor_notify, bool p_fatal) { + String fstr(p_fatal ? "FATAL: " : ""); String err(fstr + "Index " + p_index_str + " = " + itos(p_index) + " is out of bounds (" + p_size_str + " = " + itos(p_size) + ")."); _err_print_error(p_function, p_file, p_line, err.utf8().get_data(), p_message); } -void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool fatal) { - _err_print_index_error(p_function, p_file, p_line, p_index, p_size, p_index_str, p_size_str, p_message.utf8().get_data(), fatal); +void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify, bool p_fatal) { + _err_print_index_error(p_function, p_file, p_line, p_index, p_size, p_index_str, p_size_str, p_message.utf8().get_data(), p_fatal); } diff --git a/core/error/error_macros.h b/core/error/error_macros.h index f909a67d55..4eb862dce2 100644 --- a/core/error/error_macros.h +++ b/core/error/error_macros.h @@ -46,7 +46,7 @@ enum ErrorHandlerType { // Pointer to the error handler printing function. Reassign to any function to have errors printed. // Parameters: userdata, function, file, line, error, explanation, type. -typedef void (*ErrorHandlerFunc)(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type); +typedef void (*ErrorHandlerFunc)(void *, const char *, const char *, int p_line, const char *, const char *, bool p_editor_notify, ErrorHandlerType p_type); struct ErrorHandlerList { ErrorHandlerFunc errfunc = nullptr; @@ -61,14 +61,14 @@ void add_error_handler(ErrorHandlerList *p_handler); void remove_error_handler(ErrorHandlerList *p_handler); // Functions used by the error macros. -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool fatal = false); -void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool fatal = false); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool p_editor_notify = false, bool fatal = false); +void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify = false, bool fatal = false); #ifdef __GNUC__ //#define FUNCTION_STR __PRETTY_FUNCTION__ - too annoying @@ -89,13 +89,6 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li #define GENERATE_TRAP() __builtin_trap() #endif -// Used to strip debug messages in release mode -#ifdef DEBUG_ENABLED -#define DEBUG_STR(m_msg) m_msg -#else -#define DEBUG_STR(m_msg) "" -#endif - /** * Error macros. * WARNING: These macros work in the opposite way to assert(). @@ -135,11 +128,22 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * If not, prints `m_msg` and the current function returns. */ -#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ - _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ - return; \ - } else \ +#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ + return; \ + } else \ + ((void)0) + +/** + * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. + * If not, prints `m_msg`, notifies in the editor, and the current function returns. + */ +#define ERR_FAIL_INDEX_EDMSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ + return; \ + } else \ ((void)0) /** @@ -160,11 +164,22 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * If not, prints `m_msg` and the current function returns `m_retval`. */ -#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ - _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ - return m_retval; \ - } else \ +#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ + return m_retval; \ + } else \ + ((void)0) + +/** + * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. + * If not, prints `m_msg`, notifies in the editor, and the current function returns `m_retval`. + */ +#define ERR_FAIL_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ + return m_retval; \ + } else \ ((void)0) /** @@ -189,11 +204,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * If not, prints `m_msg` and the application crashes. */ -#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ - _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg), true); \ - GENERATE_TRAP(); \ - } else \ +#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ + GENERATE_TRAP(); \ + } else \ ((void)0) // Unsigned integer index out of bounds error macros. @@ -216,11 +231,21 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Ensures an unsigned integer index `m_index` is less than `m_size`. * If not, prints `m_msg` and the current function returns. */ -#define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \ - if (unlikely((m_index) >= (m_size))) { \ - _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ - return; \ - } else \ +#define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ + return; \ + } else \ + ((void)0) +/** + * Ensures an unsigned integer index `m_index` is less than `m_size`. + * If not, prints `m_msg`, notifies in the editor, and the current function returns. + */ +#define ERR_FAIL_UNSIGNED_INDEX_EDMSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ + return; \ + } else \ ((void)0) /** @@ -241,11 +266,22 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Ensures an unsigned integer index `m_index` is less than `m_size`. * If not, prints `m_msg` and the current function returns `m_retval`. */ -#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ - if (unlikely((m_index) >= (m_size))) { \ - _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ - return m_retval; \ - } else \ +#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ + if (unlikely((m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ + return m_retval; \ + } else \ + ((void)0) + +/** + * Ensures an unsigned integer index `m_index` is less than `m_size`. + * If not, prints `m_msg`, notifies in the editor, and the current function returns `m_retval`. + */ +#define ERR_FAIL_UNSIGNED_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg) \ + if (unlikely((m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ + return m_retval; \ + } else \ ((void)0) /** @@ -270,11 +306,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Ensures an unsigned integer index `m_index` is less than `m_size`. * If not, prints `m_msg` and the application crashes. */ -#define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \ - if (unlikely((m_index) >= (m_size))) { \ - _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg), true); \ - GENERATE_TRAP(); \ - } else \ +#define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) >= (m_size))) { \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ + GENERATE_TRAP(); \ + } else \ ((void)0) // Null reference error macros. @@ -297,11 +333,22 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Ensures a pointer `m_param` is not null. * If it is null, prints `m_msg` and the current function returns. */ -#define ERR_FAIL_NULL_MSG(m_param, m_msg) \ - if (unlikely(m_param == nullptr)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \ - return; \ - } else \ +#define ERR_FAIL_NULL_MSG(m_param, m_msg) \ + if (unlikely(m_param == nullptr)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \ + return; \ + } else \ + ((void)0) + +/** + * Ensures a pointer `m_param` is not null. + * If it is null, prints `m_msg`, notifies in the editor, and the current function returns. + */ +#define ERR_FAIL_NULL_EDMSG(m_param, m_msg) \ + if (unlikely(m_param == nullptr)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \ + return; \ + } else \ ((void)0) /** @@ -322,11 +369,22 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Ensures a pointer `m_param` is not null. * If it is null, prints `m_msg` and the current function returns `m_retval`. */ -#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \ - if (unlikely(m_param == nullptr)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \ - return m_retval; \ - } else \ +#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \ + if (unlikely(m_param == nullptr)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \ + return m_retval; \ + } else \ + ((void)0) + +/** + * Ensures a pointer `m_param` is not null. + * If it is null, prints `m_msg`, notifies in the editor, and the current function returns `m_retval`. + */ +#define ERR_FAIL_NULL_V_EDMSG(m_param, m_retval, m_msg) \ + if (unlikely(m_param == nullptr)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \ + return m_retval; \ + } else \ ((void)0) /** @@ -352,11 +410,25 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * If checking for null use ERR_FAIL_NULL_MSG instead. * If checking index bounds use ERR_FAIL_INDEX_MSG instead. */ -#define ERR_FAIL_COND_MSG(m_cond, m_msg) \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", DEBUG_STR(m_msg)); \ - return; \ - } else \ +#define ERR_FAIL_COND_MSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg); \ + return; \ + } else \ + ((void)0) + +/** + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg`, notifies in the editor, and the current function returns. + * + * If checking for null use ERR_FAIL_NULL_MSG instead. + * If checking index bounds use ERR_FAIL_INDEX_MSG instead. + */ +#define ERR_FAIL_COND_EDMSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg, true); \ + return; \ + } else \ ((void)0) /** @@ -382,11 +454,25 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * If checking for null use ERR_FAIL_NULL_V_MSG instead. * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead. */ -#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), DEBUG_STR(m_msg)); \ - return m_retval; \ - } else \ +#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg); \ + return m_retval; \ + } else \ + ((void)0) + +/** + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg`, notifies in the editor, and the current function returns `m_retval`. + * + * If checking for null use ERR_FAIL_NULL_V_MSG instead. + * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead. + */ +#define ERR_FAIL_COND_V_EDMSG(m_cond, m_retval, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg, true); \ + return m_retval; \ + } else \ ((void)0) /** @@ -407,11 +493,22 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Ensures `m_cond` is false. * If `m_cond` is true, prints `m_msg` and the current loop continues. */ -#define ERR_CONTINUE_MSG(m_cond, m_msg) \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", DEBUG_STR(m_msg)); \ - continue; \ - } else \ +#define ERR_CONTINUE_MSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg); \ + continue; \ + } else \ + ((void)0) + +/** + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg`, notifies in the editor, and the current loop continues. + */ +#define ERR_CONTINUE_EDMSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg, true); \ + continue; \ + } else \ ((void)0) /** @@ -432,11 +529,22 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Ensures `m_cond` is false. * If `m_cond` is true, prints `m_msg` and the current loop breaks. */ -#define ERR_BREAK_MSG(m_cond, m_msg) \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", DEBUG_STR(m_msg)); \ - break; \ - } else \ +#define ERR_BREAK_MSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg); \ + break; \ + } else \ + ((void)0) + +/** + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg`, notifies in the editor, and the current loop breaks. + */ +#define ERR_BREAK_EDMSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg, true); \ + break; \ + } else \ ((void)0) /** @@ -461,11 +569,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * Ensures `m_cond` is false. * If `m_cond` is true, prints `m_msg` and the application crashes. */ -#define CRASH_COND_MSG(m_cond, m_msg) \ - if (unlikely(m_cond)) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", DEBUG_STR(m_msg)); \ - GENERATE_TRAP(); \ - } else \ +#define CRASH_COND_MSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", m_msg); \ + GENERATE_TRAP(); \ + } else \ ((void)0) // Generic error macros. @@ -490,11 +598,24 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * * Prints `m_msg`, and the current function returns. */ -#define ERR_FAIL_MSG(m_msg) \ - if (true) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", DEBUG_STR(m_msg)); \ - return; \ - } else \ +#define ERR_FAIL_MSG(m_msg) \ + if (true) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg); \ + return; \ + } else \ + ((void)0) + +/** + * Try using `ERR_FAIL_COND_MSG`. + * Only use this macro if more complex error detection or recovery is required. + * + * Prints `m_msg`, notifies in the editor, and the current function returns. + */ +#define ERR_FAIL_EDMSG(m_msg) \ + if (true) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg, true); \ + return; \ + } else \ ((void)0) /** @@ -517,11 +638,24 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * * Prints `m_msg`, and the current function returns `m_retval`. */ -#define ERR_FAIL_V_MSG(m_retval, m_msg) \ - if (true) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), DEBUG_STR(m_msg)); \ - return m_retval; \ - } else \ +#define ERR_FAIL_V_MSG(m_retval, m_msg) \ + if (true) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg); \ + return m_retval; \ + } else \ + ((void)0) + +/** + * Try using `ERR_FAIL_COND_V_MSG`. + * Only use this macro if more complex error detection or recovery is required. + * + * Prints `m_msg`, notifies in the editor, and the current function returns `m_retval`. + */ +#define ERR_FAIL_V_EDMSG(m_retval, m_msg) \ + if (true) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg, true); \ + return m_retval; \ + } else \ ((void)0) /** @@ -535,6 +669,16 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg) /** + * Try using `ERR_FAIL_COND_MSG`, `ERR_FAIL_COND_V_MSG`, `ERR_CONTINUE_MSG` or ERR_BREAK_MSG. + * Only use this macro at the start of a function that has not been implemented yet, or + * if more complex error detection or recovery is required. + * + * Prints `m_msg` and notifies the editor. + */ +#define ERR_PRINT_ED(m_msg) \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, ) + +/** * Prints `m_msg` once during the application lifetime. */ #define ERR_PRINT_ONCE(m_msg) \ @@ -547,6 +691,19 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li } else \ ((void)0) +/** + * Prints `m_msg` and notifies the editor once during the application lifetime. + */ +#define ERR_PRINT_ONCE_ED(m_msg) \ + if (true) { \ + static bool first_print = true; \ + if (first_print) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true); \ + first_print = false; \ + } \ + } else \ + ((void)0) + // Print warning message macros. /** @@ -555,21 +712,44 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. */ #define WARN_PRINT(m_msg) \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, ERR_HANDLER_WARNING) + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, false, ERR_HANDLER_WARNING) + +/** + * Prints `m_msg` and notifies the editor. + * + * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. + */ +#define WARN_PRINT_ED(m_msg) \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true, ERR_HANDLER_WARNING) /** * Prints `m_msg` once during the application lifetime. * * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. */ -#define WARN_PRINT_ONCE(m_msg) \ - if (true) { \ - static bool first_print = true; \ - if (first_print) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, ERR_HANDLER_WARNING); \ - first_print = false; \ - } \ - } else \ +#define WARN_PRINT_ONCE(m_msg) \ + if (true) { \ + static bool first_print = true; \ + if (first_print) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, false, ERR_HANDLER_WARNING); \ + first_print = false; \ + } \ + } else \ + ((void)0) + +/** + * Prints `m_msg` and notifies the editor once during the application lifetime. + * + * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. + */ +#define WARN_PRINT_ONCE_ED(m_msg) \ + if (true) { \ + static bool first_print = true; \ + if (first_print) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true, ERR_HANDLER_WARNING); \ + first_print = false; \ + } \ + } else \ ((void)0) // Print deprecated warning message macros. @@ -577,27 +757,27 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li /** * Warns that the current function is deprecated. */ -#define WARN_DEPRECATED \ - if (true) { \ - static SafeFlag warning_shown; \ - if (!warning_shown.is_set()) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", ERR_HANDLER_WARNING); \ - warning_shown.set(); \ - } \ - } else \ +#define WARN_DEPRECATED \ + if (true) { \ + static SafeFlag warning_shown; \ + if (!warning_shown.is_set()) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", false, ERR_HANDLER_WARNING); \ + warning_shown.set(); \ + } \ + } else \ ((void)0) /** * Warns that the current function is deprecated and prints `m_msg`. */ -#define WARN_DEPRECATED_MSG(m_msg) \ - if (true) { \ - static SafeFlag warning_shown; \ - if (!warning_shown.is_set()) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", DEBUG_STR(m_msg), ERR_HANDLER_WARNING); \ - warning_shown.set(); \ - } \ - } else \ +#define WARN_DEPRECATED_MSG(m_msg) \ + if (true) { \ + static SafeFlag warning_shown; \ + if (!warning_shown.is_set()) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", m_msg, false, ERR_HANDLER_WARNING); \ + warning_shown.set(); \ + } \ + } else \ ((void)0) /** @@ -618,11 +798,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li * * Prints `m_msg`, and then the application crashes. */ -#define CRASH_NOW_MSG(m_msg) \ - if (true) { \ - _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", DEBUG_STR(m_msg)); \ - GENERATE_TRAP(); \ - } else \ +#define CRASH_NOW_MSG(m_msg) \ + if (true) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", m_msg); \ + GENERATE_TRAP(); \ + } else \ ((void)0) #endif // ERROR_MACROS_H diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index a8547a0090..03b2426370 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -353,11 +353,11 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { api_dump["global_constants"] = constants; Array enums; - for (Map<String, List<Pair<String, int>>>::Element *E = enum_list.front(); E; E = E->next()) { + for (const KeyValue<String, List<Pair<String, int>>> &E : enum_list) { Dictionary d1; - d1["name"] = E->key(); + d1["name"] = E.key; Array values; - for (const Pair<String, int> &F : E->get()) { + for (const Pair<String, int> &F : E.value) { Dictionary d2; d2["name"] = F.first; d2["value"] = F.second; @@ -841,6 +841,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { { Array native_structures; + // AudioStream structures { Dictionary d; d["name"] = "AudioFrame"; @@ -849,6 +850,22 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { native_structures.push_back(d); } + // TextServer structures + { + Dictionary d; + d["name"] = "Glyph"; + d["format"] = "int start,int end,uint8_t count,uint8_t repeat,uint16_t flags,float x_off,float y_off,float advance,RID font_rid,int font_size,int32_t index"; + + native_structures.push_back(d); + } + { + Dictionary d; + d["name"] = "CaretInfo"; + d["format"] = "Rect2 leading_caret,Rect2 trailing_caret,TextServer::Direction leading_direction,TextServer::Direction trailing_direction"; + + native_structures.push_back(d); + } + api_dump["native_structures"] = native_structures; } diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp index de107b4156..4770c9c65f 100644 --- a/core/extension/gdnative_interface.cpp +++ b/core/extension/gdnative_interface.cpp @@ -51,13 +51,13 @@ static void gdnative_free(void *p_mem) { // Helper print functions. static void gdnative_print_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) { - _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_ERROR); + _err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_ERROR); } static void gdnative_print_warning(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) { - _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_WARNING); + _err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_WARNING); } static void gdnative_print_script_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) { - _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_SCRIPT); + _err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_SCRIPT); } // Variant functions @@ -661,6 +661,128 @@ static const char32_t *gdnative_string_operator_index_const(const GDNativeString return &self->ptr()[p_index]; } +/* Packed array functions */ + +static uint8_t *gdnative_packed_byte_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { + PackedByteArray *self = (PackedByteArray *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return &self->ptrw()[p_index]; +} + +static const uint8_t *gdnative_packed_byte_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { + const PackedByteArray *self = (const PackedByteArray *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return &self->ptr()[p_index]; +} + +static GDNativeTypePtr gdnative_packed_color_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { + PackedColorArray *self = (PackedColorArray *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return (GDNativeTypePtr)&self->ptrw()[p_index]; +} + +static GDNativeTypePtr gdnative_packed_color_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { + const PackedColorArray *self = (const PackedColorArray *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return (GDNativeTypePtr)&self->ptr()[p_index]; +} + +static float *gdnative_packed_float32_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { + PackedFloat32Array *self = (PackedFloat32Array *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return &self->ptrw()[p_index]; +} + +static const float *gdnative_packed_float32_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { + const PackedFloat32Array *self = (const PackedFloat32Array *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return &self->ptr()[p_index]; +} + +static double *gdnative_packed_float64_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { + PackedFloat64Array *self = (PackedFloat64Array *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return &self->ptrw()[p_index]; +} + +static const double *gdnative_packed_float64_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { + const PackedFloat64Array *self = (const PackedFloat64Array *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return &self->ptr()[p_index]; +} + +static int32_t *gdnative_packed_int32_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { + PackedInt32Array *self = (PackedInt32Array *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return &self->ptrw()[p_index]; +} + +static const int32_t *gdnative_packed_int32_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { + const PackedInt32Array *self = (const PackedInt32Array *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return &self->ptr()[p_index]; +} + +static int64_t *gdnative_packed_int64_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { + PackedInt64Array *self = (PackedInt64Array *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return &self->ptrw()[p_index]; +} + +static const int64_t *gdnative_packed_int64_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { + const PackedInt64Array *self = (const PackedInt64Array *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return &self->ptr()[p_index]; +} + +static GDNativeStringPtr gdnative_packed_string_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { + PackedStringArray *self = (PackedStringArray *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return (GDNativeStringPtr)&self->ptrw()[p_index]; +} + +static GDNativeStringPtr gdnative_packed_string_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { + const PackedStringArray *self = (const PackedStringArray *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return (GDNativeStringPtr)&self->ptr()[p_index]; +} + +static GDNativeTypePtr gdnative_packed_vector2_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { + PackedVector2Array *self = (PackedVector2Array *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return (GDNativeTypePtr)&self->ptrw()[p_index]; +} + +static GDNativeTypePtr gdnative_packed_vector2_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { + const PackedVector2Array *self = (const PackedVector2Array *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return (GDNativeTypePtr)&self->ptr()[p_index]; +} + +static GDNativeTypePtr gdnative_packed_vector3_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { + PackedVector3Array *self = (PackedVector3Array *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return (GDNativeTypePtr)&self->ptrw()[p_index]; +} + +static GDNativeTypePtr gdnative_packed_vector3_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { + const PackedVector3Array *self = (const PackedVector3Array *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return (GDNativeTypePtr)&self->ptr()[p_index]; +} + +static GDNativeVariantPtr gdnative_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { + Array *self = (Array *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return (GDNativeTypePtr)&self[p_index]; +} + +static GDNativeVariantPtr gdnative_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { + const Array *self = (const Array *)p_self; + ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); + return (GDNativeTypePtr)&self[p_index]; +} + /* OBJECT API */ static void gdnative_object_method_bind_call(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error) { @@ -732,14 +854,21 @@ static GDNativeMethodBindPtr gdnative_classdb_get_method_bind(const char *p_clas return (GDNativeMethodBindPtr)mb; } -static GDNativeClassConstructor gdnative_classdb_get_constructor(const char *p_classname) { +static GDNativeClassConstructor gdnative_classdb_get_constructor(const char *p_classname, GDNativeExtensionPtr *r_extension) { ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(StringName(p_classname)); if (class_info) { + if (r_extension) { + *r_extension = class_info->native_extension; + } return (GDNativeClassConstructor)class_info->creation_func; } return nullptr; } +static GDNativeObjectPtr gdnative_classdb_construct_object(GDNativeClassConstructor p_constructor, GDNativeExtensionPtr p_extension) { + return (GDNativeObjectPtr)ClassDB::construct_object((Object * (*)()) p_constructor, (ObjectNativeExtension *)p_extension); +} + static void *gdnative_classdb_get_class_tag(const char *p_classname) { ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(p_classname); return class_info ? class_info->class_ptr : nullptr; @@ -843,6 +972,35 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) { gdni.string_operator_index = gdnative_string_operator_index; gdni.string_operator_index_const = gdnative_string_operator_index_const; + /* Packed array functions */ + + gdni.packed_byte_array_operator_index = gdnative_packed_byte_array_operator_index; + gdni.packed_byte_array_operator_index_const = gdnative_packed_byte_array_operator_index_const; + + gdni.packed_color_array_operator_index = gdnative_packed_color_array_operator_index; + gdni.packed_color_array_operator_index_const = gdnative_packed_color_array_operator_index_const; + + gdni.packed_float32_array_operator_index = gdnative_packed_float32_array_operator_index; + gdni.packed_float32_array_operator_index_const = gdnative_packed_float32_array_operator_index_const; + gdni.packed_float64_array_operator_index = gdnative_packed_float64_array_operator_index; + gdni.packed_float64_array_operator_index_const = gdnative_packed_float64_array_operator_index_const; + + gdni.packed_int32_array_operator_index = gdnative_packed_int32_array_operator_index; + gdni.packed_int32_array_operator_index_const = gdnative_packed_int32_array_operator_index_const; + gdni.packed_int64_array_operator_index = gdnative_packed_int64_array_operator_index; + gdni.packed_int64_array_operator_index_const = gdnative_packed_int64_array_operator_index_const; + + gdni.packed_string_array_operator_index = gdnative_packed_string_array_operator_index; + gdni.packed_string_array_operator_index_const = gdnative_packed_string_array_operator_index_const; + + gdni.packed_vector2_array_operator_index = gdnative_packed_vector2_array_operator_index; + gdni.packed_vector2_array_operator_index_const = gdnative_packed_vector2_array_operator_index_const; + gdni.packed_vector3_array_operator_index = gdnative_packed_vector3_array_operator_index; + gdni.packed_vector3_array_operator_index_const = gdnative_packed_vector3_array_operator_index_const; + + gdni.array_operator_index = gdnative_array_operator_index; + gdni.array_operator_index_const = gdnative_array_operator_index_const; + /* OBJECT */ gdni.object_method_bind_call = gdnative_object_method_bind_call; @@ -859,6 +1017,7 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) { /* CLASSDB */ gdni.classdb_get_constructor = gdnative_classdb_get_constructor; + gdni.classdb_construct_object = gdnative_classdb_construct_object; gdni.classdb_get_method_bind = gdnative_classdb_get_method_bind; gdni.classdb_get_class_tag = gdnative_classdb_get_class_tag; @@ -869,6 +1028,8 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) { gdni.classdb_register_extension_class_method = nullptr; gdni.classdb_register_extension_class_integer_constant = nullptr; gdni.classdb_register_extension_class_property = nullptr; + gdni.classdb_register_extension_class_property_group = nullptr; + gdni.classdb_register_extension_class_property_subgroup = nullptr; gdni.classdb_register_extension_class_signal = nullptr; gdni.classdb_unregister_extension_class = nullptr; } diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h index 3a5b04429c..8f8cb5a3e0 100644 --- a/core/extension/gdnative_interface.h +++ b/core/extension/gdnative_interface.h @@ -137,6 +137,7 @@ typedef void *GDNativeStringNamePtr; typedef void *GDNativeStringPtr; typedef void *GDNativeObjectPtr; typedef void *GDNativeTypePtr; +typedef void *GDNativeExtensionPtr; typedef void *GDNativeMethodBindPtr; typedef int64_t GDNativeInt; typedef uint8_t GDNativeBool; @@ -181,11 +182,11 @@ typedef void *(*GDNativeInstanceBindingCreateCallback)(void *p_token, void *p_in typedef void (*GDNativeInstanceBindingFreeCallback)(void *p_token, void *p_instance, void *p_binding); typedef GDNativeBool (*GDNativeInstanceBindingReferenceCallback)(void *p_token, void *p_binding, GDNativeBool p_reference); -struct GDNativeInstanceBindingCallbacks { +typedef struct { GDNativeInstanceBindingCreateCallback create_callback; GDNativeInstanceBindingFreeCallback free_callback; GDNativeInstanceBindingReferenceCallback reference_callback; -}; +} GDNativeInstanceBindingCallbacks; /* EXTENSION CLASSES */ @@ -387,6 +388,35 @@ typedef struct { char32_t *(*string_operator_index)(GDNativeStringPtr p_self, GDNativeInt p_index); const char32_t *(*string_operator_index_const)(const GDNativeStringPtr p_self, GDNativeInt p_index); + /* Packed array functions */ + + uint8_t *(*packed_byte_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedByteArray + const uint8_t *(*packed_byte_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedByteArray + + GDNativeTypePtr (*packed_color_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedColorArray, returns Color ptr + GDNativeTypePtr (*packed_color_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedColorArray, returns Color ptr + + float *(*packed_float32_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat32Array + const float *(*packed_float32_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat32Array + double *(*packed_float64_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat64Array + const double *(*packed_float64_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat64Array + + int32_t *(*packed_int32_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array + const int32_t *(*packed_int32_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array + int64_t *(*packed_int64_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array + const int64_t *(*packed_int64_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array + + GDNativeStringPtr (*packed_string_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedStringArray + GDNativeStringPtr (*packed_string_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedStringArray + + GDNativeTypePtr (*packed_vector2_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr + GDNativeTypePtr (*packed_vector2_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr + GDNativeTypePtr (*packed_vector3_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr + GDNativeTypePtr (*packed_vector3_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr + + GDNativeVariantPtr (*array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be an Array ptr + GDNativeVariantPtr (*array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be an Array ptr + /* OBJECT */ void (*object_method_bind_call)(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_ret, GDNativeCallError *r_error); @@ -402,7 +432,8 @@ typedef struct { /* CLASSDB */ - GDNativeClassConstructor (*classdb_get_constructor)(const char *p_classname); + GDNativeClassConstructor (*classdb_get_constructor)(const char *p_classname, GDNativeExtensionPtr *r_extension); + GDNativeObjectPtr (*classdb_construct_object)(GDNativeClassConstructor p_constructor, GDNativeExtensionPtr p_extension); GDNativeMethodBindPtr (*classdb_get_method_bind)(const char *p_classname, const char *p_methodname, GDNativeInt p_hash); void *(*classdb_get_class_tag)(const char *p_classname); @@ -412,6 +443,8 @@ typedef struct { void (*classdb_register_extension_class_method)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info); void (*classdb_register_extension_class_integer_constant)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_enum_name, const char *p_constant_name, GDNativeInt p_constant_value); void (*classdb_register_extension_class_property)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter); + void (*classdb_register_extension_class_property_group)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_group_name, const char *p_prefix); + void (*classdb_register_extension_class_property_subgroup)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_subgroup_name, const char *p_prefix); void (*classdb_register_extension_class_signal)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count); void (*classdb_unregister_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */ } GDNativeInterface; @@ -423,6 +456,8 @@ typedef enum { GDNATIVE_INITIALIZATION_SERVERS, GDNATIVE_INITIALIZATION_SCENE, GDNATIVE_INITIALIZATION_EDITOR, + GDNATIVE_INITIALIZATION_DRIVER, + GDNATIVE_MAX_INITIALIZATION_LEVEL, } GDNativeInitializationLevel; typedef struct { diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp index a3cd7ca14c..a6b0a708c3 100644 --- a/core/extension/native_extension.cpp +++ b/core/extension/native_extension.cpp @@ -35,7 +35,9 @@ #include "core/object/method_bind.h" #include "core/os/os.h" -const char *NativeExtension::EXTENSION_LIST_CONFIG_FILE = "res://.godot/extension_list.cfg"; +String NativeExtension::get_extension_list_config_file() { + return ProjectSettings::get_singleton()->get_project_data_path().plus_file("extension_list.cfg"); +} class NativeExtensionMethodBind : public MethodBind { GDNativeExtensionClassMethodCall call_func; @@ -184,6 +186,7 @@ void NativeExtension::_register_extension_class_integer_constant(const GDNativeE ClassDB::bind_integer_constant(class_name, p_enum_name, p_constant_name, p_constant_value); } + void NativeExtension::_register_extension_class_property(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter) { NativeExtension *self = (NativeExtension *)p_library; @@ -202,6 +205,24 @@ void NativeExtension::_register_extension_class_property(const GDNativeExtension ClassDB::add_property(class_name, pinfo, p_setter, p_getter); } +void NativeExtension::_register_extension_class_property_group(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_group_name, const char *p_prefix) { + NativeExtension *self = (NativeExtension *)p_library; + + StringName class_name = p_class_name; + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property group '" + String(p_group_name) + "' for unexisting class '" + class_name + "'."); + + ClassDB::add_property_group(class_name, p_group_name, p_prefix); +} + +void NativeExtension::_register_extension_class_property_subgroup(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_subgroup_name, const char *p_prefix) { + NativeExtension *self = (NativeExtension *)p_library; + + StringName class_name = p_class_name; + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property subgroup '" + String(p_subgroup_name) + "' for unexisting class '" + class_name + "'."); + + ClassDB::add_property_subgroup(class_name, p_subgroup_name, p_prefix); +} + void NativeExtension::_register_extension_class_signal(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count) { NativeExtension *self = (NativeExtension *)p_library; @@ -325,6 +346,8 @@ void NativeExtension::initialize_native_extensions() { gdnative_interface.classdb_register_extension_class_method = _register_extension_class_method; gdnative_interface.classdb_register_extension_class_integer_constant = _register_extension_class_integer_constant; gdnative_interface.classdb_register_extension_class_property = _register_extension_class_property; + gdnative_interface.classdb_register_extension_class_property_group = _register_extension_class_property_group; + gdnative_interface.classdb_register_extension_class_property_subgroup = _register_extension_class_property_subgroup; gdnative_interface.classdb_register_extension_class_signal = _register_extension_class_signal; gdnative_interface.classdb_unregister_extension_class = _unregister_extension_class; } diff --git a/core/extension/native_extension.h b/core/extension/native_extension.h index b661381d64..f7f235d8fc 100644 --- a/core/extension/native_extension.h +++ b/core/extension/native_extension.h @@ -50,6 +50,8 @@ class NativeExtension : public Resource { static void _register_extension_class_method(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info); static void _register_extension_class_integer_constant(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_enum_name, const char *p_constant_name, GDNativeInt p_constant_value); static void _register_extension_class_property(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter); + static void _register_extension_class_property_group(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_group_name, const char *p_prefix); + static void _register_extension_class_property_subgroup(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_subgroup_name, const char *p_prefix); static void _register_extension_class_signal(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count); static void _unregister_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name); @@ -60,7 +62,7 @@ protected: static void _bind_methods(); public: - static const char *EXTENSION_LIST_CONFIG_FILE; + static String get_extension_list_config_file(); Error open_library(const String &p_path, const String &p_entry_symbol); void close_library(); @@ -70,6 +72,7 @@ public: INITIALIZATION_LEVEL_SERVERS, INITIALIZATION_LEVEL_SCENE, INITIALIZATION_LEVEL_EDITOR, + INITIALIZATION_LEVEL_DRIVER, }; bool is_library_open() const; diff --git a/core/extension/native_extension_manager.cpp b/core/extension/native_extension_manager.cpp index 8b7a9df4f1..c8755250d5 100644 --- a/core/extension/native_extension_manager.cpp +++ b/core/extension/native_extension_manager.cpp @@ -84,8 +84,8 @@ bool NativeExtensionManager::is_extension_loaded(const String &p_path) const { Vector<String> NativeExtensionManager::get_loaded_extensions() const { Vector<String> ret; - for (const Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) { - ret.push_back(E->key()); + for (const KeyValue<String, Ref<NativeExtension>> &E : native_extension_map) { + ret.push_back(E.key); } return ret; } @@ -97,22 +97,22 @@ Ref<NativeExtension> NativeExtensionManager::get_extension(const String &p_path) void NativeExtensionManager::initialize_extensions(NativeExtension::InitializationLevel p_level) { ERR_FAIL_COND(int32_t(p_level) - 1 != level); - for (Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) { - E->get()->initialize_library(p_level); + for (KeyValue<String, Ref<NativeExtension>> &E : native_extension_map) { + E.value->initialize_library(p_level); } level = p_level; } void NativeExtensionManager::deinitialize_extensions(NativeExtension::InitializationLevel p_level) { ERR_FAIL_COND(int32_t(p_level) != level); - for (Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) { - E->get()->deinitialize_library(p_level); + for (KeyValue<String, Ref<NativeExtension>> &E : native_extension_map) { + E.value->deinitialize_library(p_level); } level = int32_t(p_level) - 1; } void NativeExtensionManager::load_extensions() { - FileAccessRef f = FileAccess::open(NativeExtension::EXTENSION_LIST_CONFIG_FILE, FileAccess::READ); + FileAccessRef f = FileAccess::open(NativeExtension::get_extension_list_config_file(), FileAccess::READ); while (f && !f->eof_reached()) { String s = f->get_line().strip_edges(); if (s != String()) { diff --git a/core/input/SCsub b/core/input/SCsub index 740398b266..b12bf561de 100644 --- a/core/input/SCsub +++ b/core/input/SCsub @@ -7,19 +7,15 @@ import input_builders # Order matters here. Higher index controller database files write on top of lower index database files. controller_databases = [ - "#core/input/gamecontrollerdb.txt", - "#core/input/godotcontrollerdb.txt", + "gamecontrollerdb.txt", + "godotcontrollerdb.txt", ] -env.Depends("#core/input/default_controller_mappings.gen.cpp", controller_databases) -env.CommandNoCache( - "#core/input/default_controller_mappings.gen.cpp", +gensource = env.CommandNoCache( + "default_controller_mappings.gen.cpp", controller_databases, env.Run(input_builders.make_default_controller_mappings, "Generating default controller mappings."), ) env.add_source_files(env.core_sources, "*.cpp") - -# Don't warn about duplicate entry here, we need it registered manually for first build, -# even if later builds will pick it up twice due to above *.cpp globbing. -env.add_source_files(env.core_sources, "#core/input/default_controller_mappings.gen.cpp", warn_duplicates=False) +env.add_source_files(env.core_sources, gensource) diff --git a/core/input/input.cpp b/core/input/input.cpp index 9195f7d8b5..296aa1f071 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -35,10 +35,6 @@ #include "core/input/input_map.h" #include "core/os/os.h" -#ifdef TOOLS_ENABLED -#include "editor/editor_settings.h" -#endif - static const char *_joy_buttons[JOY_BUTTON_SDL_MAX] = { "a", "b", @@ -121,6 +117,10 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer); ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer); ClassDB::bind_method(D_METHOD("get_gyroscope"), &Input::get_gyroscope); + ClassDB::bind_method(D_METHOD("set_gravity", "value"), &Input::set_gravity); + ClassDB::bind_method(D_METHOD("set_accelerometer", "value"), &Input::set_accelerometer); + ClassDB::bind_method(D_METHOD("set_magnetometer", "value"), &Input::set_magnetometer); + ClassDB::bind_method(D_METHOD("set_gyroscope", "value"), &Input::set_gyroscope); ClassDB::bind_method(D_METHOD("get_last_mouse_speed"), &Input::get_last_mouse_speed); ClassDB::bind_method(D_METHOD("get_mouse_button_mask"), &Input::get_mouse_button_mask); ClassDB::bind_method(D_METHOD("set_mouse_mode", "mode"), &Input::set_mouse_mode); @@ -162,9 +162,6 @@ void Input::_bind_methods() { } void Input::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { -#ifdef TOOLS_ENABLED - const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; - String pf = p_function; if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || pf == "is_action_just_pressed" || pf == "is_action_just_released" || @@ -179,10 +176,9 @@ void Input::get_argument_options(const StringName &p_function, int p_idx, List<S } String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length()); - r_options->push_back(name.quote(quote_style)); + r_options->push_back(name.quote()); } } -#endif } void Input::SpeedTrack::update(const Vector2 &p_delta_p) { @@ -863,9 +859,9 @@ void Input::release_pressed_events() { joy_buttons_pressed.clear(); _joy_axis.clear(); - for (Map<StringName, Input::Action>::Element *E = action_state.front(); E; E = E->next()) { - if (E->get().pressed) { - action_release(E->key()); + for (const KeyValue<StringName, Input::Action> &E : action_state) { + if (E.value.pressed) { + action_release(E.key); } } } @@ -1322,8 +1318,8 @@ void Input::add_joy_mapping(String p_mapping, bool p_update_existing) { if (p_update_existing) { Vector<String> entry = p_mapping.split(","); String uid = entry[0]; - for (Map<int, Joypad>::Element *E = joy_names.front(); E; E = E->next()) { - Joypad &joy = E->get(); + for (KeyValue<int, Joypad> &E : joy_names) { + Joypad &joy = E.value; if (joy.uid == uid) { joy.mapping = map_db.size() - 1; } @@ -1337,8 +1333,8 @@ void Input::remove_joy_mapping(String p_guid) { map_db.remove(i); } } - for (Map<int, Joypad>::Element *E = joy_names.front(); E; E = E->next()) { - Joypad &joy = E->get(); + for (KeyValue<int, Joypad> &E : joy_names) { + Joypad &joy = E.value; if (joy.uid == p_guid) { joy.mapping = -1; } diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index 50b2099236..c6448b1e44 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -452,8 +452,13 @@ bool InputEventKey::is_match(const Ref<InputEvent> &p_event, bool p_exact_match) return false; } - return keycode == key->keycode && - (!p_exact_match || get_modifiers_mask() == key->get_modifiers_mask()); + if (keycode == 0) { + return physical_keycode == key->physical_keycode && + (!p_exact_match || get_modifiers_mask() == key->get_modifiers_mask()); + } else { + return keycode == key->keycode && + (!p_exact_match || get_modifiers_mask() == key->get_modifiers_mask()); + } } void InputEventKey::_bind_methods() { @@ -864,6 +869,8 @@ void InputEventMouseMotion::_bind_methods() { /////////////////////////////////// void InputEventJoypadMotion::set_axis(JoyAxis p_axis) { + ERR_FAIL_INDEX(p_axis, JOY_AXIS_MAX); + axis = p_axis; emit_changed(); } @@ -1557,9 +1564,13 @@ bool InputEventShortcut::is_pressed() const { } String InputEventShortcut::as_text() const { + ERR_FAIL_COND_V(shortcut.is_null(), "None"); + return vformat(RTR("Input Event with Shortcut=%s"), shortcut->get_as_text()); } String InputEventShortcut::to_string() { + ERR_FAIL_COND_V(shortcut.is_null(), "None"); + return vformat("InputEventShortcut: shortcut=%s", shortcut->get_as_text()); } diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index c7ca65f61a..1ec4299093 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "core/input/input.h" #include "core/os/keyboard.h" +#include "core/os/os.h" InputMap *InputMap::singleton = nullptr; @@ -317,36 +318,36 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = { { "ui_text_dedent", TTRC("Dedent") }, { "ui_text_backspace", TTRC("Backspace") }, { "ui_text_backspace_word", TTRC("Backspace Word") }, - { "ui_text_backspace_word.OSX", TTRC("Backspace Word") }, + { "ui_text_backspace_word.macos", TTRC("Backspace Word") }, { "ui_text_backspace_all_to_left", TTRC("Backspace all to Left") }, - { "ui_text_backspace_all_to_left.OSX", TTRC("Backspace all to Left") }, + { "ui_text_backspace_all_to_left.macos", TTRC("Backspace all to Left") }, { "ui_text_delete", TTRC("Delete") }, { "ui_text_delete_word", TTRC("Delete Word") }, - { "ui_text_delete_word.OSX", TTRC("Delete Word") }, + { "ui_text_delete_word.macos", TTRC("Delete Word") }, { "ui_text_delete_all_to_right", TTRC("Delete all to Right") }, - { "ui_text_delete_all_to_right.OSX", TTRC("Delete all to Right") }, + { "ui_text_delete_all_to_right.macos", TTRC("Delete all to Right") }, { "ui_text_caret_left", TTRC("Caret Left") }, { "ui_text_caret_word_left", TTRC("Caret Word Left") }, - { "ui_text_caret_word_left.OSX", TTRC("Caret Word Left") }, + { "ui_text_caret_word_left.macos", TTRC("Caret Word Left") }, { "ui_text_caret_right", TTRC("Caret Right") }, { "ui_text_caret_word_right", TTRC("Caret Word Right") }, - { "ui_text_caret_word_right.OSX", TTRC("Caret Word Right") }, + { "ui_text_caret_word_right.macos", TTRC("Caret Word Right") }, { "ui_text_caret_up", TTRC("Caret Up") }, { "ui_text_caret_down", TTRC("Caret Down") }, { "ui_text_caret_line_start", TTRC("Caret Line Start") }, - { "ui_text_caret_line_start.OSX", TTRC("Caret Line Start") }, + { "ui_text_caret_line_start.macos", TTRC("Caret Line Start") }, { "ui_text_caret_line_end", TTRC("Caret Line End") }, - { "ui_text_caret_line_end.OSX", TTRC("Caret Line End") }, + { "ui_text_caret_line_end.macos", TTRC("Caret Line End") }, { "ui_text_caret_page_up", TTRC("Caret Page Up") }, { "ui_text_caret_page_down", TTRC("Caret Page Down") }, { "ui_text_caret_document_start", TTRC("Caret Document Start") }, - { "ui_text_caret_document_start.OSX", TTRC("Caret Document Start") }, + { "ui_text_caret_document_start.macos", TTRC("Caret Document Start") }, { "ui_text_caret_document_end", TTRC("Caret Document End") }, - { "ui_text_caret_document_end.OSX", TTRC("Caret Document End") }, + { "ui_text_caret_document_end.macos", TTRC("Caret Document End") }, { "ui_text_scroll_up", TTRC("Scroll Up") }, - { "ui_text_scroll_up.OSX", TTRC("Scroll Up") }, + { "ui_text_scroll_up.macos", TTRC("Scroll Up") }, { "ui_text_scroll_down", TTRC("Scroll Down") }, - { "ui_text_scroll_down.OSX", TTRC("Scroll Down") }, + { "ui_text_scroll_down.macos", TTRC("Scroll Down") }, { "ui_text_select_all", TTRC("Select All") }, { "ui_text_select_word_under_caret", TTRC("Select Word Under Caret") }, { "ui_text_toggle_insert_mode", TTRC("Toggle Insert Mode") }, @@ -466,7 +467,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { // ///// UI Text Input Shortcuts ///// inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(KEY_SPACE | KEY_MASK_CMD)); + inputs.push_back(InputEventKey::create_reference(KEY_SPACE | KEY_MASK_CTRL)); default_builtin_cache.insert("ui_text_completion_query", inputs); inputs = List<Ref<InputEvent>>(); @@ -516,14 +517,14 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_ALT)); - default_builtin_cache.insert("ui_text_backspace_word.OSX", inputs); + default_builtin_cache.insert("ui_text_backspace_word.macos", inputs); inputs = List<Ref<InputEvent>>(); default_builtin_cache.insert("ui_text_backspace_all_to_left", inputs); inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_CMD)); - default_builtin_cache.insert("ui_text_backspace_all_to_left.OSX", inputs); + default_builtin_cache.insert("ui_text_backspace_all_to_left.macos", inputs); inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_DELETE)); @@ -535,14 +536,14 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_DELETE | KEY_MASK_ALT)); - default_builtin_cache.insert("ui_text_delete_word.OSX", inputs); + default_builtin_cache.insert("ui_text_delete_word.macos", inputs); inputs = List<Ref<InputEvent>>(); default_builtin_cache.insert("ui_text_delete_all_to_right", inputs); inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_DELETE | KEY_MASK_CMD)); - default_builtin_cache.insert("ui_text_delete_all_to_right.OSX", inputs); + default_builtin_cache.insert("ui_text_delete_all_to_right.macos", inputs); // Text Caret Movement Left/Right @@ -556,7 +557,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_LEFT | KEY_MASK_ALT)); - default_builtin_cache.insert("ui_text_caret_word_left.OSX", inputs); + default_builtin_cache.insert("ui_text_caret_word_left.macos", inputs); inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_RIGHT)); @@ -568,7 +569,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_RIGHT | KEY_MASK_ALT)); - default_builtin_cache.insert("ui_text_caret_word_right.OSX", inputs); + default_builtin_cache.insert("ui_text_caret_word_right.macos", inputs); // Text Caret Movement Up/Down @@ -589,7 +590,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_A | KEY_MASK_CTRL)); inputs.push_back(InputEventKey::create_reference(KEY_LEFT | KEY_MASK_CMD)); - default_builtin_cache.insert("ui_text_caret_line_start.OSX", inputs); + default_builtin_cache.insert("ui_text_caret_line_start.macos", inputs); inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_END)); @@ -598,7 +599,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_E | KEY_MASK_CTRL)); inputs.push_back(InputEventKey::create_reference(KEY_RIGHT | KEY_MASK_CMD)); - default_builtin_cache.insert("ui_text_caret_line_end.OSX", inputs); + default_builtin_cache.insert("ui_text_caret_line_end.macos", inputs); // Text Caret Movement Page Up/Down @@ -618,7 +619,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_UP | KEY_MASK_CMD)); - default_builtin_cache.insert("ui_text_caret_document_start.OSX", inputs); + default_builtin_cache.insert("ui_text_caret_document_start.macos", inputs); inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_END | KEY_MASK_CMD)); @@ -626,7 +627,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_DOWN | KEY_MASK_CMD)); - default_builtin_cache.insert("ui_text_caret_document_end.OSX", inputs); + default_builtin_cache.insert("ui_text_caret_document_end.macos", inputs); // Text Scrolling @@ -636,7 +637,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_UP | KEY_MASK_CMD | KEY_MASK_ALT)); - default_builtin_cache.insert("ui_text_scroll_up.OSX", inputs); + default_builtin_cache.insert("ui_text_scroll_up.macos", inputs); inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_DOWN | KEY_MASK_CMD)); @@ -644,7 +645,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(KEY_DOWN | KEY_MASK_CMD | KEY_MASK_ALT)); - default_builtin_cache.insert("ui_text_scroll_down.OSX", inputs); + default_builtin_cache.insert("ui_text_scroll_down.macos", inputs); // Text Misc @@ -699,34 +700,57 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { return default_builtin_cache; } -void InputMap::load_default() { +const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with_feature_overrides_applied() { + if (default_builtin_with_overrides_cache.size() > 0) { + return default_builtin_with_overrides_cache; + } + OrderedHashMap<String, List<Ref<InputEvent>>> builtins = get_builtins(); - // List of Builtins which have an override for OSX. - Vector<String> osx_builtins; + // Get a list of all built in inputs which are valid overrides for the OS + // Key = builtin name (e.g. ui_accept) + // Value = override/feature names (e.g. macos, if it was defined as "ui_accept.macos" and the platform supports that feature) + Map<String, Vector<String>> builtins_with_overrides; for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) { - if (String(E.key()).ends_with(".OSX")) { - // Strip .OSX from name: some_input_name.OSX -> some_input_name - osx_builtins.push_back(String(E.key()).split(".")[0]); + String fullname = E.key(); + + Vector<String> split = fullname.split("."); + String name = split[0]; + String override_for = split.size() > 1 ? split[1] : String(); + + if (override_for != String() && OS::get_singleton()->has_feature(override_for)) { + builtins_with_overrides[name].push_back(override_for); } } for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) { String fullname = E.key(); - String name = fullname.split(".")[0]; - String override_for = fullname.split(".").size() > 1 ? fullname.split(".")[1] : ""; -#ifdef APPLE_STYLE_KEYS - if (osx_builtins.has(name) && override_for != "OSX") { - // Name has osx builtin but this particular one is for non-osx systems - so skip. + Vector<String> split = fullname.split("."); + String name = split[0]; + String override_for = split.size() > 1 ? split[1] : String(); + + if (builtins_with_overrides.has(name) && override_for == String()) { + // Builtin has an override but this particular one is not an override, so skip. continue; } -#else - if (override_for == "OSX") { - // Override for OSX - not needed on non-osx platforms. + + if (override_for != String() && !OS::get_singleton()->has_feature(override_for)) { + // OS does not support this override - skip. continue; } -#endif + + default_builtin_with_overrides_cache.insert(name, E.value()); + } + + return default_builtin_with_overrides_cache; +} + +void InputMap::load_default() { + OrderedHashMap<String, List<Ref<InputEvent>>> builtins = get_builtins_with_feature_overrides_applied(); + + for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) { + String name = E.key(); add_action(name); @@ -746,3 +770,7 @@ InputMap::InputMap() { ERR_FAIL_COND_MSG(singleton, "Singleton in InputMap already exist."); singleton = this; } + +InputMap::~InputMap() { + singleton = nullptr; +} diff --git a/core/input/input_map.h b/core/input/input_map.h index a2d3952f94..8bef722089 100644 --- a/core/input/input_map.h +++ b/core/input/input_map.h @@ -56,6 +56,7 @@ private: mutable OrderedHashMap<StringName, Action> input_map; OrderedHashMap<String, List<Ref<InputEvent>>> default_builtin_cache; + OrderedHashMap<String, List<Ref<InputEvent>>> default_builtin_with_overrides_cache; List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; @@ -93,8 +94,10 @@ public: String get_builtin_display_name(const String &p_name) const; // Use an Ordered Map so insertion order is preserved. We want the elements to be 'grouped' somewhat. const OrderedHashMap<String, List<Ref<InputEvent>>> &get_builtins(); + const OrderedHashMap<String, List<Ref<InputEvent>>> &get_builtins_with_feature_overrides_applied(); InputMap(); + ~InputMap(); }; #endif // INPUT_MAP_H diff --git a/core/input/shortcut.cpp b/core/input/shortcut.cpp index d0cb08724e..30e35190e4 100644 --- a/core/input/shortcut.cpp +++ b/core/input/shortcut.cpp @@ -31,14 +31,26 @@ #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; +void Shortcut::set_events(const Array &p_events) { + for (int i = 0; i < p_events.size(); i++) { + Ref<InputEventShortcut> ies = p_events[i]; + ERR_FAIL_COND_MSG(ies.is_valid(), "Cannot set a shortcut event to an instance of InputEventShortcut."); + } + + events = p_events; emit_changed(); } -Ref<InputEvent> Shortcut::get_event() const { - return event; +void Shortcut::set_events_list(const List<Ref<InputEvent>> *p_events) { + events.clear(); + + for (const Ref<InputEvent> &ie : *p_events) { + events.push_back(ie); + } +} + +Array Shortcut::get_events() const { + return events; } bool Shortcut::matches_event(const Ref<InputEvent> &p_event) const { @@ -48,29 +60,73 @@ bool Shortcut::matches_event(const Ref<InputEvent> &p_event) const { return true; } } - return event.is_valid() && event->is_match(p_event, true); + + for (int i = 0; i < events.size(); i++) { + Ref<InputEvent> ie = events[i]; + bool valid = ie.is_valid() && ie->is_match(p_event); + + // Stop on first valid event - don't need to check further. + if (valid) { + return true; + } + } + + return false; } String Shortcut::get_as_text() const { - if (event.is_valid()) { - return event->as_text(); - } else { - return "None"; + for (int i = 0; i < events.size(); i++) { + Ref<InputEvent> ie = events[i]; + // Return first shortcut which is valid + if (ie.is_valid()) { + return ie->as_text(); + } } + + return "None"; } bool Shortcut::has_valid_event() const { - return event.is_valid(); + // Tests if there is ANY input event which is valid. + for (int i = 0; i < events.size(); i++) { + Ref<InputEvent> ie = events[i]; + if (ie.is_valid()) { + return true; + } + } + + return false; } void Shortcut::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_event", "event"), &Shortcut::set_event); - ClassDB::bind_method(D_METHOD("get_event"), &Shortcut::get_event); + ClassDB::bind_method(D_METHOD("set_events", "events"), &Shortcut::set_events); + ClassDB::bind_method(D_METHOD("get_events"), &Shortcut::get_events); ClassDB::bind_method(D_METHOD("has_valid_event"), &Shortcut::has_valid_event); ClassDB::bind_method(D_METHOD("matches_event", "event"), &Shortcut::matches_event); ClassDB::bind_method(D_METHOD("get_as_text"), &Shortcut::get_as_text); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), "set_event", "get_event"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "events", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")), "set_events", "get_events"); +} + +bool Shortcut::is_event_array_equal(const Array &p_event_array1, const Array &p_event_array2) { + if (p_event_array1.size() != p_event_array2.size()) { + return false; + } + + bool is_same = true; + for (int i = 0; i < p_event_array1.size(); i++) { + Ref<InputEvent> ie_1 = p_event_array1[i]; + Ref<InputEvent> ie_2 = p_event_array2[i]; + + is_same = ie_1->is_match(ie_2); + + // Break on the first that doesn't match - don't need to check further. + if (!is_same) { + break; + } + } + + return is_same; } diff --git a/core/input/shortcut.h b/core/input/shortcut.h index 249dd1971f..a989b10626 100644 --- a/core/input/shortcut.h +++ b/core/input/shortcut.h @@ -37,18 +37,22 @@ class Shortcut : public Resource { GDCLASS(Shortcut, Resource); - Ref<InputEvent> event; + Array events; protected: static void _bind_methods(); public: - void set_event(const Ref<InputEvent> &p_shortcut); - Ref<InputEvent> get_event() const; + void set_events(const Array &p_events); + Array get_events() const; + + void set_events_list(const List<Ref<InputEvent>> *p_events); + bool matches_event(const Ref<InputEvent> &p_event) const; bool has_valid_event() const; String get_as_text() const; -}; + static bool is_event_array_equal(const Array &p_event_array1, const Array &p_event_array2); +}; #endif // SHORTCUT_H diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index e54c947340..df631053b8 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -233,7 +233,7 @@ uint64_t FileAccessCompressed::get_position() const { if (writing) { return write_pos; } else { - return read_block * block_size + read_pos; + return (uint64_t)read_block * block_size + read_pos; } } diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 7b43daf9c0..b2832b2a75 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -110,8 +110,8 @@ PackedData::PackedData() { } void PackedData::_free_packed_dirs(PackedDir *p_dir) { - for (Map<String, PackedDir *>::Element *E = p_dir->subdirs.front(); E; E = E->next()) { - _free_packed_dirs(E->get()); + for (const KeyValue<String, PackedDir *> &E : p_dir->subdirs) { + _free_packed_dirs(E.value); } memdelete(p_dir); } @@ -395,8 +395,8 @@ Error DirAccessPack::list_dir_begin() { list_dirs.clear(); list_files.clear(); - for (Map<String, PackedData::PackedDir *>::Element *E = current->subdirs.front(); E; E = E->next()) { - list_dirs.push_back(E->key()); + for (const KeyValue<String, PackedData::PackedDir *> &E : current->subdirs) { + list_dirs.push_back(E.key); } for (Set<String>::Element *E = current->files.front(); E; E = E->next()) { diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index b5c882e9ce..53bf7456e6 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -99,7 +99,7 @@ static int godot_testerror(voidpf opaque, voidpf stream) { } static voidpf godot_alloc(voidpf opaque, uInt items, uInt size) { - return memalloc(items * size); + return memalloc((size_t)items * size); } static void godot_free(voidpf opaque, voidpf address) { diff --git a/core/io/image.cpp b/core/io/image.cpp index 3112dd217f..c70f4b86bd 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -2506,7 +2506,7 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po clipped_src_rect.position.y = ABS(p_dest.y); } - if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { + if (clipped_src_rect.has_no_area()) { return; } @@ -2561,7 +2561,7 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co clipped_src_rect.position.y = ABS(p_dest.y); } - if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { + if (clipped_src_rect.has_no_area()) { return; } @@ -2615,7 +2615,7 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const P clipped_src_rect.position.y = ABS(p_dest.y); } - if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { + if (clipped_src_rect.has_no_area()) { return; } @@ -2664,7 +2664,7 @@ void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, c clipped_src_rect.position.y = ABS(p_dest.y); } - if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { + if (clipped_src_rect.has_no_area()) { return; } diff --git a/core/io/ip.cpp b/core/io/ip.cpp index e3102508a3..68b4e4b354 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -288,8 +288,8 @@ Array IP::_get_local_interfaces() const { Array results; Map<String, Interface_Info> interfaces; get_local_interfaces(&interfaces); - for (Map<String, Interface_Info>::Element *E = interfaces.front(); E; E = E->next()) { - Interface_Info &c = E->get(); + for (KeyValue<String, Interface_Info> &E : interfaces) { + Interface_Info &c = E.value; Dictionary rc; rc["name"] = c.name; rc["friendly"] = c.name_friendly; @@ -310,8 +310,8 @@ Array IP::_get_local_interfaces() const { void IP::get_local_addresses(List<IPAddress> *r_addresses) const { Map<String, Interface_Info> interfaces; get_local_interfaces(&interfaces); - for (Map<String, Interface_Info>::Element *E = interfaces.front(); E; E = E->next()) { - for (const IPAddress &F : E->get().ip_addresses) { + for (const KeyValue<String, Interface_Info> &E : interfaces) { + for (const IPAddress &F : E.value.ip_addresses) { r_addresses->push_front(F); } } diff --git a/core/io/logger.cpp b/core/io/logger.cpp index 09539f716c..b68a8b20a5 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -50,7 +50,7 @@ void Logger::set_flush_stdout_on_print(bool value) { _flush_stdout_on_print = value; } -void Logger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) { +void Logger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) { if (!should_log(true)) { return; } @@ -81,7 +81,11 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c err_details = p_code; } - logf_error("%s: %s\n", err_type, err_details); + if (p_editor_notify) { + logf_error("%s: %s\n", err_type, err_details); + } else { + logf_error("USER %s: %s\n", err_type, err_details); + } logf_error(" at: %s (%s:%i) - %s\n", p_function, p_file, p_line, p_code); } @@ -256,7 +260,7 @@ void CompositeLogger::logv(const char *p_format, va_list p_list, bool p_err) { } } -void CompositeLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) { +void CompositeLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) { if (!should_log(true)) { return; } diff --git a/core/io/logger.h b/core/io/logger.h index ccf68562d6..f244f70e7e 100644 --- a/core/io/logger.h +++ b/core/io/logger.h @@ -54,7 +54,7 @@ public: static void set_flush_stdout_on_print(bool value); virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0 = 0; - virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR); + virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR); void logf(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; void logf_error(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; @@ -103,7 +103,7 @@ public: CompositeLogger(Vector<Logger *> p_loggers); virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0; - virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR); + virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type = ERR_ERROR); void add_logger(Logger *p_logger); diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp deleted file mode 100644 index c145225751..0000000000 --- a/core/io/multiplayer_api.cpp +++ /dev/null @@ -1,1150 +0,0 @@ -/*************************************************************************/ -/* multiplayer_api.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "multiplayer_api.h" - -#include "core/debugger/engine_debugger.h" -#include "core/io/marshalls.h" -#include "core/io/multiplayer_replicator.h" -#include "scene/main/node.h" - -#include <stdint.h> - -#ifdef DEBUG_ENABLED -#include "core/os/os.h" -#endif - -String _get_rpc_md5(const Node *p_node) { - String rpc_list; - const Vector<MultiplayerAPI::RPCConfig> node_config = p_node->get_node_rpc_methods(); - for (int i = 0; i < node_config.size(); i++) { - rpc_list += String(node_config[i].name); - } - if (p_node->get_script_instance()) { - const Vector<MultiplayerAPI::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods(); - for (int i = 0; i < script_config.size(); i++) { - rpc_list += String(script_config[i].name); - } - } - return rpc_list.md5_text(); -} - -const MultiplayerAPI::RPCConfig _get_rpc_config(const Node *p_node, const StringName &p_method, uint16_t &r_id) { - const Vector<MultiplayerAPI::RPCConfig> node_config = p_node->get_node_rpc_methods(); - for (int i = 0; i < node_config.size(); i++) { - if (node_config[i].name == p_method) { - r_id = ((uint16_t)i) | (1 << 15); - return node_config[i]; - } - } - if (p_node->get_script_instance()) { - const Vector<MultiplayerAPI::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods(); - for (int i = 0; i < script_config.size(); i++) { - if (script_config[i].name == p_method) { - r_id = (uint16_t)i; - return script_config[i]; - } - } - } - return MultiplayerAPI::RPCConfig(); -} - -const MultiplayerAPI::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_id) { - Vector<MultiplayerAPI::RPCConfig> config; - uint16_t id = p_id; - if (id & (1 << 15)) { - id = id & ~(1 << 15); - config = p_node->get_node_rpc_methods(); - } else if (p_node->get_script_instance()) { - config = p_node->get_script_instance()->get_rpc_methods(); - } - if (id < config.size()) { - return config[id]; - } - return MultiplayerAPI::RPCConfig(); -} - -_FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) { - switch (mode) { - case MultiplayerAPI::RPC_MODE_DISABLED: { - return false; - } break; - case MultiplayerAPI::RPC_MODE_ANY: { - return true; - } break; - case MultiplayerAPI::RPC_MODE_AUTHORITY: { - return !p_node->is_network_authority() && p_remote_id == p_node->get_network_authority(); - } break; - } - - return false; -} - -void MultiplayerAPI::poll() { - if (!network_peer.is_valid() || network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED) { - return; - } - - network_peer->poll(); - - if (!network_peer.is_valid()) { // It's possible that polling might have resulted in a disconnection, so check here. - return; - } - - while (network_peer->get_available_packet_count()) { - int sender = network_peer->get_packet_peer(); - const uint8_t *packet; - int len; - - Error err = network_peer->get_packet(&packet, len); - if (err != OK) { - ERR_PRINT("Error getting packet!"); - break; // Something is wrong! - } - - rpc_sender_id = sender; - _process_packet(sender, packet, len); - rpc_sender_id = 0; - - if (!network_peer.is_valid()) { - break; // It's also possible that a packet or RPC caused a disconnection, so also check here. - } - } - if (network_peer.is_valid() && network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTED) { - replicator->poll(); - } -} - -void MultiplayerAPI::clear() { - replicator->clear(); - connected_peers.clear(); - path_get_cache.clear(); - path_send_cache.clear(); - packet_cache.clear(); - last_send_cache_id = 1; -} - -void MultiplayerAPI::set_root_node(Node *p_node) { - root_node = p_node; -} - -Node *MultiplayerAPI::get_root_node() { - return root_node; -} - -void MultiplayerAPI::set_network_peer(const Ref<MultiplayerPeer> &p_peer) { - if (p_peer == network_peer) { - return; // Nothing to do - } - - ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, - "Supplied MultiplayerPeer must be connecting or connected."); - - if (network_peer.is_valid()) { - network_peer->disconnect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); - network_peer->disconnect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer)); - network_peer->disconnect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server)); - network_peer->disconnect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); - network_peer->disconnect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); - clear(); - } - - network_peer = p_peer; - - if (network_peer.is_valid()) { - network_peer->connect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); - network_peer->connect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer)); - network_peer->connect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server)); - network_peer->connect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); - network_peer->connect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); - } -} - -Ref<MultiplayerPeer> MultiplayerAPI::get_network_peer() const { - return network_peer; -} - -#ifdef DEBUG_ENABLED -void _profile_node_data(const String &p_what, ObjectID p_id) { - if (EngineDebugger::is_profiling("multiplayer")) { - Array values; - values.push_back("node"); - values.push_back(p_id); - values.push_back(p_what); - EngineDebugger::profiler_add_frame_data("multiplayer", values); - } -} - -void _profile_bandwidth_data(const String &p_inout, int p_size) { - if (EngineDebugger::is_profiling("multiplayer")) { - Array values; - values.push_back("bandwidth"); - values.push_back(p_inout); - values.push_back(OS::get_singleton()->get_ticks_msec()); - values.push_back(p_size); - EngineDebugger::profiler_add_frame_data("multiplayer", values); - } -} -#endif - -// Returns the packet size stripping the node path added when the node is not yet cached. -int get_packet_len(uint32_t p_node_target, int p_packet_len) { - if (p_node_target & 0x80000000) { - int ofs = p_node_target & 0x7FFFFFFF; - return p_packet_len - (p_packet_len - ofs); - } else { - return p_packet_len; - } -} - -void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(root_node == nullptr, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it."); - ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small."); - -#ifdef DEBUG_ENABLED - _profile_bandwidth_data("in", p_packet_len); -#endif - - // Extract the `packet_type` from the LSB three bits: - uint8_t packet_type = p_packet[0] & 7; - - switch (packet_type) { - case NETWORK_COMMAND_SIMPLIFY_PATH: { - _process_simplify_path(p_from, p_packet, p_packet_len); - } break; - - case NETWORK_COMMAND_CONFIRM_PATH: { - _process_confirm_path(p_from, p_packet, p_packet_len); - } break; - - case NETWORK_COMMAND_REMOTE_CALL: { - // Extract packet meta - int packet_min_size = 1; - int name_id_offset = 1; - ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); - // Compute the meta size, which depends on the compression level. - int node_id_compression = (p_packet[0] & 24) >> NODE_ID_COMPRESSION_SHIFT; - int name_id_compression = (p_packet[0] & 32) >> NAME_ID_COMPRESSION_SHIFT; - - switch (node_id_compression) { - case NETWORK_NODE_ID_COMPRESSION_8: - packet_min_size += 1; - name_id_offset += 1; - break; - case NETWORK_NODE_ID_COMPRESSION_16: - packet_min_size += 2; - name_id_offset += 2; - break; - case NETWORK_NODE_ID_COMPRESSION_32: - packet_min_size += 4; - name_id_offset += 4; - break; - default: - ERR_FAIL_MSG("Was not possible to extract the node id compression mode."); - } - switch (name_id_compression) { - case NETWORK_NAME_ID_COMPRESSION_8: - packet_min_size += 1; - break; - case NETWORK_NAME_ID_COMPRESSION_16: - packet_min_size += 2; - break; - default: - ERR_FAIL_MSG("Was not possible to extract the name id compression mode."); - } - ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); - - uint32_t node_target = 0; - switch (node_id_compression) { - case NETWORK_NODE_ID_COMPRESSION_8: - node_target = p_packet[1]; - break; - case NETWORK_NODE_ID_COMPRESSION_16: - node_target = decode_uint16(p_packet + 1); - break; - case NETWORK_NODE_ID_COMPRESSION_32: - node_target = decode_uint32(p_packet + 1); - break; - default: - // Unreachable, checked before. - CRASH_NOW(); - } - - Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len); - ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found."); - - uint16_t name_id = 0; - switch (name_id_compression) { - case NETWORK_NAME_ID_COMPRESSION_8: - name_id = p_packet[name_id_offset]; - break; - case NETWORK_NAME_ID_COMPRESSION_16: - name_id = decode_uint16(p_packet + name_id_offset); - break; - default: - // Unreachable, checked before. - CRASH_NOW(); - } - - const int packet_len = get_packet_len(node_target, p_packet_len); - _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size); - } break; - - case NETWORK_COMMAND_RAW: { - _process_raw(p_from, p_packet, p_packet_len); - } break; - case NETWORK_COMMAND_SPAWN: { - replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, true); - } break; - case NETWORK_COMMAND_DESPAWN: { - replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, false); - } break; - case NETWORK_COMMAND_SYNC: { - replicator->process_sync(p_from, p_packet, p_packet_len); - } break; - } -} - -Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) { - Node *node = nullptr; - - if (p_node_target & 0x80000000) { - // Use full path (not cached yet). - int ofs = p_node_target & 0x7FFFFFFF; - - ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared."); - - String paths; - paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs); - - NodePath np = paths; - - node = root_node->get_node(np); - - if (!node) { - ERR_PRINT("Failed to get path from RPC: " + String(np) + "."); - } - return node; - } else { - // Use cached path. - return get_cached_node(p_from, p_node_target); - } -} - -void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { - ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small."); - - // Check that remote can call the RPC on this node. - const RPCConfig config = _get_rpc_config_by_id(p_node, p_rpc_method_id); - ERR_FAIL_COND(config.name == StringName()); - - bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from); - ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_network_authority()) + "."); - - int argc = 0; - bool byte_only = false; - - const bool byte_only_or_no_args = ((p_packet[0] & 64) >> BYTE_ONLY_OR_NO_ARGS_SHIFT) == 1; - if (byte_only_or_no_args) { - if (p_offset < p_packet_len) { - // This packet contains only bytes. - argc = 1; - byte_only = true; - } else { - // This rpc calls a method without parameters. - } - } else { - // Normal variant, takes the argument count from the packet. - ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); - argc = p_packet[p_offset]; - p_offset += 1; - } - - Vector<Variant> args; - Vector<const Variant *> argp; - args.resize(argc); - argp.resize(argc); - -#ifdef DEBUG_ENABLED - _profile_node_data("in_rpc", p_node->get_instance_id()); -#endif - - if (byte_only) { - Vector<uint8_t> pure_data; - const int len = p_packet_len - p_offset; - pure_data.resize(len); - memcpy(pure_data.ptrw(), &p_packet[p_offset], len); - args.write[0] = pure_data; - argp.write[0] = &args[0]; - p_offset += len; - } else { - for (int i = 0; i < argc; i++) { - ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); - - int vlen; - Error err = decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); - ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument."); - - argp.write[i] = &args[i]; - p_offset += vlen; - } - } - - Callable::CallError ce; - - p_node->call(config.name, (const Variant **)argp.ptr(), argc, ce); - if (ce.error != Callable::CallError::CALL_OK) { - String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce); - error = "RPC - " + error; - ERR_PRINT(error); - } -} - -void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small."); - int ofs = 1; - - String methods_md5; - methods_md5.parse_utf8((const char *)(p_packet + ofs), 32); - ofs += 33; - - int id = decode_uint32(&p_packet[ofs]); - ofs += 4; - - String paths; - paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs); - - NodePath path = paths; - - if (!path_get_cache.has(p_from)) { - path_get_cache[p_from] = PathGetCache(); - } - - Node *node = root_node->get_node(path); - ERR_FAIL_COND(node == nullptr); - const bool valid_rpc_checksum = _get_rpc_md5(node) == methods_md5; - if (valid_rpc_checksum == false) { - ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); - } - - PathGetCache::NodeInfo ni; - ni.path = path; - - path_get_cache[p_from].nodes[id] = ni; - - // Encode path to send ack. - CharString pname = String(path).utf8(); - int len = encode_cstring(pname.get_data(), nullptr); - - Vector<uint8_t> packet; - - packet.resize(1 + 1 + len); - packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH; - packet.write[1] = valid_rpc_checksum; - encode_cstring(pname.get_data(), &packet.write[2]); - - network_peer->set_transfer_channel(0); - network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); - network_peer->set_target_peer(p_from); - network_peer->put_packet(packet.ptr(), packet.size()); -} - -void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small."); - - const bool valid_rpc_checksum = p_packet[1]; - - String paths; - paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2); - - NodePath path = paths; - - if (valid_rpc_checksum == false) { - ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); - } - - PathSentCache *psc = path_send_cache.getptr(path); - ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache."); - - Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from); - ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path."); - E->get() = true; -} - -bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) { - bool has_all_peers = true; - List<int> peers_to_add; // If one is missing, take note to add it. - - for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { - if (p_target < 0 && E->get() == -p_target) { - continue; // Continue, excluded. - } - - if (p_target > 0 && E->get() != p_target) { - continue; // Continue, not for this peer. - } - - Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); - - if (!F || !F->get()) { - // Path was not cached, or was cached but is unconfirmed. - if (!F) { - // Not cached at all, take note. - peers_to_add.push_back(E->get()); - } - - has_all_peers = false; - } - } - - if (peers_to_add.size() > 0) { - // Those that need to be added, send a message for this. - - // Encode function name. - const CharString path = String(p_path).utf8(); - const int path_len = encode_cstring(path.get_data(), nullptr); - - // Extract MD5 from rpc methods list. - const String methods_md5 = _get_rpc_md5(p_node); - const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder. - - Vector<uint8_t> packet; - packet.resize(1 + 4 + path_len + methods_md5_len); - int ofs = 0; - - packet.write[ofs] = NETWORK_COMMAND_SIMPLIFY_PATH; - ofs += 1; - - ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]); - - ofs += encode_uint32(psc->id, &packet.write[ofs]); - - ofs += encode_cstring(path.get_data(), &packet.write[ofs]); - - for (int &E : peers_to_add) { - network_peer->set_target_peer(E); // To all of you. - network_peer->set_transfer_channel(0); - network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); - network_peer->put_packet(packet.ptr(), packet.size()); - - psc->confirmed_peers.insert(E, false); // Insert into confirmed, but as false since it was not confirmed. - } - } - - return has_all_peers; -} - -// The variant is compressed and encoded; The first byte contains all the meta -// information and the format is: -// - The first LSB 5 bits are used for the variant type. -// - The next two bits are used to store the encoding mode. -// - The most significant is used to store the boolean value. -#define VARIANT_META_TYPE_MASK 0x1F -#define VARIANT_META_EMODE_MASK 0x60 -#define VARIANT_META_BOOL_MASK 0x80 -#define ENCODE_8 0 << 5 -#define ENCODE_16 1 << 5 -#define ENCODE_32 2 << 5 -#define ENCODE_64 3 << 5 -Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) { - // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31 - CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK); - - uint8_t *buf = r_buffer; - r_len = 0; - uint8_t encode_mode = 0; - - switch (p_variant.get_type()) { - case Variant::BOOL: { - if (buf) { - // We still have 1 free bit in the meta, so let's use it. - buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0; - buf[0] |= encode_mode | p_variant.get_type(); - } - r_len += 1; - } break; - case Variant::INT: { - if (buf) { - // Reserve the first byte for the meta. - buf += 1; - } - r_len += 1; - int64_t val = p_variant; - if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) { - // Use 8 bit - encode_mode = ENCODE_8; - if (buf) { - buf[0] = val; - } - r_len += 1; - } else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) { - // Use 16 bit - encode_mode = ENCODE_16; - if (buf) { - encode_uint16(val, buf); - } - r_len += 2; - } else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) { - // Use 32 bit - encode_mode = ENCODE_32; - if (buf) { - encode_uint32(val, buf); - } - r_len += 4; - } else { - // Use 64 bit - encode_mode = ENCODE_64; - if (buf) { - encode_uint64(val, buf); - } - r_len += 8; - } - // Store the meta - if (buf) { - buf -= 1; - buf[0] = encode_mode | p_variant.get_type(); - } - } break; - default: - // Any other case is not yet compressed. - Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding); - if (err != OK) { - return err; - } - if (r_buffer) { - // The first byte is not used by the marshalling, so store the type - // so we know how to decompress and decode this variant. - r_buffer[0] = p_variant.get_type(); - } - } - - return OK; -} - -Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) { - const uint8_t *buf = p_buffer; - int len = p_len; - - ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); - uint8_t type = buf[0] & VARIANT_META_TYPE_MASK; - uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK; - - ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA); - - switch (type) { - case Variant::BOOL: { - bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0; - r_variant = val; - if (r_len) { - *r_len = 1; - } - } break; - case Variant::INT: { - buf += 1; - len -= 1; - if (r_len) { - *r_len = 1; - } - if (encode_mode == ENCODE_8) { - // 8 bits. - ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); - int8_t val = buf[0]; - r_variant = val; - if (r_len) { - (*r_len) += 1; - } - } else if (encode_mode == ENCODE_16) { - // 16 bits. - ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA); - int16_t val = decode_uint16(buf); - r_variant = val; - if (r_len) { - (*r_len) += 2; - } - } else if (encode_mode == ENCODE_32) { - // 32 bits. - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - int32_t val = decode_uint32(buf); - r_variant = val; - if (r_len) { - (*r_len) += 4; - } - } else { - // 64 bits. - ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); - int64_t val = decode_uint64(buf); - r_variant = val; - if (r_len) { - (*r_len) += 8; - } - } - } break; - default: - Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding); - if (err != OK) { - return err; - } - } - - return OK; -} - -void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) { - ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree."); - - ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree."); - - ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to remote call/set when networking is disconnected."); - - ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments >255."); - - if (p_to != 0 && !connected_peers.has(ABS(p_to))) { - ERR_FAIL_COND_MSG(p_to == network_peer->get_unique_id(), "Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id()) + "."); - - ERR_FAIL_MSG("Attempt to remote call unexisting ID: " + itos(p_to) + "."); - } - - NodePath from_path = (root_node->get_path()).rel_path_to(p_from->get_path()); - ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!"); - - // See if the path is cached. - PathSentCache *psc = path_send_cache.getptr(from_path); - if (!psc) { - // Path is not cached, create. - path_send_cache[from_path] = PathSentCache(); - psc = path_send_cache.getptr(from_path); - psc->id = last_send_cache_id++; - } - - // See if all peers have cached path (if so, call can be fast). - const bool has_all_peers = _send_confirm_path(p_from, from_path, psc, p_to); - - // Create base packet, lots of hardcode because it must be tight. - - int ofs = 0; - -#define MAKE_ROOM(m_amount) \ - if (packet_cache.size() < m_amount) \ - packet_cache.resize(m_amount); - - // Encode meta. - // The meta is composed by a single byte that contains (starting from the least significant bit): - // - `NetworkCommands` in the first three bits. - // - `NetworkNodeIdCompression` in the next 2 bits. - // - `NetworkNameIdCompression` in the next 1 bit. - // - `byte_only_or_no_args` in the next 1 bit. - // - So we still have the last bit free! - uint8_t command_type = NETWORK_COMMAND_REMOTE_CALL; - uint8_t node_id_compression = UINT8_MAX; - uint8_t name_id_compression = UINT8_MAX; - bool byte_only_or_no_args = false; - - MAKE_ROOM(1); - // The meta is composed along the way, so just set 0 for now. - packet_cache.write[0] = 0; - ofs += 1; - - // Encode Node ID. - if (has_all_peers) { - // Compress the node ID only if all the target peers already know it. - if (psc->id >= 0 && psc->id <= 255) { - // We can encode the id in 1 byte - node_id_compression = NETWORK_NODE_ID_COMPRESSION_8; - MAKE_ROOM(ofs + 1); - packet_cache.write[ofs] = static_cast<uint8_t>(psc->id); - ofs += 1; - } else if (psc->id >= 0 && psc->id <= 65535) { - // We can encode the id in 2 bytes - node_id_compression = NETWORK_NODE_ID_COMPRESSION_16; - MAKE_ROOM(ofs + 2); - encode_uint16(static_cast<uint16_t>(psc->id), &(packet_cache.write[ofs])); - ofs += 2; - } else { - // Too big, let's use 4 bytes. - node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; - MAKE_ROOM(ofs + 4); - encode_uint32(psc->id, &(packet_cache.write[ofs])); - ofs += 4; - } - } else { - // The targets don't know the node yet, so we need to use 32 bits int. - node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; - MAKE_ROOM(ofs + 4); - encode_uint32(psc->id, &(packet_cache.write[ofs])); - ofs += 4; - } - - // Encode method ID - if (p_rpc_id <= UINT8_MAX) { - // The ID fits in 1 byte - name_id_compression = NETWORK_NAME_ID_COMPRESSION_8; - MAKE_ROOM(ofs + 1); - packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id); - ofs += 1; - } else { - // The ID is larger, let's use 2 bytes - name_id_compression = NETWORK_NAME_ID_COMPRESSION_16; - MAKE_ROOM(ofs + 2); - encode_uint16(p_rpc_id, &(packet_cache.write[ofs])); - ofs += 2; - } - - if (p_argcount == 0) { - byte_only_or_no_args = true; - } else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) { - byte_only_or_no_args = true; - // Special optimization when only the byte vector is sent. - const Vector<uint8_t> data = *p_arg[0]; - MAKE_ROOM(ofs + data.size()); - memcpy(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size()); - ofs += data.size(); - } else { - // Arguments - MAKE_ROOM(ofs + 1); - packet_cache.write[ofs] = p_argcount; - ofs += 1; - for (int i = 0; i < p_argcount; i++) { - int len(0); - Error err = encode_and_compress_variant(*p_arg[i], nullptr, len); - ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); - MAKE_ROOM(ofs + len); - encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len); - ofs += len; - } - } - - ERR_FAIL_COND(command_type > 7); - ERR_FAIL_COND(node_id_compression > 3); - ERR_FAIL_COND(name_id_compression > 1); - - // We can now set the meta - packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + ((byte_only_or_no_args ? 1 : 0) << BYTE_ONLY_OR_NO_ARGS_SHIFT); - -#ifdef DEBUG_ENABLED - _profile_bandwidth_data("out", ofs); -#endif - - // Take chance and set transfer mode, since all send methods will use it. - network_peer->set_transfer_channel(p_config.channel); - network_peer->set_transfer_mode(p_config.transfer_mode); - - if (has_all_peers) { - // They all have verified paths, so send fast. - network_peer->set_target_peer(p_to); // To all of you. - network_peer->put_packet(packet_cache.ptr(), ofs); // A message with love. - } else { - // Unreachable because the node ID is never compressed if the peers doesn't know it. - CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32); - - // Not all verified path, so send one by one. - - // Append path at the end, since we will need it for some packets. - CharString pname = String(from_path).utf8(); - int path_len = encode_cstring(pname.get_data(), nullptr); - MAKE_ROOM(ofs + path_len); - encode_cstring(pname.get_data(), &(packet_cache.write[ofs])); - - for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { - if (p_to < 0 && E->get() == -p_to) { - continue; // Continue, excluded. - } - - if (p_to > 0 && E->get() != p_to) { - continue; // Continue, not for this peer. - } - - Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); - ERR_CONTINUE(!F); // Should never happen. - - network_peer->set_target_peer(E->get()); // To this one specifically. - - if (F->get()) { - // This one confirmed path, so use id. - encode_uint32(psc->id, &(packet_cache.write[1])); - network_peer->put_packet(packet_cache.ptr(), ofs); - } else { - // This one did not confirm path yet, so use entire path (sorry!). - encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag. - network_peer->put_packet(packet_cache.ptr(), ofs + path_len); - } - } - } -} - -void MultiplayerAPI::_add_peer(int p_id) { - connected_peers.insert(p_id); - path_get_cache.insert(p_id, PathGetCache()); - if (is_network_server()) { - replicator->spawn_all(p_id); - } - emit_signal(SNAME("network_peer_connected"), p_id); -} - -void MultiplayerAPI::_del_peer(int p_id) { - connected_peers.erase(p_id); - // Cleanup get cache. - path_get_cache.erase(p_id); - // Cleanup sent cache. - // Some refactoring is needed to make this faster and do paths GC. - List<NodePath> keys; - path_send_cache.get_key_list(&keys); - for (const NodePath &E : keys) { - PathSentCache *psc = path_send_cache.getptr(E); - psc->confirmed_peers.erase(p_id); - } - emit_signal(SNAME("network_peer_disconnected"), p_id); -} - -void MultiplayerAPI::_connected_to_server() { - emit_signal(SNAME("connected_to_server")); -} - -void MultiplayerAPI::_connection_failed() { - emit_signal(SNAME("connection_failed")); -} - -void MultiplayerAPI::_server_disconnected() { - emit_signal(SNAME("server_disconnected")); -} - -void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { - ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to call an RPC while no network peer is active."); - ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree."); - ERR_FAIL_COND_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected."); - - int node_id = network_peer->get_unique_id(); - bool call_local_native = false; - bool call_local_script = false; - uint16_t rpc_id = UINT16_MAX; - const RPCConfig config = _get_rpc_config(p_node, p_method, rpc_id); - ERR_FAIL_COND_MSG(config.name == StringName(), - vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is not marked for RPCs.", p_method, p_node->get_path())); - if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { - if (rpc_id & (1 << 15)) { - call_local_native = config.sync; - } else { - call_local_script = config.sync; - } - } - - if (p_peer_id != node_id) { -#ifdef DEBUG_ENABLED - _profile_node_data("out_rpc", p_node->get_instance_id()); -#endif - - _send_rpc(p_node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount); - } - - if (call_local_native) { - int temp_id = rpc_sender_id; - rpc_sender_id = get_network_unique_id(); - Callable::CallError ce; - p_node->call(p_method, p_arg, p_argcount, ce); - rpc_sender_id = temp_id; - if (ce.error != Callable::CallError::CALL_OK) { - String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce); - error = "rpc() aborted in local call: - " + error + "."; - ERR_PRINT(error); - return; - } - } - - if (call_local_script) { - int temp_id = rpc_sender_id; - rpc_sender_id = get_network_unique_id(); - Callable::CallError ce; - ce.error = Callable::CallError::CALL_OK; - p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce); - rpc_sender_id = temp_id; - if (ce.error != Callable::CallError::CALL_OK) { - String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce); - error = "rpc() aborted in script local call: - " + error + "."; - ERR_PRINT(error); - return; - } - } - - ERR_FAIL_COND_MSG(p_peer_id == node_id && !config.sync, "RPC '" + p_method + "' on yourself is not allowed by selected mode."); -} - -Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, MultiplayerPeer::TransferMode p_mode, int p_channel) { - ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet."); - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no network peer is active."); - ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected."); - - MAKE_ROOM(p_data.size() + 1); - const uint8_t *r = p_data.ptr(); - packet_cache.write[0] = NETWORK_COMMAND_RAW; - memcpy(&packet_cache.write[1], &r[0], p_data.size()); - - network_peer->set_target_peer(p_to); - network_peer->set_transfer_channel(p_channel); - network_peer->set_transfer_mode(p_mode); - - return network_peer->put_packet(packet_cache.ptr(), p_data.size() + 1); -} - -void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small."); - - Vector<uint8_t> out; - int len = p_packet_len - 1; - out.resize(len); - { - uint8_t *w = out.ptrw(); - memcpy(&w[0], &p_packet[1], len); - } - emit_signal(SNAME("network_peer_packet"), p_from, out); -} - -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(); -} - -bool MultiplayerAPI::is_network_server() const { - return network_peer.is_valid() && network_peer->is_server(); -} - -void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) { - ERR_FAIL_COND_MSG(!network_peer.is_valid(), "No network peer is assigned. Unable to set 'refuse_new_connections'."); - network_peer->set_refuse_new_connections(p_refuse); -} - -bool MultiplayerAPI::is_refusing_new_network_connections() const { - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. Unable to get 'refuse_new_connections'."); - return network_peer->is_refusing_new_connections(); -} - -Vector<int> MultiplayerAPI::get_network_connected_peers() const { - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), Vector<int>(), "No network peer is assigned. Assume no peers are connected."); - - Vector<int> ret; - for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { - ret.push_back(E->get()); - } - - return ret; -} - -void MultiplayerAPI::set_allow_object_decoding(bool p_enable) { - allow_object_decoding = p_enable; -} - -bool MultiplayerAPI::is_object_decoding_allowed() const { - return allow_object_decoding; -} - -MultiplayerReplicator *MultiplayerAPI::get_replicator() const { - return replicator; -} - -void MultiplayerAPI::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) { - replicator->scene_enter_exit_notify(p_scene, p_node, p_enter); -} - -void MultiplayerAPI::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); - ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node); - ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &MultiplayerAPI::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer); - ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer); - ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id); - ClassDB::bind_method(D_METHOD("is_network_server"), &MultiplayerAPI::is_network_server); - ClassDB::bind_method(D_METHOD("get_rpc_sender_id"), &MultiplayerAPI::get_rpc_sender_id); - ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &MultiplayerAPI::set_network_peer); - ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll); - ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear); - - ClassDB::bind_method(D_METHOD("get_network_connected_peers"), &MultiplayerAPI::get_network_connected_peers); - ClassDB::bind_method(D_METHOD("set_refuse_new_network_connections", "refuse"), &MultiplayerAPI::set_refuse_new_network_connections); - ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections); - ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding); - ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed); - 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"))); - ADD_SIGNAL(MethodInfo("network_peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet"))); - ADD_SIGNAL(MethodInfo("connected_to_server")); - ADD_SIGNAL(MethodInfo("connection_failed")); - ADD_SIGNAL(MethodInfo("server_disconnected")); - - BIND_ENUM_CONSTANT(RPC_MODE_DISABLED); - BIND_ENUM_CONSTANT(RPC_MODE_ANY); - BIND_ENUM_CONSTANT(RPC_MODE_AUTHORITY); -} - -MultiplayerAPI::MultiplayerAPI() { - replicator = memnew(MultiplayerReplicator(this)); - clear(); -} - -MultiplayerAPI::~MultiplayerAPI() { - clear(); - memdelete(replicator); -} diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 8da44fd290..87d2b66e5b 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -138,6 +138,7 @@ Error PacketPeer::_get_packet_error() const { void PacketPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &PacketPeer::_bnd_get_var, DEFVAL(false)); ClassDB::bind_method(D_METHOD("put_var", "var", "full_objects"), &PacketPeer::put_var, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_packet"), &PacketPeer::_get_packet); ClassDB::bind_method(D_METHOD("put_packet", "buffer"), &PacketPeer::_put_packet); ClassDB::bind_method(D_METHOD("get_packet_error"), &PacketPeer::_get_packet_error); @@ -151,6 +152,51 @@ void PacketPeer::_bind_methods() { /***************/ +int PacketPeerExtension::get_available_packet_count() const { + int count; + if (GDVIRTUAL_CALL(_get_available_packet_count, count)) { + return count; + } + WARN_PRINT_ONCE("PacketPeerExtension::_get_available_packet_count is unimplemented!"); + return -1; +} + +Error PacketPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + int err; + if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("PacketPeerExtension::_get_packet_native is unimplemented!"); + return FAILED; +} + +Error PacketPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + int err; + if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("PacketPeerExtension::_put_packet_native is unimplemented!"); + return FAILED; +} + +int PacketPeerExtension::get_max_packet_size() const { + int size; + if (GDVIRTUAL_CALL(_get_max_packet_size, size)) { + return size; + } + WARN_PRINT_ONCE("PacketPeerExtension::_get_max_packet_size is unimplemented!"); + return 0; +} + +void PacketPeerExtension::_bind_methods() { + GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size"); + GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size"); + GDVIRTUAL_BIND(_get_available_packet_count); + GDVIRTUAL_BIND(_get_max_packet_size); +} + +/***************/ + void PacketPeerStream::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream_peer", "peer"), &PacketPeerStream::set_stream_peer); ClassDB::bind_method(D_METHOD("get_stream_peer"), &PacketPeerStream::get_stream_peer); diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index 9a345af3d0..bc1f4aaabf 100644 --- a/core/io/packet_peer.h +++ b/core/io/packet_peer.h @@ -35,6 +35,10 @@ #include "core/object/class_db.h" #include "core/templates/ring_buffer.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" +#include "core/variant/native_ptr.h" + class PacketPeer : public RefCounted { GDCLASS(PacketPeer, RefCounted); @@ -73,6 +77,25 @@ public: ~PacketPeer() {} }; +class PacketPeerExtension : public PacketPeer { + GDCLASS(PacketPeerExtension, PacketPeer); + +protected: + static void _bind_methods(); + +public: + virtual int get_available_packet_count() const override; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; + virtual int get_max_packet_size() const override; + + /* GDExtension */ + GDVIRTUAL0RC(int, _get_available_packet_count); + GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); + GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int); + GDVIRTUAL0RC(int, _get_max_packet_size); +}; + class PacketPeerStream : public PacketPeer { GDCLASS(PacketPeerStream, PacketPeer); diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 87b4d7195d..972076e397 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -136,6 +136,7 @@ String Resource::get_scene_unique_id() const { void Resource::set_name(const String &p_name) { name = p_name; + emit_changed(); } String Resource::get_name() const { @@ -540,9 +541,9 @@ void ResourceCache::dump(const char *p_file, bool p_short) { } } - for (Map<String, int>::Element *E = type_count.front(); E; E = E->next()) { + for (const KeyValue<String, int> &E : type_count) { if (f) { - f->store_line(E->key() + " count: " + itos(E->get())); + f->store_line(E.key + " count: " + itos(E.value)); } } if (f) { diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 84fd6496a7..cbb033f6c6 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -1960,8 +1960,8 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p Vector<RES> save_order; save_order.resize(external_resources.size()); - for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { - save_order.write[E->get()] = E->key(); + for (const KeyValue<RES, int> &E : external_resources) { + save_order.write[E.value] = E.key; } for (int i = 0; i < save_order.size(); i++) { diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index 1e166015b0..cd44c537a8 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -418,7 +418,7 @@ Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_extension(const St } String ResourceFormatImporter::get_import_base_path(const String &p_for_file) const { - return ProjectSettings::IMPORTED_FILES_PATH.plus_file(p_for_file.get_file() + "-" + p_for_file.md5_text()); + return ProjectSettings::get_singleton()->get_imported_files_path().plus_file(p_for_file.get_file() + "-" + p_for_file.md5_text()); } bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) const { diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 3026236f07..2198761c2a 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -156,8 +156,8 @@ void ResourceFormatLoader::get_dependencies(const String &p_path, List<String> * Error ResourceFormatLoader::rename_dependencies(const String &p_path, const Map<String, String> &p_map) { Dictionary deps_dict; - for (Map<String, String>::Element *E = p_map.front(); E; E = E->next()) { - deps_dict[E->key()] = E->value(); + for (KeyValue<String, String> E : p_map) { + deps_dict[E.key] = E.value; } int64_t err; diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp index 97d683f415..b7d01712ff 100644 --- a/core/io/resource_uid.cpp +++ b/core/io/resource_uid.cpp @@ -29,6 +29,8 @@ /*************************************************************************/ #include "resource_uid.h" + +#include "core/config/project_settings.h" #include "core/crypto/crypto.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" @@ -36,7 +38,9 @@ static constexpr uint32_t char_count = ('z' - 'a'); static constexpr uint32_t base = char_count + ('9' - '0'); -const char *ResourceUID::CACHE_FILE = "res://.godot/uid_cache.bin"; +String ResourceUID::get_cache_file() { + return ProjectSettings::get_singleton()->get_project_data_path().plus_file("uid_cache.bin"); +} String ResourceUID::id_to_text(ID p_id) const { if (p_id < 0) { @@ -126,8 +130,7 @@ String ResourceUID::get_id_path(ID p_id) const { MutexLock l(mutex); ERR_FAIL_COND_V(!unique_ids.has(p_id), String()); const CharString &cs = unique_ids[p_id].cs; - String s(cs.ptr()); - return s; + return String::utf8(cs.ptr()); } void ResourceUID::remove_id(ID p_id) { MutexLock l(mutex); @@ -136,12 +139,13 @@ void ResourceUID::remove_id(ID p_id) { } Error ResourceUID::save_to_cache() { - if (!FileAccess::exists(CACHE_FILE)) { + String cache_file = get_cache_file(); + if (!FileAccess::exists(cache_file)) { DirAccessRef d = DirAccess::create(DirAccess::ACCESS_RESOURCES); - d->make_dir_recursive(String(CACHE_FILE).get_base_dir()); //ensure base dir exists + d->make_dir_recursive(String(cache_file).get_base_dir()); //ensure base dir exists } - FileAccessRef f = FileAccess::open(CACHE_FILE, FileAccess::WRITE); + FileAccessRef f = FileAccess::open(cache_file, FileAccess::WRITE); if (!f) { return ERR_CANT_OPEN; } @@ -165,7 +169,7 @@ Error ResourceUID::save_to_cache() { } Error ResourceUID::load_from_cache() { - FileAccessRef f = FileAccess::open(CACHE_FILE, FileAccess::READ); + FileAccessRef f = FileAccess::open(get_cache_file(), FileAccess::READ); if (!f) { return ERR_CANT_OPEN; } @@ -207,7 +211,7 @@ Error ResourceUID::update_cache() { for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) { if (!E.get().saved_to_cache) { if (f == nullptr) { - f = FileAccess::open(CACHE_FILE, FileAccess::READ_WRITE); //append + f = FileAccess::open(get_cache_file(), FileAccess::READ_WRITE); //append if (!f) { return ERR_CANT_OPEN; } diff --git a/core/io/resource_uid.h b/core/io/resource_uid.h index b12138425a..2f1bfdf243 100644 --- a/core/io/resource_uid.h +++ b/core/io/resource_uid.h @@ -44,7 +44,7 @@ public: INVALID_ID = -1 }; - static const char *CACHE_FILE; + static String get_cache_file(); private: mutable Ref<Crypto> crypto; diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 27f8d4e88f..8ab025dda1 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -410,6 +410,63 @@ void StreamPeer::_bind_methods() { //////////////////////////////// +int StreamPeerExtension::get_available_bytes() const { + int count; + if (GDVIRTUAL_CALL(_get_available_bytes, count)) { + return count; + } + WARN_PRINT_ONCE("StreamPeerExtension::_get_available_bytes is unimplemented!"); + return -1; +} + +Error StreamPeerExtension::get_data(uint8_t *r_buffer, int p_bytes) { + int err; + int received = 0; + if (GDVIRTUAL_CALL(_get_data, r_buffer, p_bytes, &received, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("StreamPeerExtension::_get_data is unimplemented!"); + return FAILED; +} + +Error StreamPeerExtension::get_partial_data(uint8_t *r_buffer, int p_bytes, int &r_received) { + int err; + if (GDVIRTUAL_CALL(_get_partial_data, r_buffer, p_bytes, &r_received, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("StreamPeerExtension::_get_partial_data is unimplemented!"); + return FAILED; +} + +Error StreamPeerExtension::put_data(const uint8_t *p_data, int p_bytes) { + int err; + int sent = 0; + if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &sent, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("StreamPeerExtension::_put_data is unimplemented!"); + return FAILED; +} + +Error StreamPeerExtension::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { + int err; + if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &r_sent, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("StreamPeerExtension::_put_partial_data is unimplemented!"); + return FAILED; +} + +void StreamPeerExtension::_bind_methods() { + GDVIRTUAL_BIND(_get_data, "r_buffer", "r_bytes", "r_received"); + GDVIRTUAL_BIND(_get_partial_data, "r_buffer", "r_bytes", "r_received"); + GDVIRTUAL_BIND(_put_data, "p_data", "p_bytes", "r_sent"); + GDVIRTUAL_BIND(_put_partial_data, "p_data", "p_bytes", "r_sent"); + GDVIRTUAL_BIND(_get_available_bytes); +} + +//////////////////////////////// + void StreamPeerBuffer::_bind_methods() { ClassDB::bind_method(D_METHOD("seek", "position"), &StreamPeerBuffer::seek); ClassDB::bind_method(D_METHOD("get_size"), &StreamPeerBuffer::get_size); diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index effc3850af..89432951c5 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -33,6 +33,10 @@ #include "core/object/ref_counted.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" +#include "core/variant/native_ptr.h" + class StreamPeer : public RefCounted { GDCLASS(StreamPeer, RefCounted); OBJ_CATEGORY("Networking"); @@ -58,6 +62,7 @@ public: virtual int get_available_bytes() const = 0; + /* helpers */ void set_big_endian(bool p_big_endian); bool is_big_endian_enabled() const; @@ -92,6 +97,26 @@ public: StreamPeer() {} }; +class StreamPeerExtension : public StreamPeer { + GDCLASS(StreamPeerExtension, StreamPeer); + +protected: + static void _bind_methods(); + +public: + virtual Error put_data(const uint8_t *p_data, int p_bytes) override; + virtual Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override; + virtual Error get_data(uint8_t *p_buffer, int p_bytes) override; + virtual Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override; + virtual int get_available_bytes() const override; + + GDVIRTUAL3R(int, _put_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>); + GDVIRTUAL3R(int, _put_partial_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>); + GDVIRTUAL3R(int, _get_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>); + GDVIRTUAL3R(int, _get_partial_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>); + GDVIRTUAL0RC(int, _get_available_bytes); +}; + class StreamPeerBuffer : public StreamPeer { GDCLASS(StreamPeerBuffer, StreamPeer); diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp index fb4c76aa7a..24808cc8d6 100644 --- a/core/io/zip_io.cpp +++ b/core/io/zip_io.cpp @@ -100,7 +100,7 @@ int zipio_testerror(voidpf opaque, voidpf stream) { } voidpf zipio_alloc(voidpf opaque, uInt items, uInt size) { - voidpf ptr = memalloc(items * size); + voidpf ptr = memalloc((size_t)items * size); memset(ptr, 0, items * size); return ptr; } diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index b380860522..d59dbf1ba8 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -47,8 +47,8 @@ int AStar::get_available_point_id() const { } void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) { - ERR_FAIL_COND(p_id < 0); - ERR_FAIL_COND(p_weight_scale < 1); + ERR_FAIL_COND_MSG(p_id < 0, vformat("Can't add a point with negative id: %d.", p_id)); + ERR_FAIL_COND_MSG(p_weight_scale < 1, vformat("Can't add a point with weight scale less than one: %f.", p_weight_scale)); Point *found_pt; bool p_exists = points.lookup(p_id, found_pt); @@ -72,7 +72,7 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) { Vector3 AStar::get_point_position(int p_id) const { Point *p; bool p_exists = points.lookup(p_id, p); - ERR_FAIL_COND_V(!p_exists, Vector3()); + ERR_FAIL_COND_V_MSG(!p_exists, Vector3(), vformat("Can't get point's position. Point with id: %d doesn't exist.", p_id)); return p->pos; } @@ -80,7 +80,7 @@ Vector3 AStar::get_point_position(int p_id) const { void AStar::set_point_position(int p_id, const Vector3 &p_pos) { Point *p; bool p_exists = points.lookup(p_id, p); - ERR_FAIL_COND(!p_exists); + ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set point's position. Point with id: %d doesn't exist.", p_id)); p->pos = p_pos; } @@ -88,7 +88,7 @@ void AStar::set_point_position(int p_id, const Vector3 &p_pos) { real_t AStar::get_point_weight_scale(int p_id) const { Point *p; bool p_exists = points.lookup(p_id, p); - ERR_FAIL_COND_V(!p_exists, 0); + ERR_FAIL_COND_V_MSG(!p_exists, 0, vformat("Can't get point's weight scale. Point with id: %d doesn't exist.", p_id)); return p->weight_scale; } @@ -96,8 +96,8 @@ real_t AStar::get_point_weight_scale(int p_id) const { void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) { Point *p; bool p_exists = points.lookup(p_id, p); - ERR_FAIL_COND(!p_exists); - ERR_FAIL_COND(p_weight_scale < 1); + ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set point's weight scale. Point with id: %d doesn't exist.", p_id)); + ERR_FAIL_COND_MSG(p_weight_scale < 1, vformat("Can't set point's weight scale less than one: %f.", p_weight_scale)); p->weight_scale = p_weight_scale; } @@ -105,7 +105,7 @@ void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) { void AStar::remove_point(int p_id) { Point *p; bool p_exists = points.lookup(p_id, p); - ERR_FAIL_COND(!p_exists); + ERR_FAIL_COND_MSG(!p_exists, vformat("Can't remove point. Point with id: %d doesn't exist.", p_id)); for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) { Segment s(p_id, (*it.key)); @@ -129,15 +129,15 @@ void AStar::remove_point(int p_id) { } void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) { - ERR_FAIL_COND(p_id == p_with_id); + ERR_FAIL_COND_MSG(p_id == p_with_id, vformat("Can't connect point with id: %d to itself.", p_id)); Point *a; bool from_exists = points.lookup(p_id, a); - ERR_FAIL_COND(!from_exists); + ERR_FAIL_COND_MSG(!from_exists, vformat("Can't connect points. Point with id: %d doesn't exist.", p_id)); Point *b; bool to_exists = points.lookup(p_with_id, b); - ERR_FAIL_COND(!to_exists); + ERR_FAIL_COND_MSG(!to_exists, vformat("Can't connect points. Point with id: %d doesn't exist.", p_with_id)); a->neighbours.set(b->id, b); @@ -169,11 +169,11 @@ void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) { void AStar::disconnect_points(int p_id, int p_with_id, bool bidirectional) { Point *a; bool a_exists = points.lookup(p_id, a); - ERR_FAIL_COND(!a_exists); + ERR_FAIL_COND_MSG(!a_exists, vformat("Can't disconnect points. Point with id: %d doesn't exist.", p_id)); Point *b; bool b_exists = points.lookup(p_with_id, b); - ERR_FAIL_COND(!b_exists); + ERR_FAIL_COND_MSG(!b_exists, vformat("Can't disconnect points. Point with id: %d doesn't exist.", p_with_id)); Segment s(p_id, p_with_id); int remove_direction = bidirectional ? (int)Segment::BIDIRECTIONAL : s.direction; @@ -223,7 +223,7 @@ Array AStar::get_points() { Vector<int> AStar::get_point_connections(int p_id) { Point *p; bool p_exists = points.lookup(p_id, p); - ERR_FAIL_COND_V(!p_exists, Vector<int>()); + ERR_FAIL_COND_V_MSG(!p_exists, Vector<int>(), vformat("Can't get point's connections. Point with id: %d doesn't exist.", p_id)); Vector<int> point_list; @@ -260,8 +260,8 @@ int AStar::get_point_capacity() const { } void AStar::reserve_space(int p_num_nodes) { - ERR_FAIL_COND_MSG(p_num_nodes <= 0, "New capacity must be greater than 0, was: " + itos(p_num_nodes) + "."); - ERR_FAIL_COND_MSG((uint32_t)p_num_nodes < points.get_capacity(), "New capacity must be greater than current capacity: " + itos(points.get_capacity()) + ", new was: " + itos(p_num_nodes) + "."); + ERR_FAIL_COND_MSG(p_num_nodes <= 0, vformat("New capacity must be greater than 0, new was: %d.", p_num_nodes)); + ERR_FAIL_COND_MSG((uint32_t)p_num_nodes < points.get_capacity(), vformat("New capacity must be greater than current capacity: %d, new was: %d.", points.get_capacity(), p_num_nodes)); points.reserve(p_num_nodes); } @@ -389,11 +389,11 @@ real_t AStar::_estimate_cost(int p_from_id, int p_to_id) { Point *from_point; bool from_exists = points.lookup(p_from_id, from_point); - ERR_FAIL_COND_V(!from_exists, 0); + ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_from_id)); Point *to_point; bool to_exists = points.lookup(p_to_id, to_point); - ERR_FAIL_COND_V(!to_exists, 0); + ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_to_id)); return from_point->pos.distance_to(to_point->pos); } @@ -406,11 +406,11 @@ real_t AStar::_compute_cost(int p_from_id, int p_to_id) { Point *from_point; bool from_exists = points.lookup(p_from_id, from_point); - ERR_FAIL_COND_V(!from_exists, 0); + ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_from_id)); Point *to_point; bool to_exists = points.lookup(p_to_id, to_point); - ERR_FAIL_COND_V(!to_exists, 0); + ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_to_id)); return from_point->pos.distance_to(to_point->pos); } @@ -418,11 +418,11 @@ real_t AStar::_compute_cost(int p_from_id, int p_to_id) { Vector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) { Point *a; bool from_exists = points.lookup(p_from_id, a); - ERR_FAIL_COND_V(!from_exists, Vector<Vector3>()); + ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id)); Point *b; bool to_exists = points.lookup(p_to_id, b); - ERR_FAIL_COND_V(!to_exists, Vector<Vector3>()); + ERR_FAIL_COND_V_MSG(!to_exists, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_to_id)); if (a == b) { Vector<Vector3> ret; @@ -467,11 +467,11 @@ Vector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) { Vector<int> AStar::get_id_path(int p_from_id, int p_to_id) { Point *a; bool from_exists = points.lookup(p_from_id, a); - ERR_FAIL_COND_V(!from_exists, Vector<int>()); + ERR_FAIL_COND_V_MSG(!from_exists, Vector<int>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id)); Point *b; bool to_exists = points.lookup(p_to_id, b); - ERR_FAIL_COND_V(!to_exists, Vector<int>()); + ERR_FAIL_COND_V_MSG(!to_exists, Vector<int>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_to_id)); if (a == b) { Vector<int> ret; @@ -516,7 +516,7 @@ Vector<int> AStar::get_id_path(int p_from_id, int p_to_id) { void AStar::set_point_disabled(int p_id, bool p_disabled) { Point *p; bool p_exists = points.lookup(p_id, p); - ERR_FAIL_COND(!p_exists); + ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set if point is disabled. Point with id: %d doesn't exist.", p_id)); p->enabled = !p_disabled; } @@ -524,7 +524,7 @@ void AStar::set_point_disabled(int p_id, bool p_disabled) { bool AStar::is_point_disabled(int p_id) const { Point *p; bool p_exists = points.lookup(p_id, p); - ERR_FAIL_COND_V(!p_exists, false); + ERR_FAIL_COND_V_MSG(!p_exists, false, vformat("Can't get if point is disabled. Point with id: %d doesn't exist.", p_id)); return !p->enabled; } @@ -663,11 +663,11 @@ real_t AStar2D::_estimate_cost(int p_from_id, int p_to_id) { AStar::Point *from_point; bool from_exists = astar.points.lookup(p_from_id, from_point); - ERR_FAIL_COND_V(!from_exists, 0); + ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_from_id)); AStar::Point *to_point; bool to_exists = astar.points.lookup(p_to_id, to_point); - ERR_FAIL_COND_V(!to_exists, 0); + ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_to_id)); return from_point->pos.distance_to(to_point->pos); } @@ -680,11 +680,11 @@ real_t AStar2D::_compute_cost(int p_from_id, int p_to_id) { AStar::Point *from_point; bool from_exists = astar.points.lookup(p_from_id, from_point); - ERR_FAIL_COND_V(!from_exists, 0); + ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_from_id)); AStar::Point *to_point; bool to_exists = astar.points.lookup(p_to_id, to_point); - ERR_FAIL_COND_V(!to_exists, 0); + ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_to_id)); return from_point->pos.distance_to(to_point->pos); } @@ -692,11 +692,11 @@ real_t AStar2D::_compute_cost(int p_from_id, int p_to_id) { Vector<Vector2> AStar2D::get_point_path(int p_from_id, int p_to_id) { AStar::Point *a; bool from_exists = astar.points.lookup(p_from_id, a); - ERR_FAIL_COND_V(!from_exists, Vector<Vector2>()); + ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id)); AStar::Point *b; bool to_exists = astar.points.lookup(p_to_id, b); - ERR_FAIL_COND_V(!to_exists, Vector<Vector2>()); + ERR_FAIL_COND_V_MSG(!to_exists, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_to_id)); if (a == b) { Vector<Vector2> ret; @@ -741,11 +741,11 @@ Vector<Vector2> AStar2D::get_point_path(int p_from_id, int p_to_id) { Vector<int> AStar2D::get_id_path(int p_from_id, int p_to_id) { AStar::Point *a; bool from_exists = astar.points.lookup(p_from_id, a); - ERR_FAIL_COND_V(!from_exists, Vector<int>()); + ERR_FAIL_COND_V_MSG(!from_exists, Vector<int>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id)); AStar::Point *b; bool to_exists = astar.points.lookup(p_to_id, b); - ERR_FAIL_COND_V(!to_exists, Vector<int>()); + ERR_FAIL_COND_V_MSG(!to_exists, Vector<int>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_to_id)); if (a == b) { Vector<int> ret; diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index 33aa65f15d..51a1309f0e 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -52,8 +52,8 @@ void AABB::merge_with(const AABB &p_aabb) { beg_1 = position; beg_2 = p_aabb.position; - end_1 = Vector3(size.x, size.y, size.z) + beg_1; - end_2 = Vector3(p_aabb.size.x, p_aabb.size.y, p_aabb.size.z) + beg_2; + end_1 = size + beg_1; + end_2 = p_aabb.size + beg_2; min.x = (beg_1.x < beg_2.x) ? beg_1.x : beg_2.x; min.y = (beg_1.y < beg_2.y) ? beg_1.y : beg_2.y; diff --git a/core/math/aabb.h b/core/math/aabb.h index e16246902a..97d92fbe37 100644 --- a/core/math/aabb.h +++ b/core/math/aabb.h @@ -118,6 +118,10 @@ public: return position + size; } + _FORCE_INLINE_ Vector3 get_center() const { + return position + (size * 0.5); + } + operator String() const; _FORCE_INLINE_ AABB() {} diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 5c42213e61..a7f89522d7 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -207,6 +207,10 @@ Basis Basis::transposed() const { return tr; } +Basis Basis::from_scale(const Vector3 &p_scale) { + return Basis(p_scale.x, 0, 0, 0, p_scale.y, 0, 0, 0, p_scale.z); +} + // Multiplies the matrix from left by the scaling matrix: M -> S.M // See the comment for Basis::rotated for further explanation. void Basis::scale(const Vector3 &p_scale) { @@ -246,10 +250,7 @@ void Basis::make_scale_uniform() { } Basis Basis::scaled_local(const Vector3 &p_scale) const { - Basis b; - b.set_diagonal(p_scale); - - return (*this) * b; + return (*this) * Basis::from_scale(p_scale); } Vector3 Basis::get_scale_abs() const { @@ -775,7 +776,7 @@ Basis::operator String() const { Quaternion Basis::get_quaternion() const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(!is_rotation(), Quaternion(), "Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quaternion() or call orthonormalized() instead."); + ERR_FAIL_COND_V_MSG(!is_rotation(), Quaternion(), "Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quaternion() or call orthonormalized() if the Basis contains linearly independent vectors."); #endif /* Allow getting a quaternion from an unnormalized transform */ Basis m = *this; @@ -991,21 +992,23 @@ void Basis::set_axis_angle(const Vector3 &p_axis, real_t p_phi) { } void Basis::set_axis_angle_scale(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { - set_diagonal(p_scale); + _set_diagonal(p_scale); rotate(p_axis, p_phi); } void Basis::set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale) { - set_diagonal(p_scale); + _set_diagonal(p_scale); rotate(p_euler); } void Basis::set_quaternion_scale(const Quaternion &p_quaternion, const Vector3 &p_scale) { - set_diagonal(p_scale); + _set_diagonal(p_scale); rotate(p_quaternion); } -void Basis::set_diagonal(const Vector3 &p_diag) { +// This also sets the non-diagonal elements to 0, which is misleading from the +// name, so we want this method to be private. Use `from_scale` externally. +void Basis::_set_diagonal(const Vector3 &p_diag) { elements[0][0] = p_diag.x; elements[0][1] = 0; elements[0][2] = 0; diff --git a/core/math/basis.h b/core/math/basis.h index 9d8ed16e29..eb107d7e4e 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -35,6 +35,9 @@ #include "core/math/vector3.h" class Basis { +private: + void _set_diagonal(const Vector3 &p_diag); + public: Vector3 elements[3] = { Vector3(1, 0, 0), @@ -166,8 +169,6 @@ public: int get_orthogonal_index() const; void set_orthogonal_index(int p_index); - void set_diagonal(const Vector3 &p_diag); - bool is_orthogonal() const; bool is_diagonal() const; bool is_rotation() const; @@ -254,6 +255,7 @@ public: Basis(const Vector3 &p_axis, real_t p_phi) { set_axis_angle(p_axis, p_phi); } Basis(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { set_axis_angle_scale(p_axis, p_phi, p_scale); } + static Basis from_scale(const Vector3 &p_scale); _FORCE_INLINE_ Basis(const Vector3 &row0, const Vector3 &row1, const Vector3 &row2) { elements[0] = row0; diff --git a/core/math/bvh.h b/core/math/bvh.h index cefbc9b0db..65b8b102a3 100644 --- a/core/math/bvh.h +++ b/core/math/bvh.h @@ -200,7 +200,7 @@ public: // use in conjunction with activate if you have deferred the collision check, and // set pairable has never been called. - // (deferred collision checks are a workaround for visual server for historical reasons) + // (deferred collision checks are a workaround for rendering server for historical reasons) void force_collision_check(BVHHandle p_handle) { if (USE_PAIRS) { // the aabb should already be up to date in the BVH diff --git a/core/math/bvh_cull.inc b/core/math/bvh_cull.inc index cba8ea6cb3..d7edc8a884 100644 --- a/core/math/bvh_cull.inc +++ b/core/math/bvh_cull.inc @@ -14,7 +14,7 @@ struct CullParams { uint32_t pairable_type; // optional components for different tests - Vector3 point; + Point point; BVHABB_CLASS abb; typename BVHABB_CLASS::ConvexHull hull; typename BVHABB_CLASS::Segment segment; diff --git a/core/math/bvh_debug.inc b/core/math/bvh_debug.inc index a97304334c..55db794ee3 100644 --- a/core/math/bvh_debug.inc +++ b/core/math/bvh_debug.inc @@ -6,24 +6,21 @@ void _debug_recursive_print_tree(int p_tree_id) const { } String _debug_aabb_to_string(const BVHABB_CLASS &aabb) const { - String sz = "("; - sz += itos(aabb.min.x); - sz += " ~ "; - sz += itos(-aabb.neg_max.x); - sz += ") ("; + Point size = aabb.calculate_size(); - sz += itos(aabb.min.y); - sz += " ~ "; - sz += itos(-aabb.neg_max.y); - sz += ") ("; + String sz; + float vol = 0.0; - sz += itos(aabb.min.z); - sz += " ~ "; - sz += itos(-aabb.neg_max.z); - sz += ") "; + for (int i = 0; i < Point::AXES_COUNT; ++i) { + sz += "("; + sz += itos(aabb.min[i]); + sz += " ~ "; + sz += itos(-aabb.neg_max[i]); + sz += ") "; + + vol += size[i]; + } - Vector3 size = aabb.calculate_size(); - float vol = size.x * size.y * size.z; sz += "vol " + itos(vol); return sz; diff --git a/core/math/bvh_split.inc b/core/math/bvh_split.inc index 3fcc4c7b10..6f54d06ce7 100644 --- a/core/math/bvh_split.inc +++ b/core/math/bvh_split.inc @@ -28,11 +28,15 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u Point centre = full_bound.calculate_centre(); Point size = full_bound.calculate_size(); - int order[3]; + int order[Point::AXIS_COUNT]; order[0] = size.min_axis(); - order[2] = size.max_axis(); - order[1] = 3 - (order[0] + order[2]); + order[Point::AXIS_COUNT - 1] = size.max_axis(); + + static_assert(Point::AXIS_COUNT <= 3); + if (Point::AXIS_COUNT == 3) { + order[1] = 3 - (order[0] + order[2]); + } // simplest case, split on the longest axis int split_axis = order[0]; @@ -54,7 +58,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u // detect when split on longest axis failed int min_threshold = MAX_ITEMS / 4; - int min_group_size[3]; + int min_group_size[Point::AXIS_COUNT]; min_group_size[0] = MIN(num_a, num_b); if (min_group_size[0] < min_threshold) { // slow but sure .. first move everything back into a @@ -64,7 +68,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u num_b = 0; // now calculate the best split - for (int axis = 1; axis < 3; axis++) { + for (int axis = 1; axis < Point::AXIS_COUNT; axis++) { split_axis = order[axis]; int count = 0; @@ -82,7 +86,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u // best axis int best_axis = 0; int best_min = min_group_size[0]; - for (int axis = 1; axis < 3; axis++) { + for (int axis = 1; axis < Point::AXIS_COUNT; axis++) { if (min_group_size[axis] > best_min) { best_min = min_group_size[axis]; best_axis = axis; diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp index 21cb0efe20..f67035c803 100644 --- a/core/math/convex_hull.cpp +++ b/core/math/convex_hull.cpp @@ -2260,10 +2260,21 @@ Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry3 r_mesh.vertices = ch.vertices; - r_mesh.edges.resize(ch.edges.size()); + // Copy the edges over. There's two "half-edges" for every edge, so we pick only one of them. + r_mesh.edges.resize(ch.edges.size() / 2); + uint32_t edges_copied = 0; for (uint32_t i = 0; i < ch.edges.size(); i++) { - r_mesh.edges.write[i].a = (&ch.edges[i])->get_source_vertex(); - r_mesh.edges.write[i].b = (&ch.edges[i])->get_target_vertex(); + uint32_t a = (&ch.edges[i])->get_source_vertex(); + uint32_t b = (&ch.edges[i])->get_target_vertex(); + if (a < b) { // Copy only the "canonical" edge. For the reverse edge, this will be false. + ERR_BREAK(edges_copied >= (uint32_t)r_mesh.edges.size()); + r_mesh.edges.write[edges_copied].a = a; + r_mesh.edges.write[edges_copied].b = b; + edges_copied++; + } + } + if (edges_copied != (uint32_t)r_mesh.edges.size()) { + ERR_PRINT("Invalid edge count."); } r_mesh.faces.resize(ch.faces.size()); diff --git a/core/math/convex_hull.h b/core/math/convex_hull.h index a860d60b02..806c6cc3fb 100644 --- a/core/math/convex_hull.h +++ b/core/math/convex_hull.h @@ -49,7 +49,7 @@ subject to the following restrictions: #include "core/templates/vector.h" /// Convex hull implementation based on Preparata and Hong -/// See https://code.google.com/p/bullet/issues/detail?id=275 +/// See https://code.google.com/archive/p/bullet/issues/275 /// Ole Kniemeyer, MAXON Computer GmbH class ConvexHullComputer { public: diff --git a/core/math/delaunay_2d.h b/core/math/delaunay_2d.h index 95064e5700..2f80cb5634 100644 --- a/core/math/delaunay_2d.h +++ b/core/math/delaunay_2d.h @@ -101,7 +101,7 @@ public: } float delta_max = MAX(rect.size.width, rect.size.height); - Vector2 center = rect.position + rect.size * 0.5; + Vector2 center = rect.get_center(); points.push_back(Vector2(center.x - 20 * delta_max, center.y - delta_max)); points.push_back(Vector2(center.x, center.y + 20 * delta_max)); diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h index d63132b4da..0b6286cd9d 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 https://bulletphysics.org +Copyright (c) 2003-2013 Erwin Coumans http://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/face3.cpp b/core/math/face3.cpp index 9af3f868d2..31a853e1a9 100644 --- a/core/math/face3.cpp +++ b/core/math/face3.cpp @@ -151,8 +151,8 @@ Face3::Side Face3::get_side_of(const Face3 &p_face, ClockDirection p_clock_dir) } Vector3 Face3::get_random_point_inside() const { - real_t a = Math::random(0, 1); - real_t b = Math::random(0, 1); + real_t a = Math::random(0.0, 1.0); + real_t b = Math::random(0.0, 1.0); if (a > b) { SWAP(a, b); } @@ -229,7 +229,7 @@ bool Face3::intersects_aabb(const AABB &p_aabb) const { axis.normalize(); real_t minA, maxA, minB, maxB; - p_aabb.project_range_in_plane(Plane(axis, 0), minA, maxA); + p_aabb.project_range_in_plane(Plane(axis), minA, maxA); project_range(axis, Transform3D(), minB, maxB); if (maxA < minB || maxB < minA) { diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp index 6628b760e0..88d2656025 100644 --- a/core/math/geometry_3d.cpp +++ b/core/math/geometry_3d.cpp @@ -819,11 +819,9 @@ Vector<Plane> Geometry3D::build_sphere_planes(real_t p_radius, int p_lats, int p planes.push_back(Plane(normal, p_radius)); for (int j = 1; j <= p_lats; j++) { - // FIXME: This is stupid. - Vector3 angle = normal.lerp(axis, j / (real_t)p_lats).normalized(); - Vector3 pos = angle * p_radius; - planes.push_back(Plane(pos, angle)); - planes.push_back(Plane(pos * axis_neg, angle * axis_neg)); + Vector3 plane_normal = normal.lerp(axis, j / (real_t)p_lats).normalized(); + planes.push_back(Plane(plane_normal, p_radius)); + planes.push_back(Plane(plane_normal * axis_neg, p_radius)); } } @@ -852,10 +850,10 @@ Vector<Plane> Geometry3D::build_capsule_planes(real_t p_radius, real_t p_height, planes.push_back(Plane(normal, p_radius)); for (int j = 1; j <= p_lats; j++) { - Vector3 angle = normal.lerp(axis, j / (real_t)p_lats).normalized(); - Vector3 pos = axis * p_height * 0.5 + angle * p_radius; - planes.push_back(Plane(pos, angle)); - planes.push_back(Plane(pos * axis_neg, angle * axis_neg)); + Vector3 plane_normal = normal.lerp(axis, j / (real_t)p_lats).normalized(); + Vector3 position = axis * p_height * 0.5 + plane_normal * p_radius; + planes.push_back(Plane(plane_normal, position)); + planes.push_back(Plane(plane_normal * axis_neg, position * axis_neg)); } } diff --git a/core/math/plane.h b/core/math/plane.h index 2267b28c53..18be5d5d12 100644 --- a/core/math/plane.h +++ b/core/math/plane.h @@ -85,8 +85,8 @@ public: normal(p_a, p_b, p_c), d(p_d) {} - _FORCE_INLINE_ Plane(const Vector3 &p_normal, real_t p_d); - _FORCE_INLINE_ Plane(const Vector3 &p_point, const Vector3 &p_normal); + _FORCE_INLINE_ Plane(const Vector3 &p_normal, real_t p_d = 0.0); + _FORCE_INLINE_ Plane(const Vector3 &p_normal, const Vector3 &p_point); _FORCE_INLINE_ Plane(const Vector3 &p_point1, const Vector3 &p_point2, const Vector3 &p_point3, ClockDirection p_dir = CLOCKWISE); }; @@ -109,7 +109,7 @@ Plane::Plane(const Vector3 &p_normal, real_t p_d) : d(p_d) { } -Plane::Plane(const Vector3 &p_point, const Vector3 &p_normal) : +Plane::Plane(const Vector3 &p_normal, const Vector3 &p_point) : normal(p_normal), d(p_normal.dot(p_point)) { } diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index 0960fe19a6..d438a9a377 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -265,8 +265,8 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_ //create new faces from horizon edges List<List<Face>::Element *> new_faces; //new faces - for (Map<Edge, FaceConnect>::Element *E = lit_edges.front(); E; E = E->next()) { - FaceConnect &fc = E->get(); + for (KeyValue<Edge, FaceConnect> &E : lit_edges) { + FaceConnect &fc = E.value; if (fc.left && fc.right) { continue; //edge is uninteresting, not on horizon } @@ -275,8 +275,8 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_ Face face; face.vertices[0] = f.points_over[next]; - face.vertices[1] = E->key().vertices[0]; - face.vertices[2] = E->key().vertices[1]; + face.vertices[1] = E.key.vertices[0]; + face.vertices[2] = E.key.vertices[1]; Plane p(p_points[face.vertices[0]], p_points[face.vertices[1]], p_points[face.vertices[2]]); @@ -418,13 +418,13 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_ } // remove all edge connections to this face - for (Map<Edge, RetFaceConnect>::Element *G = ret_edges.front(); G; G = G->next()) { - if (G->get().left == O) { - G->get().left = nullptr; + for (KeyValue<Edge, RetFaceConnect> &G : ret_edges) { + if (G.value.left == O) { + G.value.left = nullptr; } - if (G->get().right == O) { - G->get().right = nullptr; + if (G.value.right == O) { + G.value.right = nullptr; } } @@ -444,10 +444,10 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_ } r_mesh.edges.resize(ret_edges.size()); idx = 0; - for (Map<Edge, RetFaceConnect>::Element *E = ret_edges.front(); E; E = E->next()) { + for (const KeyValue<Edge, RetFaceConnect> &E : ret_edges) { Geometry3D::MeshData::Edge e; - e.a = E->key().vertices[0]; - e.b = E->key().vertices[1]; + e.a = E.key.vertices[0]; + e.b = E.key.vertices[1]; r_mesh.edges.write[idx++] = e; } diff --git a/core/math/rect2.h b/core/math/rect2.h index ab0b489b4a..2557959fa2 100644 --- a/core/math/rect2.h +++ b/core/math/rect2.h @@ -46,6 +46,8 @@ struct Rect2 { real_t get_area() const { return size.width * size.height; } + _FORCE_INLINE_ Vector2 get_center() const { return position + (size * 0.5); } + inline bool intersects(const Rect2 &p_rect, const bool p_include_borders = false) const { if (p_include_borders) { if (position.x > (p_rect.position.x + p_rect.size.width)) { @@ -259,7 +261,7 @@ struct Rect2 { } _FORCE_INLINE_ bool intersects_filled_polygon(const Vector2 *p_points, int p_point_count) const { - Vector2 center = position + size * 0.5; + Vector2 center = get_center(); int side_plus = 0; int side_minus = 0; Vector2 end = position + size; @@ -344,6 +346,8 @@ struct Rect2i { int get_area() const { return size.width * size.height; } + _FORCE_INLINE_ Vector2i get_center() const { return position + (size / 2); } + inline bool intersects(const Rect2i &p_rect) const { if (position.x > (p_rect.position.x + p_rect.size.width)) { return false; diff --git a/core/math/static_raycaster.cpp b/core/math/static_raycaster.cpp new file mode 100644 index 0000000000..da05d49428 --- /dev/null +++ b/core/math/static_raycaster.cpp @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* static_raycaster.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "static_raycaster.h" + +StaticRaycaster *(*StaticRaycaster::create_function)() = nullptr; + +Ref<StaticRaycaster> StaticRaycaster::create() { + if (create_function) { + return Ref<StaticRaycaster>(create_function()); + } + return Ref<StaticRaycaster>(); +} diff --git a/core/math/static_raycaster.h b/core/math/static_raycaster.h new file mode 100644 index 0000000000..3759c788a7 --- /dev/null +++ b/core/math/static_raycaster.h @@ -0,0 +1,111 @@ +/*************************************************************************/ +/* static_raycaster.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef STATIC_RAYCASTER_H +#define STATIC_RAYCASTER_H + +#include "core/object/ref_counted.h" + +#if !defined(__aligned) + +#if defined(_WIN32) && defined(_MSC_VER) +#define __aligned(...) __declspec(align(__VA_ARGS__)) +#else +#define __aligned(...) __attribute__((aligned(__VA_ARGS__))) +#endif + +#endif + +class StaticRaycaster : public RefCounted { + GDCLASS(StaticRaycaster, RefCounted) +protected: + static StaticRaycaster *(*create_function)(); + +public: + // compatible with embree3 rays + struct __aligned(16) Ray { + const static unsigned int INVALID_GEOMETRY_ID = ((unsigned int)-1); // from rtcore_common.h + + /*! Default construction does nothing. */ + _FORCE_INLINE_ Ray() : + geomID(INVALID_GEOMETRY_ID) {} + + /*! Constructs a ray from origin, direction, and ray segment. Near + * has to be smaller than far. */ + _FORCE_INLINE_ Ray(const Vector3 &org, + const Vector3 &dir, + float tnear = 0.0f, + float tfar = INFINITY) : + org(org), + tnear(tnear), + dir(dir), + time(0.0f), + tfar(tfar), + mask(-1), + u(0.0), + v(0.0), + primID(INVALID_GEOMETRY_ID), + geomID(INVALID_GEOMETRY_ID), + instID(INVALID_GEOMETRY_ID) {} + + /*! Tests if we hit something. */ + _FORCE_INLINE_ explicit operator bool() const { return geomID != INVALID_GEOMETRY_ID; } + + public: + Vector3 org; //!< Ray origin + tnear + float tnear; //!< Start of ray segment + Vector3 dir; //!< Ray direction + tfar + float time; //!< Time of this ray for motion blur. + float tfar; //!< End of ray segment + unsigned int mask; //!< used to mask out objects during traversal + unsigned int id; //!< ray ID + unsigned int flags; //!< ray flags + + Vector3 normal; //!< Not normalized geometry normal + float u; //!< Barycentric u coordinate of hit + float v; //!< Barycentric v coordinate of hit + unsigned int primID; //!< primitive ID + unsigned int geomID; //!< geometry ID + unsigned int instID; //!< instance ID + }; + + virtual bool intersect(Ray &p_ray) = 0; + virtual void intersect(Vector<Ray> &r_rays) = 0; + + virtual void add_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, unsigned int p_id) = 0; + virtual void commit() = 0; + + virtual void set_mesh_filter(const Set<int> &p_mesh_ids) = 0; + virtual void clear_mesh_filter() = 0; + + static Ref<StaticRaycaster> create(); +}; + +#endif // STATIC_RAYCASTER_H diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index 16934d67df..496a557844 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -63,7 +63,7 @@ Transform2D Transform2D::affine_inverse() const { return inv; } -void Transform2D::rotate(real_t p_phi) { +void Transform2D::rotate(const real_t p_phi) { *this = Transform2D(p_phi, Vector2()) * (*this); } @@ -72,7 +72,7 @@ real_t Transform2D::get_skew() const { return Math::acos(elements[0].normalized().dot(SGN(det) * elements[1].normalized())) - Math_PI * 0.5; } -void Transform2D::set_skew(float p_angle) { +void Transform2D::set_skew(const real_t p_angle) { real_t det = basis_determinant(); elements[1] = SGN(det) * elements[0].rotated((Math_PI * 0.5 + p_angle)).normalized() * elements[1].length(); } @@ -81,7 +81,7 @@ real_t Transform2D::get_rotation() const { return Math::atan2(elements[0].y, elements[0].x); } -void Transform2D::set_rotation(real_t p_rot) { +void Transform2D::set_rotation(const real_t p_rot) { Size2 scale = get_scale(); real_t cr = Math::cos(p_rot); real_t sr = Math::sin(p_rot); @@ -92,7 +92,7 @@ void Transform2D::set_rotation(real_t p_rot) { set_scale(scale); } -Transform2D::Transform2D(real_t p_rot, const Vector2 &p_pos) { +Transform2D::Transform2D(const real_t p_rot, const Vector2 &p_pos) { real_t cr = Math::cos(p_rot); real_t sr = Math::sin(p_rot); elements[0][0] = cr; @@ -102,6 +102,14 @@ Transform2D::Transform2D(real_t p_rot, const Vector2 &p_pos) { elements[2] = p_pos; } +Transform2D::Transform2D(const real_t p_rot, const Size2 &p_scale, const real_t p_skew, const Vector2 &p_pos) { + elements[0][0] = Math::cos(p_rot) * p_scale.x; + elements[1][1] = Math::cos(p_rot + p_skew) * p_scale.y; + elements[1][0] = -Math::sin(p_rot + p_skew) * p_scale.y; + elements[0][1] = Math::sin(p_rot) * p_scale.x; + elements[2] = p_pos; +} + Size2 Transform2D::get_scale() const { real_t det_sign = SGN(basis_determinant()); return Size2(elements[0].length(), det_sign * elements[1].length()); @@ -126,7 +134,7 @@ void Transform2D::scale_basis(const Size2 &p_scale) { elements[1][1] *= p_scale.y; } -void Transform2D::translate(real_t p_tx, real_t p_ty) { +void Transform2D::translate(const real_t p_tx, const real_t p_ty) { translate(Vector2(p_tx, p_ty)); } @@ -231,7 +239,7 @@ Transform2D Transform2D::translated(const Vector2 &p_offset) const { return copy; } -Transform2D Transform2D::rotated(real_t p_phi) const { +Transform2D Transform2D::rotated(const real_t p_phi) const { Transform2D copy = *this; copy.rotate(p_phi); return copy; @@ -241,7 +249,7 @@ real_t Transform2D::basis_determinant() const { return elements[0].x * elements[1].y - elements[0].y * elements[1].x; } -Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, real_t p_c) const { +Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, const real_t p_c) const { //extract parameters Vector2 p1 = get_origin(); Vector2 p2 = p_transform.get_origin(); @@ -271,7 +279,7 @@ Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, real_t } //construct matrix - Transform2D res(Math::atan2(v.y, v.x), p1.lerp(p2, p_c)); + Transform2D res(v.angle(), p1.lerp(p2, p_c)); res.scale_basis(s1.lerp(s2, p_c)); return res; } diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index 34cfd0c1a9..6ed3af2ba7 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -68,17 +68,17 @@ struct Transform2D { void affine_invert(); Transform2D affine_inverse() const; - void set_rotation(real_t p_rot); + void set_rotation(const real_t p_rot); real_t get_rotation() const; real_t get_skew() const; - void set_skew(float p_angle); - _FORCE_INLINE_ void set_rotation_and_scale(real_t p_rot, const Size2 &p_scale); - _FORCE_INLINE_ void set_rotation_scale_and_skew(real_t p_rot, const Size2 &p_scale, float p_skew); - void rotate(real_t p_phi); + void set_skew(const real_t p_angle); + _FORCE_INLINE_ void set_rotation_and_scale(const real_t p_rot, const Size2 &p_scale); + _FORCE_INLINE_ void set_rotation_scale_and_skew(const real_t p_rot, const Size2 &p_scale, const real_t p_skew); + void rotate(const real_t p_phi); void scale(const Size2 &p_scale); void scale_basis(const Size2 &p_scale); - void translate(real_t p_tx, real_t p_ty); + void translate(const real_t p_tx, const real_t p_ty); void translate(const Vector2 &p_translation); real_t basis_determinant() const; @@ -92,7 +92,7 @@ struct Transform2D { Transform2D scaled(const Size2 &p_scale) const; Transform2D basis_scaled(const Size2 &p_scale) const; Transform2D translated(const Vector2 &p_offset) const; - Transform2D rotated(real_t p_phi) const; + Transform2D rotated(const real_t p_phi) const; Transform2D untranslated() const; @@ -110,7 +110,7 @@ struct Transform2D { void operator*=(const real_t p_val); Transform2D operator*(const real_t p_val) const; - Transform2D interpolate_with(const Transform2D &p_transform, real_t p_c) const; + Transform2D interpolate_with(const Transform2D &p_transform, const real_t p_c) const; _FORCE_INLINE_ Vector2 basis_xform(const Vector2 &p_vec) const; _FORCE_INLINE_ Vector2 basis_xform_inv(const Vector2 &p_vec) const; @@ -123,7 +123,7 @@ struct Transform2D { operator String() const; - Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) { + Transform2D(const real_t xx, const real_t xy, const real_t yx, const real_t yy, const real_t ox, const real_t oy) { elements[0][0] = xx; elements[0][1] = xy; elements[1][0] = yx; @@ -138,7 +138,10 @@ struct Transform2D { elements[2] = p_origin; } - Transform2D(real_t p_rot, const Vector2 &p_pos); + Transform2D(const real_t p_rot, const Vector2 &p_pos); + + Transform2D(const real_t p_rot, const Size2 &p_scale, const real_t p_skew, const Vector2 &p_pos); + Transform2D() { elements[0][0] = 1.0; elements[1][1] = 1.0; @@ -185,14 +188,14 @@ Rect2 Transform2D::xform(const Rect2 &p_rect) const { return new_rect; } -void Transform2D::set_rotation_and_scale(real_t p_rot, const Size2 &p_scale) { +void Transform2D::set_rotation_and_scale(const real_t p_rot, const Size2 &p_scale) { elements[0][0] = Math::cos(p_rot) * p_scale.x; elements[1][1] = Math::cos(p_rot) * p_scale.y; elements[1][0] = -Math::sin(p_rot) * p_scale.y; elements[0][1] = Math::sin(p_rot) * p_scale.x; } -void Transform2D::set_rotation_scale_and_skew(real_t p_rot, const Size2 &p_scale, float p_skew) { +void Transform2D::set_rotation_scale_and_skew(const real_t p_rot, const Size2 &p_scale, const real_t p_skew) { elements[0][0] = Math::cos(p_rot) * p_scale.x; elements[1][1] = Math::cos(p_rot + p_skew) * p_scale.y; elements[1][0] = -Math::sin(p_rot + p_skew) * p_scale.y; diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp index bf06c848c5..2f3da0b6a8 100644 --- a/core/math/triangle_mesh.cpp +++ b/core/math/triangle_mesh.cpp @@ -76,7 +76,7 @@ int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, in int index = r_max_alloc++; BVH *_new = &p_bvh[index]; _new->aabb = aabb; - _new->center = aabb.position + aabb.size * 0.5; + _new->center = aabb.get_center(); _new->face_index = -1; _new->left = left; _new->right = right; @@ -152,13 +152,13 @@ void TriangleMesh::create(const Vector<Vector3> &p_faces) { bw[i].left = -1; bw[i].right = -1; bw[i].face_index = i; - bw[i].center = bw[i].aabb.position + bw[i].aabb.size * 0.5; + bw[i].center = bw[i].aabb.get_center(); } vertices.resize(db.size()); Vector3 *vw = vertices.ptrw(); - for (Map<Vector3, int>::Element *E = db.front(); E; E = E->next()) { - vw[E->get()] = E->key(); + for (const KeyValue<Vector3, int> &E : db) { + vw[E.value] = E.key; } } diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h index 463b0dd5c8..2d3b4db4bb 100644 --- a/core/math/triangle_mesh.h +++ b/core/math/triangle_mesh.h @@ -37,11 +37,13 @@ class TriangleMesh : public RefCounted { GDCLASS(TriangleMesh, RefCounted); +public: struct Triangle { Vector3 normal; int indices[3]; }; +private: Vector<Triangle> triangles; Vector<Vector3> vertices; @@ -86,8 +88,8 @@ public: Vector3 get_area_normal(const AABB &p_aabb) const; Vector<Face3> get_faces() const; - Vector<Triangle> get_triangles() const { return triangles; } - Vector<Vector3> get_vertices() const { return vertices; } + const Vector<Triangle> &get_triangles() const { return triangles; } + const Vector<Vector3> &get_vertices() const { return vertices; } void get_indices(Vector<int> *r_triangles_indices) const; void create(const Vector<Vector3> &p_faces); diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index b53dc05a00..16e43d7d06 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -79,7 +79,7 @@ real_t Vector2::angle_to(const Vector2 &p_vector2) const { } real_t Vector2::angle_to_point(const Vector2 &p_vector2) const { - return Math::atan2(y - p_vector2.y, x - p_vector2.x); + return (*this - p_vector2).angle(); } real_t Vector2::dot(const Vector2 &p_other) const { diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index 401c3ccd9c..fa212c178a 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -115,12 +115,6 @@ Basis Vector3::outer(const Vector3 &p_b) const { return Basis(row0, row1, row2); } -Basis Vector3::to_diagonal_matrix() const { - return Basis(x, 0, 0, - 0, y, 0, - 0, 0, z); -} - bool Vector3::is_equal_approx(const Vector3 &p_v) const { return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z); } diff --git a/core/math/vector3.h b/core/math/vector3.h index 6a4c42f41b..e65ac31c02 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -106,7 +106,6 @@ struct Vector3 { _FORCE_INLINE_ Vector3 cross(const Vector3 &p_b) const; _FORCE_INLINE_ real_t dot(const Vector3 &p_b) const; Basis outer(const Vector3 &p_b) const; - Basis to_diagonal_matrix() const; _FORCE_INLINE_ Vector3 abs() const; _FORCE_INLINE_ Vector3 floor() const; diff --git a/core/multiplayer/SCsub b/core/multiplayer/SCsub new file mode 100644 index 0000000000..19a6549225 --- /dev/null +++ b/core/multiplayer/SCsub @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +Import("env") + +env.add_source_files(env.core_sources, "*.cpp") diff --git a/core/io/multiplayer_peer.h b/core/multiplayer/multiplayer.h index 7ca4e7930b..be398f02c8 100644 --- a/core/io/multiplayer_peer.h +++ b/core/multiplayer/multiplayer.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* multiplayer_peer.h */ +/* multiplayer.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,58 +28,53 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef NETWORKED_MULTIPLAYER_PEER_H -#define NETWORKED_MULTIPLAYER_PEER_H +#ifndef MULTIPLAYER_H +#define MULTIPLAYER_H -#include "core/io/packet_peer.h" +#include "core/variant/binder_common.h" -class MultiplayerPeer : public PacketPeer { - GDCLASS(MultiplayerPeer, PacketPeer); +#include "core/string/string_name.h" -protected: - static void _bind_methods(); +namespace Multiplayer { -public: - enum { - TARGET_PEER_BROADCAST = 0, - TARGET_PEER_SERVER = 1 - }; - enum TransferMode { - TRANSFER_MODE_UNRELIABLE, - TRANSFER_MODE_UNRELIABLE_ORDERED, - TRANSFER_MODE_RELIABLE, - }; - - enum ConnectionStatus { - CONNECTION_DISCONNECTED, - CONNECTION_CONNECTING, - CONNECTION_CONNECTED, - }; - - virtual void set_transfer_channel(int p_channel) = 0; - virtual int get_transfer_channel() const = 0; - virtual void set_transfer_mode(TransferMode p_mode) = 0; - virtual TransferMode get_transfer_mode() const = 0; - virtual void set_target_peer(int p_peer_id) = 0; - - virtual int get_packet_peer() const = 0; +enum TransferMode { + TRANSFER_MODE_UNRELIABLE, + TRANSFER_MODE_UNRELIABLE_ORDERED, + TRANSFER_MODE_RELIABLE +}; - virtual bool is_server() const = 0; +enum RPCMode { + RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default) + RPC_MODE_ANY_PEER, // Any peer can call this RPC + RPC_MODE_AUTHORITY, // / Only the node's multiplayer authority (server by default) can call this RPC +}; - virtual void poll() = 0; +struct RPCConfig { + StringName name; + RPCMode rpc_mode = RPC_MODE_DISABLED; + bool call_local = false; + TransferMode transfer_mode = TRANSFER_MODE_RELIABLE; + int channel = 0; - virtual int get_unique_id() const = 0; + bool operator==(RPCConfig const &p_other) const { + return name == p_other.name; + } +}; - virtual void set_refuse_new_connections(bool p_enable) = 0; - virtual bool is_refusing_new_connections() const = 0; +struct SortRPCConfig { + StringName::AlphCompare compare; + bool operator()(const RPCConfig &p_a, const RPCConfig &p_b) const { + return compare(p_a.name, p_b.name); + } +}; - virtual ConnectionStatus get_connection_status() const = 0; - uint32_t generate_unique_id() const; +}; // namespace Multiplayer - MultiplayerPeer() {} -}; +// This is needed for proper docs generation (i.e. not "Multiplayer."-prefixed). +typedef Multiplayer::RPCMode RPCMode; +typedef Multiplayer::TransferMode TransferMode; -VARIANT_ENUM_CAST(MultiplayerPeer::TransferMode) -VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus) +VARIANT_ENUM_CAST(RPCMode); +VARIANT_ENUM_CAST(TransferMode); -#endif // NETWORKED_MULTIPLAYER_PEER_H +#endif // MULTIPLAYER_H diff --git a/core/multiplayer/multiplayer_api.cpp b/core/multiplayer/multiplayer_api.cpp new file mode 100644 index 0000000000..9543f77c1e --- /dev/null +++ b/core/multiplayer/multiplayer_api.cpp @@ -0,0 +1,668 @@ +/*************************************************************************/ +/* multiplayer_api.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "multiplayer_api.h" + +#include "core/debugger/engine_debugger.h" +#include "core/io/marshalls.h" +#include "core/multiplayer/multiplayer_replicator.h" +#include "core/multiplayer/rpc_manager.h" +#include "scene/main/node.h" + +#include <stdint.h> + +#ifdef DEBUG_ENABLED +#include "core/os/os.h" +#endif + +#ifdef DEBUG_ENABLED +void MultiplayerAPI::profile_bandwidth(const String &p_inout, int p_size) { + if (EngineDebugger::is_profiling("multiplayer")) { + Array values; + values.push_back("bandwidth"); + values.push_back(p_inout); + values.push_back(OS::get_singleton()->get_ticks_msec()); + values.push_back(p_size); + EngineDebugger::profiler_add_frame_data("multiplayer", values); + } +} +#endif + +void MultiplayerAPI::poll() { + if (!multiplayer_peer.is_valid() || multiplayer_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED) { + return; + } + + multiplayer_peer->poll(); + + if (!multiplayer_peer.is_valid()) { // It's possible that polling might have resulted in a disconnection, so check here. + return; + } + + while (multiplayer_peer->get_available_packet_count()) { + int sender = multiplayer_peer->get_packet_peer(); + const uint8_t *packet; + int len; + + Error err = multiplayer_peer->get_packet(&packet, len); + if (err != OK) { + ERR_PRINT("Error getting packet!"); + break; // Something is wrong! + } + + remote_sender_id = sender; + _process_packet(sender, packet, len); + remote_sender_id = 0; + + if (!multiplayer_peer.is_valid()) { + break; // It's also possible that a packet or RPC caused a disconnection, so also check here. + } + } + if (multiplayer_peer.is_valid() && multiplayer_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTED) { + replicator->poll(); + } +} + +void MultiplayerAPI::clear() { + replicator->clear(); + connected_peers.clear(); + path_get_cache.clear(); + path_send_cache.clear(); + packet_cache.clear(); + last_send_cache_id = 1; +} + +void MultiplayerAPI::set_root_node(Node *p_node) { + root_node = p_node; +} + +Node *MultiplayerAPI::get_root_node() { + return root_node; +} + +void MultiplayerAPI::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) { + if (p_peer == multiplayer_peer) { + return; // Nothing to do + } + + ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, + "Supplied MultiplayerPeer must be connecting or connected."); + + if (multiplayer_peer.is_valid()) { + multiplayer_peer->disconnect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); + multiplayer_peer->disconnect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer)); + multiplayer_peer->disconnect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server)); + multiplayer_peer->disconnect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); + multiplayer_peer->disconnect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); + clear(); + } + + multiplayer_peer = p_peer; + + if (multiplayer_peer.is_valid()) { + multiplayer_peer->connect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); + multiplayer_peer->connect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer)); + multiplayer_peer->connect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server)); + multiplayer_peer->connect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); + multiplayer_peer->connect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); + } +} + +Ref<MultiplayerPeer> MultiplayerAPI::get_multiplayer_peer() const { + return multiplayer_peer; +} + +void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_FAIL_COND_MSG(root_node == nullptr, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it."); + ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small."); + +#ifdef DEBUG_ENABLED + profile_bandwidth("in", p_packet_len); +#endif + + // Extract the `packet_type` from the LSB three bits: + uint8_t packet_type = p_packet[0] & CMD_MASK; + + switch (packet_type) { + case NETWORK_COMMAND_SIMPLIFY_PATH: { + _process_simplify_path(p_from, p_packet, p_packet_len); + } break; + + case NETWORK_COMMAND_CONFIRM_PATH: { + _process_confirm_path(p_from, p_packet, p_packet_len); + } break; + + case NETWORK_COMMAND_REMOTE_CALL: { + rpc_manager->process_rpc(p_from, p_packet, p_packet_len); + } break; + + case NETWORK_COMMAND_RAW: { + _process_raw(p_from, p_packet, p_packet_len); + } break; + case NETWORK_COMMAND_SPAWN: { + replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, true); + } break; + case NETWORK_COMMAND_DESPAWN: { + replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, false); + } break; + case NETWORK_COMMAND_SYNC: { + replicator->process_sync(p_from, p_packet, p_packet_len); + } break; + } +} + +void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small."); + int ofs = 1; + + String methods_md5; + methods_md5.parse_utf8((const char *)(p_packet + ofs), 32); + ofs += 33; + + int id = decode_uint32(&p_packet[ofs]); + ofs += 4; + + String paths; + paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs); + + NodePath path = paths; + + if (!path_get_cache.has(p_from)) { + path_get_cache[p_from] = PathGetCache(); + } + + Node *node = root_node->get_node(path); + ERR_FAIL_COND(node == nullptr); + const bool valid_rpc_checksum = rpc_manager->get_rpc_md5(node) == methods_md5; + if (valid_rpc_checksum == false) { + ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); + } + + PathGetCache::NodeInfo ni; + ni.path = path; + + path_get_cache[p_from].nodes[id] = ni; + + // Encode path to send ack. + CharString pname = String(path).utf8(); + int len = encode_cstring(pname.get_data(), nullptr); + + Vector<uint8_t> packet; + + packet.resize(1 + 1 + len); + packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH; + packet.write[1] = valid_rpc_checksum; + encode_cstring(pname.get_data(), &packet.write[2]); + + multiplayer_peer->set_transfer_channel(0); + multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); + multiplayer_peer->set_target_peer(p_from); + multiplayer_peer->put_packet(packet.ptr(), packet.size()); +} + +void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small."); + + const bool valid_rpc_checksum = p_packet[1]; + + String paths; + paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2); + + NodePath path = paths; + + if (valid_rpc_checksum == false) { + ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); + } + + PathSentCache *psc = path_send_cache.getptr(path); + ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache."); + + Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from); + ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path."); + E->get() = true; +} + +bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) { + bool has_all_peers = true; + List<int> peers_to_add; // If one is missing, take note to add it. + + for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { + if (p_target < 0 && E->get() == -p_target) { + continue; // Continue, excluded. + } + + if (p_target > 0 && E->get() != p_target) { + continue; // Continue, not for this peer. + } + + Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); + + if (!F || !F->get()) { + // Path was not cached, or was cached but is unconfirmed. + if (!F) { + // Not cached at all, take note. + peers_to_add.push_back(E->get()); + } + + has_all_peers = false; + } + } + + if (peers_to_add.size() > 0) { + // Those that need to be added, send a message for this. + + // Encode function name. + const CharString path = String(p_path).utf8(); + const int path_len = encode_cstring(path.get_data(), nullptr); + + // Extract MD5 from rpc methods list. + const String methods_md5 = rpc_manager->get_rpc_md5(p_node); + const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder. + + Vector<uint8_t> packet; + packet.resize(1 + 4 + path_len + methods_md5_len); + int ofs = 0; + + packet.write[ofs] = NETWORK_COMMAND_SIMPLIFY_PATH; + ofs += 1; + + ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]); + + ofs += encode_uint32(psc->id, &packet.write[ofs]); + + ofs += encode_cstring(path.get_data(), &packet.write[ofs]); + + for (int &E : peers_to_add) { + multiplayer_peer->set_target_peer(E); // To all of you. + multiplayer_peer->set_transfer_channel(0); + multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); + multiplayer_peer->put_packet(packet.ptr(), packet.size()); + + psc->confirmed_peers.insert(E, false); // Insert into confirmed, but as false since it was not confirmed. + } + } + + return has_all_peers; +} + +// The variant is compressed and encoded; The first byte contains all the meta +// information and the format is: +// - The first LSB 5 bits are used for the variant type. +// - The next two bits are used to store the encoding mode. +// - The most significant is used to store the boolean value. +#define VARIANT_META_TYPE_MASK 0x1F +#define VARIANT_META_EMODE_MASK 0x60 +#define VARIANT_META_BOOL_MASK 0x80 +#define ENCODE_8 0 << 5 +#define ENCODE_16 1 << 5 +#define ENCODE_32 2 << 5 +#define ENCODE_64 3 << 5 +Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) { + // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31 + CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK); + + uint8_t *buf = r_buffer; + r_len = 0; + uint8_t encode_mode = 0; + + switch (p_variant.get_type()) { + case Variant::BOOL: { + if (buf) { + // We still have 1 free bit in the meta, so let's use it. + buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0; + buf[0] |= encode_mode | p_variant.get_type(); + } + r_len += 1; + } break; + case Variant::INT: { + if (buf) { + // Reserve the first byte for the meta. + buf += 1; + } + r_len += 1; + int64_t val = p_variant; + if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) { + // Use 8 bit + encode_mode = ENCODE_8; + if (buf) { + buf[0] = val; + } + r_len += 1; + } else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) { + // Use 16 bit + encode_mode = ENCODE_16; + if (buf) { + encode_uint16(val, buf); + } + r_len += 2; + } else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) { + // Use 32 bit + encode_mode = ENCODE_32; + if (buf) { + encode_uint32(val, buf); + } + r_len += 4; + } else { + // Use 64 bit + encode_mode = ENCODE_64; + if (buf) { + encode_uint64(val, buf); + } + r_len += 8; + } + // Store the meta + if (buf) { + buf -= 1; + buf[0] = encode_mode | p_variant.get_type(); + } + } break; + default: + // Any other case is not yet compressed. + Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding); + if (err != OK) { + return err; + } + if (r_buffer) { + // The first byte is not used by the marshalling, so store the type + // so we know how to decompress and decode this variant. + r_buffer[0] = p_variant.get_type(); + } + } + + return OK; +} + +Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) { + const uint8_t *buf = p_buffer; + int len = p_len; + + ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); + uint8_t type = buf[0] & VARIANT_META_TYPE_MASK; + uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK; + + ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA); + + switch (type) { + case Variant::BOOL: { + bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0; + r_variant = val; + if (r_len) { + *r_len = 1; + } + } break; + case Variant::INT: { + buf += 1; + len -= 1; + if (r_len) { + *r_len = 1; + } + if (encode_mode == ENCODE_8) { + // 8 bits. + ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); + int8_t val = buf[0]; + r_variant = val; + if (r_len) { + (*r_len) += 1; + } + } else if (encode_mode == ENCODE_16) { + // 16 bits. + ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA); + int16_t val = decode_uint16(buf); + r_variant = val; + if (r_len) { + (*r_len) += 2; + } + } else if (encode_mode == ENCODE_32) { + // 32 bits. + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); + int32_t val = decode_uint32(buf); + r_variant = val; + if (r_len) { + (*r_len) += 4; + } + } else { + // 64 bits. + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); + int64_t val = decode_uint64(buf); + r_variant = val; + if (r_len) { + (*r_len) += 8; + } + } + } break; + default: + Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding); + if (err != OK) { + return err; + } + } + + return OK; +} + +void MultiplayerAPI::_add_peer(int p_id) { + connected_peers.insert(p_id); + path_get_cache.insert(p_id, PathGetCache()); + if (is_server()) { + replicator->spawn_all(p_id); + } + emit_signal(SNAME("peer_connected"), p_id); +} + +void MultiplayerAPI::_del_peer(int p_id) { + connected_peers.erase(p_id); + // Cleanup get cache. + path_get_cache.erase(p_id); + // Cleanup sent cache. + // Some refactoring is needed to make this faster and do paths GC. + List<NodePath> keys; + path_send_cache.get_key_list(&keys); + for (const NodePath &E : keys) { + PathSentCache *psc = path_send_cache.getptr(E); + psc->confirmed_peers.erase(p_id); + } + emit_signal(SNAME("peer_disconnected"), p_id); +} + +void MultiplayerAPI::_connected_to_server() { + emit_signal(SNAME("connected_to_server")); +} + +void MultiplayerAPI::_connection_failed() { + emit_signal(SNAME("connection_failed")); +} + +void MultiplayerAPI::_server_disconnected() { + emit_signal(SNAME("server_disconnected")); +} + +Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, Multiplayer::TransferMode p_mode, int p_channel) { + ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet."); + ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no multiplayer peer is active."); + ERR_FAIL_COND_V_MSG(multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a multiplayer peer which is not connected."); + + if (packet_cache.size() < p_data.size() + 1) { + packet_cache.resize(p_data.size() + 1); + } + + const uint8_t *r = p_data.ptr(); + packet_cache.write[0] = NETWORK_COMMAND_RAW; + memcpy(&packet_cache.write[1], &r[0], p_data.size()); + + multiplayer_peer->set_target_peer(p_to); + multiplayer_peer->set_transfer_channel(p_channel); + multiplayer_peer->set_transfer_mode(p_mode); + + return multiplayer_peer->put_packet(packet_cache.ptr(), p_data.size() + 1); +} + +void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small."); + + Vector<uint8_t> out; + int len = p_packet_len - 1; + out.resize(len); + { + uint8_t *w = out.ptrw(); + memcpy(&w[0], &p_packet[1], len); + } + emit_signal(SNAME("peer_packet"), p_from, out); +} + +bool MultiplayerAPI::is_cache_confirmed(NodePath p_path, int p_peer) { + const PathSentCache *psc = path_send_cache.getptr(p_path); + ERR_FAIL_COND_V(!psc, false); + const Map<int, bool>::Element *F = psc->confirmed_peers.find(p_peer); + ERR_FAIL_COND_V(!F, false); // Should never happen. + return F->get(); +} + +bool MultiplayerAPI::send_confirm_path(Node *p_node, NodePath p_path, int p_peer_id, int &r_id) { + // See if the path is cached. + PathSentCache *psc = path_send_cache.getptr(p_path); + if (!psc) { + // Path is not cached, create. + path_send_cache[p_path] = PathSentCache(); + psc = path_send_cache.getptr(p_path); + psc->id = last_send_cache_id++; + } + r_id = psc->id; + + // See if all peers have cached path (if so, call can be fast). + return _send_confirm_path(p_node, p_path, psc, p_peer_id); +} + +Node *MultiplayerAPI::get_cached_node(int p_from, uint32_t p_node_id) { + Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from); + ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d.", p_from)); + + Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(p_node_id); + ERR_FAIL_COND_V_MSG(!F, nullptr, vformat("ID %d not found in cache of peer %d.", p_node_id, p_from)); + + PathGetCache::NodeInfo *ni = &F->get(); + Node *node = root_node->get_node(ni->path); + if (!node) { + ERR_PRINT("Failed to get cached path: " + String(ni->path) + "."); + } + return node; +} + +int MultiplayerAPI::get_unique_id() const { + ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), 0, "No multiplayer peer is assigned. Unable to get unique ID."); + return multiplayer_peer->get_unique_id(); +} + +bool MultiplayerAPI::is_server() const { + return multiplayer_peer.is_valid() && multiplayer_peer->is_server(); +} + +void MultiplayerAPI::set_refuse_new_connections(bool p_refuse) { + ERR_FAIL_COND_MSG(!multiplayer_peer.is_valid(), "No multiplayer peer is assigned. Unable to set 'refuse_new_connections'."); + multiplayer_peer->set_refuse_new_connections(p_refuse); +} + +bool MultiplayerAPI::is_refusing_new_connections() const { + ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), false, "No multiplayer peer is assigned. Unable to get 'refuse_new_connections'."); + return multiplayer_peer->is_refusing_new_connections(); +} + +Vector<int> MultiplayerAPI::get_peer_ids() const { + ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), Vector<int>(), "No multiplayer peer is assigned. Assume no peers are connected."); + + Vector<int> ret; + for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { + ret.push_back(E->get()); + } + + return ret; +} + +void MultiplayerAPI::set_allow_object_decoding(bool p_enable) { + allow_object_decoding = p_enable; +} + +bool MultiplayerAPI::is_object_decoding_allowed() const { + return allow_object_decoding; +} + +void MultiplayerAPI::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) { + replicator->scene_enter_exit_notify(p_scene, p_node, p_enter); +} + +void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { + rpc_manager->rpcp(p_node, p_peer_id, p_method, p_arg, p_argcount); +} + +void MultiplayerAPI::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); + ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node); + ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &MultiplayerAPI::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("has_multiplayer_peer"), &MultiplayerAPI::has_multiplayer_peer); + ClassDB::bind_method(D_METHOD("get_multiplayer_peer"), &MultiplayerAPI::get_multiplayer_peer); + ClassDB::bind_method(D_METHOD("set_multiplayer_peer", "peer"), &MultiplayerAPI::set_multiplayer_peer); + ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerAPI::get_unique_id); + ClassDB::bind_method(D_METHOD("is_server"), &MultiplayerAPI::is_server); + ClassDB::bind_method(D_METHOD("get_remote_sender_id"), &MultiplayerAPI::get_remote_sender_id); + ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll); + ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear); + + ClassDB::bind_method(D_METHOD("get_peers"), &MultiplayerAPI::get_peer_ids); + ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "refuse"), &MultiplayerAPI::set_refuse_new_connections); + ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerAPI::is_refusing_new_connections); + ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding); + ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed); + ClassDB::bind_method(D_METHOD("get_replicator"), &MultiplayerAPI::get_replicator); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_multiplayer_peer", "get_multiplayer_peer"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_root_node", "get_root_node"); + ADD_PROPERTY_DEFAULT("refuse_new_connections", false); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replicator", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerReplicator", PROPERTY_USAGE_NONE), "", "get_replicator"); + + ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet"))); + ADD_SIGNAL(MethodInfo("connected_to_server")); + ADD_SIGNAL(MethodInfo("connection_failed")); + ADD_SIGNAL(MethodInfo("server_disconnected")); +} + +MultiplayerAPI::MultiplayerAPI() { + replicator = memnew(MultiplayerReplicator(this)); + rpc_manager = memnew(RPCManager(this)); + clear(); +} + +MultiplayerAPI::~MultiplayerAPI() { + clear(); + memdelete(replicator); + memdelete(rpc_manager); +} diff --git a/core/io/multiplayer_api.h b/core/multiplayer/multiplayer_api.h index 3c96a3eed1..1fb0318403 100644 --- a/core/io/multiplayer_api.h +++ b/core/multiplayer/multiplayer_api.h @@ -31,41 +31,17 @@ #ifndef MULTIPLAYER_API_H #define MULTIPLAYER_API_H -#include "core/io/multiplayer_peer.h" -#include "core/io/resource_uid.h" +#include "core/multiplayer/multiplayer.h" +#include "core/multiplayer/multiplayer_peer.h" #include "core/object/ref_counted.h" class MultiplayerReplicator; +class RPCManager; class MultiplayerAPI : public RefCounted { GDCLASS(MultiplayerAPI, RefCounted); public: - enum RPCMode { - RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default) - RPC_MODE_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 { - StringName name; - RPCMode rpc_mode = RPC_MODE_DISABLED; - bool sync = false; - MultiplayerPeer::TransferMode transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE; - int channel = 0; - - bool operator==(RPCConfig const &p_other) const { - return name == p_other.name; - } - }; - - struct SortRPCConfig { - StringName::AlphCompare compare; - bool operator()(const RPCConfig &p_a, const RPCConfig &p_b) const { - return compare(p_a.name, p_b.name); - } - }; - enum NetworkCommands { NETWORK_COMMAND_REMOTE_CALL = 0, NETWORK_COMMAND_SIMPLIFY_PATH, @@ -73,24 +49,20 @@ public: 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. + NETWORK_COMMAND_SYNC, }; - 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, + // For each command, the 4 MSB can contain custom flags, as defined by subsystems. + enum { + CMD_FLAG_0_SHIFT = 4, + CMD_FLAG_1_SHIFT = 5, + CMD_FLAG_2_SHIFT = 6, + CMD_FLAG_3_SHIFT = 7, }; + // This is the mask that will be used to extract the command. enum { - NODE_ID_COMPRESSION_SHIFT = 3, - NAME_ID_COMPRESSION_SHIFT = 5, - BYTE_ONLY_OR_NO_ARGS_SHIFT = 6, + CMD_MASK = 7, // 0x7 -> 0b00001111 }; private: @@ -110,49 +82,52 @@ private: Map<int, NodeInfo> nodes; }; - Ref<MultiplayerPeer> network_peer; - int rpc_sender_id = 0; + Ref<MultiplayerPeer> multiplayer_peer; Set<int> connected_peers; + int remote_sender_id = 0; + int remote_sender_override = 0; + HashMap<NodePath, PathSentCache> path_send_cache; Map<int, PathGetCache> path_get_cache; int last_send_cache_id; Vector<uint8_t> packet_cache; + Node *root_node = nullptr; bool allow_object_decoding = false; + MultiplayerReplicator *replicator = nullptr; + RPCManager *rpc_manager = nullptr; protected: static void _bind_methods(); + bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target); void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len); void _process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len); void _process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len); - Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len); - void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len); - void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount); - bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target); - public: void poll(); void clear(); void set_root_node(Node *p_node); Node *get_root_node(); - void set_network_peer(const Ref<MultiplayerPeer> &p_peer); - Ref<MultiplayerPeer> get_network_peer() const; - Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, MultiplayerPeer::TransferMode p_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE, int p_channel = 0); + void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer); + Ref<MultiplayerPeer> get_multiplayer_peer() const; + + Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, Multiplayer::TransferMode p_mode = Multiplayer::TRANSFER_MODE_RELIABLE, int p_channel = 0); Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len); Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len); // Called by Node.rpc - void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount); + void rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount); // Called by Node._notification void scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter); // Called by replicator bool send_confirm_path(Node *p_node, NodePath p_path, int p_target, int &p_id); Node *get_cached_node(int p_from, uint32_t p_node_id); + bool is_cache_confirmed(NodePath p_path, int p_peer); void _add_peer(int p_id); void _del_peer(int p_id); @@ -160,23 +135,28 @@ public: void _connection_failed(); void _server_disconnected(); - bool has_network_peer() const { return network_peer.is_valid(); } - Vector<int> get_network_connected_peers() const; - int get_rpc_sender_id() const { return rpc_sender_id; } - int get_network_unique_id() const; - bool is_network_server() const; - void set_refuse_new_network_connections(bool p_refuse); - bool is_refusing_new_network_connections() const; + bool has_multiplayer_peer() const { return multiplayer_peer.is_valid(); } + Vector<int> get_peer_ids() const; + const Set<int> get_connected_peers() const { return connected_peers; } + int get_remote_sender_id() const { return remote_sender_override ? remote_sender_override : remote_sender_id; } + void set_remote_sender_override(int p_id) { remote_sender_override = p_id; } + int get_unique_id() const; + bool is_server() const; + void set_refuse_new_connections(bool p_refuse); + bool is_refusing_new_connections() const; void set_allow_object_decoding(bool p_enable); bool is_object_decoding_allowed() const; - MultiplayerReplicator *get_replicator() const; + MultiplayerReplicator *get_replicator() const { return replicator; } + RPCManager *get_rpc_manager() const { return rpc_manager; } + +#ifdef DEBUG_ENABLED + void profile_bandwidth(const String &p_inout, int p_size); +#endif MultiplayerAPI(); ~MultiplayerAPI(); }; -VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode); - #endif // MULTIPLAYER_API_H diff --git a/core/io/multiplayer_peer.cpp b/core/multiplayer/multiplayer_peer.cpp index 83cf24d7e3..3c33948e2f 100644 --- a/core/io/multiplayer_peer.cpp +++ b/core/multiplayer/multiplayer_peer.cpp @@ -53,6 +53,30 @@ uint32_t MultiplayerPeer::generate_unique_id() const { return hash; } +void MultiplayerPeer::set_transfer_channel(int p_channel) { + transfer_channel = p_channel; +} + +int MultiplayerPeer::get_transfer_channel() const { + return transfer_channel; +} + +void MultiplayerPeer::set_transfer_mode(Multiplayer::TransferMode p_mode) { + transfer_mode = p_mode; +} + +Multiplayer::TransferMode MultiplayerPeer::get_transfer_mode() const { + return transfer_mode; +} + +void MultiplayerPeer::set_refuse_new_connections(bool p_enable) { + refuse_connections = p_enable; +} + +bool MultiplayerPeer::is_refusing_new_connections() const { + return refuse_connections; +} + void MultiplayerPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &MultiplayerPeer::set_transfer_channel); ClassDB::bind_method(D_METHOD("get_transfer_channel"), &MultiplayerPeer::get_transfer_channel); @@ -75,10 +99,6 @@ void MultiplayerPeer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel", PROPERTY_HINT_RANGE, "0,255,1"), "set_transfer_channel", "get_transfer_channel"); - BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE); - BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE_ORDERED); - BIND_ENUM_CONSTANT(TRANSFER_MODE_RELIABLE); - BIND_ENUM_CONSTANT(CONNECTION_DISCONNECTED); BIND_ENUM_CONSTANT(CONNECTION_CONNECTING); BIND_ENUM_CONSTANT(CONNECTION_CONNECTED); @@ -92,3 +112,160 @@ void MultiplayerPeer::_bind_methods() { ADD_SIGNAL(MethodInfo("connection_succeeded")); ADD_SIGNAL(MethodInfo("connection_failed")); } + +/*************/ + +int MultiplayerPeerExtension::get_available_packet_count() const { + int count; + if (GDVIRTUAL_CALL(_get_available_packet_count, count)) { + return count; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_available_packet_count is unimplemented!"); + return -1; +} + +Error MultiplayerPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + int err; + if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_native is unimplemented!"); + return FAILED; +} + +Error MultiplayerPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + int err; + if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_put_packet_native is unimplemented!"); + return FAILED; +} + +int MultiplayerPeerExtension::get_max_packet_size() const { + int size; + if (GDVIRTUAL_CALL(_get_max_packet_size, size)) { + return size; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_max_packet_size is unimplemented!"); + return 0; +} + +void MultiplayerPeerExtension::set_transfer_channel(int p_channel) { + if (GDVIRTUAL_CALL(_set_transfer_channel, p_channel)) { + return; + } + MultiplayerPeer::set_transfer_channel(p_channel); +} + +int MultiplayerPeerExtension::get_transfer_channel() const { + int channel; + if (GDVIRTUAL_CALL(_get_transfer_channel, channel)) { + return channel; + } + return MultiplayerPeer::get_transfer_channel(); +} + +void MultiplayerPeerExtension::set_transfer_mode(Multiplayer::TransferMode p_mode) { + if (GDVIRTUAL_CALL(_set_transfer_mode, p_mode)) { + return; + } + MultiplayerPeer::set_transfer_mode(p_mode); +} + +Multiplayer::TransferMode MultiplayerPeerExtension::get_transfer_mode() const { + int mode; + if (GDVIRTUAL_CALL(_get_transfer_mode, mode)) { + return (Multiplayer::TransferMode)mode; + } + return MultiplayerPeer::get_transfer_mode(); +} + +void MultiplayerPeerExtension::set_target_peer(int p_peer_id) { + if (GDVIRTUAL_CALL(_set_target_peer, p_peer_id)) { + return; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_set_target_peer is unimplemented!"); +} + +int MultiplayerPeerExtension::get_packet_peer() const { + int peer; + if (GDVIRTUAL_CALL(_get_packet_peer, peer)) { + return peer; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_peer is unimplemented!"); + return 0; +} + +bool MultiplayerPeerExtension::is_server() const { + bool server; + if (GDVIRTUAL_CALL(_is_server, server)) { + return server; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_is_server is unimplemented!"); + return false; +} + +void MultiplayerPeerExtension::poll() { + int err; + if (GDVIRTUAL_CALL(_poll, err)) { + return; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_poll is unimplemented!"); +} + +int MultiplayerPeerExtension::get_unique_id() const { + int id; + if (GDVIRTUAL_CALL(_get_unique_id, id)) { + return id; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_unique_id is unimplemented!"); + return 0; +} + +void MultiplayerPeerExtension::set_refuse_new_connections(bool p_enable) { + if (GDVIRTUAL_CALL(_set_refuse_new_connections, p_enable)) { + return; + } + MultiplayerPeer::set_refuse_new_connections(p_enable); +} + +bool MultiplayerPeerExtension::is_refusing_new_connections() const { + bool refusing; + if (GDVIRTUAL_CALL(_is_refusing_new_connections, refusing)) { + return refusing; + } + return MultiplayerPeer::is_refusing_new_connections(); +} + +MultiplayerPeer::ConnectionStatus MultiplayerPeerExtension::get_connection_status() const { + int status; + if (GDVIRTUAL_CALL(_get_connection_status, status)) { + return (ConnectionStatus)status; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_connection_status is unimplemented!"); + return CONNECTION_DISCONNECTED; +} + +void MultiplayerPeerExtension::_bind_methods() { + GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size"); + GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size"); + GDVIRTUAL_BIND(_get_available_packet_count); + GDVIRTUAL_BIND(_get_max_packet_size); + + GDVIRTUAL_BIND(_set_transfer_channel, "p_channel"); + GDVIRTUAL_BIND(_get_transfer_channel); + + GDVIRTUAL_BIND(_set_transfer_mode, "p_mode"); + GDVIRTUAL_BIND(_get_transfer_mode); + + GDVIRTUAL_BIND(_set_target_peer, "p_peer"); + + GDVIRTUAL_BIND(_get_packet_peer); + GDVIRTUAL_BIND(_is_server); + GDVIRTUAL_BIND(_poll); + GDVIRTUAL_BIND(_get_unique_id); + GDVIRTUAL_BIND(_set_refuse_new_connections, "p_enable"); + GDVIRTUAL_BIND(_is_refusing_new_connections); + GDVIRTUAL_BIND(_get_connection_status); +} diff --git a/core/multiplayer/multiplayer_peer.h b/core/multiplayer/multiplayer_peer.h new file mode 100644 index 0000000000..126ba9e645 --- /dev/null +++ b/core/multiplayer/multiplayer_peer.h @@ -0,0 +1,144 @@ +/*************************************************************************/ +/* multiplayer_peer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NETWORKED_MULTIPLAYER_PEER_H +#define NETWORKED_MULTIPLAYER_PEER_H + +#include "core/io/packet_peer.h" +#include "core/multiplayer/multiplayer.h" + +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" +#include "core/variant/native_ptr.h" + +class MultiplayerPeer : public PacketPeer { + GDCLASS(MultiplayerPeer, PacketPeer); + +protected: + static void _bind_methods(); + +private: + int transfer_channel = 0; + Multiplayer::TransferMode transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; + bool refuse_connections = false; + +public: + enum { + TARGET_PEER_BROADCAST = 0, + TARGET_PEER_SERVER = 1 + }; + + enum ConnectionStatus { + CONNECTION_DISCONNECTED, + CONNECTION_CONNECTING, + CONNECTION_CONNECTED, + }; + + virtual void set_transfer_channel(int p_channel); + virtual int get_transfer_channel() const; + virtual void set_transfer_mode(Multiplayer::TransferMode p_mode); + virtual Multiplayer::TransferMode get_transfer_mode() const; + virtual void set_refuse_new_connections(bool p_enable); + virtual bool is_refusing_new_connections() const; + + virtual void set_target_peer(int p_peer_id) = 0; + + virtual int get_packet_peer() const = 0; + + virtual bool is_server() const = 0; + + virtual void poll() = 0; + + virtual int get_unique_id() const = 0; + + virtual ConnectionStatus get_connection_status() const = 0; + + uint32_t generate_unique_id() const; + + MultiplayerPeer() {} +}; + +VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus); + +class MultiplayerPeerExtension : public MultiplayerPeer { + GDCLASS(MultiplayerPeerExtension, MultiplayerPeer); + +protected: + static void _bind_methods(); + +public: + /* PacketPeer */ + virtual int get_available_packet_count() const override; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; + virtual int get_max_packet_size() const override; + + /* MultiplayerPeer */ + virtual void set_transfer_channel(int p_channel) override; + virtual int get_transfer_channel() const override; + virtual void set_transfer_mode(Multiplayer::TransferMode p_mode) override; + virtual Multiplayer::TransferMode get_transfer_mode() const override; + virtual void set_target_peer(int p_peer_id) override; + + virtual int get_packet_peer() const override; + + virtual bool is_server() const override; + + virtual void poll() override; + + virtual int get_unique_id() const override; + + virtual void set_refuse_new_connections(bool p_enable) override; + virtual bool is_refusing_new_connections() const override; + + virtual ConnectionStatus get_connection_status() const override; + + /* PacketPeer GDExtension */ + GDVIRTUAL0RC(int, _get_available_packet_count); + GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); + GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int); + GDVIRTUAL0RC(int, _get_max_packet_size); + + /* MultiplayerPeer GDExtension */ + GDVIRTUAL1(_set_transfer_channel, int); + GDVIRTUAL0RC(int, _get_transfer_channel); + GDVIRTUAL1(_set_transfer_mode, int); + GDVIRTUAL0RC(int, _get_transfer_mode); + GDVIRTUAL1(_set_target_peer, int); + GDVIRTUAL0RC(int, _get_packet_peer); + GDVIRTUAL0RC(bool, _is_server); + GDVIRTUAL0R(int, _poll); + GDVIRTUAL0RC(int, _get_unique_id); + GDVIRTUAL1(_set_refuse_new_connections, bool); + GDVIRTUAL0RC(bool, _is_refusing_new_connections); + GDVIRTUAL0RC(int, _get_connection_status); +}; + +#endif // NETWORKED_MULTIPLAYER_PEER_H diff --git a/core/io/multiplayer_replicator.cpp b/core/multiplayer/multiplayer_replicator.cpp index b9d0675af1..6604510394 100644 --- a/core/io/multiplayer_replicator.cpp +++ b/core/multiplayer/multiplayer_replicator.cpp @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/io/multiplayer_replicator.h" +#include "core/multiplayer/multiplayer_replicator.h" #include "core/io/marshalls.h" #include "scene/main/node.h" @@ -88,7 +88,7 @@ Error MultiplayerReplicator::_sync_all_default(const ResourceUID::ID &p_scene_id } 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); + ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC | (same_size ? BYTE_OR_ZERO_FLAG : 0); ofs = 1; ofs += encode_uint64(p_scene_id, &ptr[ofs]); ptr[ofs] = cfg.sync_recv++; @@ -113,19 +113,19 @@ Error MultiplayerReplicator::_sync_all_default(const ResourceUID::ID &p_scene_id 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); + Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); + peer->set_target_peer(p_peer); + peer->set_transfer_channel(0); + peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_UNRELIABLE); + return peer->put_packet(ptr, ofs); } void MultiplayerReplicator::_process_default_sync(const ResourceUID::ID &p_id, const uint8_t *p_packet, int p_packet_len) { ERR_FAIL_COND_MSG(p_packet_len < SYNC_CMD_OFFSET + 5, "Invalid spawn packet received"); ERR_FAIL_COND_MSG(!replications.has(p_id), "Invalid spawn ID received " + itos(p_id)); SceneConfig &cfg = replications[p_id]; - ERR_FAIL_COND_MSG(cfg.mode != REPLICATION_MODE_SERVER || multiplayer->is_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; + ERR_FAIL_COND_MSG(cfg.mode != REPLICATION_MODE_SERVER || multiplayer->is_server(), "The defualt implementation only allows sync packets from the server"); + const bool same_size = p_packet[0] & BYTE_OR_ZERO_FLAG; int ofs = SYNC_CMD_OFFSET; int time = p_packet[ofs]; // Skip old update. @@ -218,7 +218,7 @@ Error MultiplayerReplicator::_send_default_spawn_despawn(int p_peer_id, const Re 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); + ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) | (is_raw ? BYTE_OR_ZERO_FLAG : 0); ofs = 1; ofs += encode_uint64(p_scene_id, &ptr[ofs]); ofs += encode_uint32(path_id, &ptr[ofs]); @@ -233,11 +233,11 @@ Error MultiplayerReplicator::_send_default_spawn_despawn(int p_peer_id, const Re 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); + Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); + peer->set_target_peer(p_peer_id); + peer->set_transfer_channel(0); + peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); + return peer->put_packet(ptr, ofs + state_len); } void MultiplayerReplicator::_process_default_spawn_despawn(int p_from, const ResourceUID::ID &p_scene_id, const uint8_t *p_packet, int p_packet_len, bool p_spawn) { @@ -262,7 +262,7 @@ void MultiplayerReplicator::_process_default_spawn_despawn(int p_from, const Res 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; + const bool is_raw = ((p_packet[0] & BYTE_OR_ZERO_FLAG) >> BYTE_OR_ZERO_SHIFT) == 1; ERR_FAIL_COND_MSG(parent->has_node(name), vformat("Unable to spawn node. Node already exists: %s/%s", parent->get_path(), name)); RES res = ResourceLoader::load(scene_path); @@ -308,7 +308,7 @@ void MultiplayerReplicator::process_spawn_despawn(int p_from, const uint8_t *p_p 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; + bool is_raw = ((p_packet[0] & BYTE_OR_ZERO_FLAG) >> BYTE_OR_ZERO_SHIFT) == 1; Variant data; int left = p_packet_len - ofs; if (is_raw && left) { @@ -350,9 +350,9 @@ void MultiplayerReplicator::process_sync(int p_from, const uint8_t *p_packet, in } } PackedByteArray pba; - pba.resize(p_packet_len - SPAWN_CMD_OFFSET); + pba.resize(p_packet_len - SYNC_CMD_OFFSET); if (pba.size()) { - memcpy(pba.ptrw(), p_packet, p_packet_len - SPAWN_CMD_OFFSET); + memcpy(pba.ptrw(), p_packet + SYNC_CMD_OFFSET, p_packet_len - SYNC_CMD_OFFSET); } Variant args[4] = { p_from, id, objs, pba }; Variant *argp[4] = { args, &args[1], &args[2], &args[3] }; @@ -466,7 +466,7 @@ Error MultiplayerReplicator::spawn_config(const ResourceUID::ID &p_id, Replicati SceneConfig cfg; cfg.mode = p_mode; for (int i = 0; i < p_props.size(); i++) { - cfg.properties.push_back(StringName(p_props[i])); + cfg.properties.push_back(p_props[i]); } cfg.on_spawn_despawn_send = p_on_send; cfg.on_spawn_despawn_receive = p_on_recv; @@ -505,7 +505,7 @@ Error MultiplayerReplicator::_send_spawn_despawn(int p_peer_id, const ResourceUI } 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); + ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) + ((is_raw ? 1 : 0) << BYTE_OR_ZERO_SHIFT); encode_uint64(p_scene_id, &ptr[1]); if (p_data.get_type() == Variant::PACKED_BYTE_ARRAY) { const PackedByteArray pba = p_data; @@ -513,21 +513,21 @@ Error MultiplayerReplicator::_send_spawn_despawn(int p_peer_id, const ResourceUI } 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); + Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); + peer->set_target_peer(p_peer_id); + peer->set_transfer_channel(0); + peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); + return peer->put_packet(ptr, SPAWN_CMD_OFFSET + data_size); } Error MultiplayerReplicator::send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, const NodePath &p_path) { - ERR_FAIL_COND_V(!multiplayer->has_network_peer(), ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id)); const SceneConfig &cfg = replications[p_scene_id]; if (cfg.on_spawn_despawn_send.is_valid()) { return _send_spawn_despawn(p_peer_id, p_scene_id, p_data, true); } else { - ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_network_server(), ERR_UNAVAILABLE, "Manual despawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests."); + ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server(), ERR_UNAVAILABLE, "Manual despawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests."); NodePath path = p_path; Object *obj = p_data.get_type() == Variant::OBJECT ? p_data.get_validated_object() : nullptr; if (path.is_empty() && obj) { @@ -542,13 +542,13 @@ Error MultiplayerReplicator::send_despawn(int p_peer_id, const ResourceUID::ID & } 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(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id)); const SceneConfig &cfg = replications[p_scene_id]; if (cfg.on_spawn_despawn_send.is_valid()) { return _send_spawn_despawn(p_peer_id, p_scene_id, p_data, false); } else { - ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_network_server(), ERR_UNAVAILABLE, "Manual spawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests."); + ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server(), ERR_UNAVAILABLE, "Manual spawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests."); NodePath path = p_path; Object *obj = p_data.get_type() == Variant::OBJECT ? p_data.get_validated_object() : nullptr; ERR_FAIL_COND_V_MSG(!obj, ERR_INVALID_PARAMETER, "Spawn default implementation requires the data to be an object."); @@ -572,7 +572,7 @@ Error MultiplayerReplicator::_spawn_despawn(ResourceUID::ID p_scene_id, Object * args[0] = p_peer; args[1] = p_scene_id; args[2] = p_obj; - args[3] = true; + args[3] = p_spawn; const Variant *argp[] = { &args[0], &args[1], &args[2], &args[3] }; Callable::CallError ce; Variant ret; @@ -619,7 +619,7 @@ Error MultiplayerReplicator::decode_state(const ResourceUID::ID &p_scene_id, Obj } void MultiplayerReplicator::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) { - if (!multiplayer->has_network_peer()) { + if (!multiplayer->has_multiplayer_peer()) { return; } Node *root_node = multiplayer->get_root_node(); @@ -634,14 +634,14 @@ void MultiplayerReplicator::scene_enter_exit_notify(const String &p_scene, Node } const SceneConfig &cfg = replications[id]; if (p_enter) { - if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_network_server()) { + if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server()) { replicated_nodes[p_node->get_instance_id()] = id; _track(id, p_node); spawn(id, p_node, 0); } emit_signal(SNAME("replicated_instance_added"), id, p_node); } else { - if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_network_server() && replicated_nodes.has(p_node->get_instance_id())) { + if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server() && replicated_nodes.has(p_node->get_instance_id())) { replicated_nodes.erase(p_node->get_instance_id()); _untrack(id, p_node); despawn(id, p_node, 0); @@ -666,7 +666,7 @@ void MultiplayerReplicator::poll() { if (!E.value.sync_interval) { continue; } - if (E.value.mode == REPLICATION_MODE_SERVER && !multiplayer->is_network_server()) { + if (E.value.mode == REPLICATION_MODE_SERVER && !multiplayer->is_server()) { continue; } uint64_t time = OS::get_singleton()->get_ticks_usec(); @@ -740,8 +740,8 @@ Error MultiplayerReplicator::sync_all(const ResourceUID::ID &p_scene_id, int p_p 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); +Error MultiplayerReplicator::send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_transfer_mode, int p_channel) { + ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED); ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER); const SceneConfig &cfg = replications[p_scene_id]; ERR_FAIL_COND_V_MSG(!cfg.on_sync_send.is_valid(), ERR_UNCONFIGURED, "Sending raw sync messages is only available with custom functions"); @@ -749,11 +749,14 @@ Error MultiplayerReplicator::send_sync(int p_peer_id, const ResourceUID::ID &p_s 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()); + if (p_data.size()) { + memcpy(&ptr[SYNC_CMD_OFFSET], p_data.ptr(), p_data.size()); + } + Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); + peer->set_target_peer(p_peer_id); + peer->set_transfer_channel(p_channel); + peer->set_transfer_mode(p_transfer_mode); + return peer->put_packet(ptr, SYNC_CMD_OFFSET + p_data.size()); } void MultiplayerReplicator::clear() { @@ -768,7 +771,7 @@ void MultiplayerReplicator::_bind_methods() { 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("send_sync", "peer_id", "scene_id", "data", "transfer_mode", "channel"), &MultiplayerReplicator::send_sync, DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); ClassDB::bind_method(D_METHOD("sync_all", "scene_id", "peer_id"), &MultiplayerReplicator::sync_all, DEFVAL(0)); ClassDB::bind_method(D_METHOD("track", "scene_id", "object"), &MultiplayerReplicator::track); ClassDB::bind_method(D_METHOD("untrack", "scene_id", "object"), &MultiplayerReplicator::untrack); diff --git a/core/io/multiplayer_replicator.h b/core/multiplayer/multiplayer_replicator.h index 2630ad7a8a..7fa774fdf4 100644 --- a/core/io/multiplayer_replicator.h +++ b/core/multiplayer/multiplayer_replicator.h @@ -31,8 +31,9 @@ #ifndef MULTIPLAYER_REPLICATOR_H #define MULTIPLAYER_REPLICATOR_H -#include "core/io/multiplayer_api.h" +#include "core/multiplayer/multiplayer_api.h" +#include "core/io/resource_uid.h" #include "core/templates/hash_map.h" #include "core/variant/typed_array.h" @@ -68,6 +69,14 @@ protected: static void _bind_methods(); private: + enum { + BYTE_OR_ZERO_SHIFT = MultiplayerAPI::CMD_FLAG_0_SHIFT, + }; + + enum { + BYTE_OR_ZERO_FLAG = 1 << BYTE_OR_ZERO_SHIFT, + }; + MultiplayerAPI *multiplayer = nullptr; Vector<uint8_t> packet_cache; Map<ResourceUID::ID, SceneConfig> replications; @@ -108,7 +117,7 @@ public: // 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); + Error send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_mode, int p_channel); void track(const ResourceUID::ID &p_scene_id, Object *p_object); void untrack(const ResourceUID::ID &p_scene_id, Object *p_object); diff --git a/core/multiplayer/rpc_manager.cpp b/core/multiplayer/rpc_manager.cpp new file mode 100644 index 0000000000..d8e875c3e6 --- /dev/null +++ b/core/multiplayer/rpc_manager.cpp @@ -0,0 +1,525 @@ +/*************************************************************************/ +/* rpc_manager.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/multiplayer/rpc_manager.h" + +#include "core/debugger/engine_debugger.h" +#include "core/io/marshalls.h" +#include "core/multiplayer/multiplayer_api.h" +#include "scene/main/node.h" + +#ifdef DEBUG_ENABLED +_FORCE_INLINE_ void RPCManager::_profile_node_data(const String &p_what, ObjectID p_id) { + if (EngineDebugger::is_profiling("multiplayer")) { + Array values; + values.push_back("node"); + values.push_back(p_id); + values.push_back(p_what); + EngineDebugger::profiler_add_frame_data("multiplayer", values); + } +} +#else +_FORCE_INLINE_ void RPCManager::_profile_node_data(const String &p_what, ObjectID p_id) {} +#endif + +// Returns the packet size stripping the node path added when the node is not yet cached. +int get_packet_len(uint32_t p_node_target, int p_packet_len) { + if (p_node_target & 0x80000000) { + int ofs = p_node_target & 0x7FFFFFFF; + return p_packet_len - (p_packet_len - ofs); + } else { + return p_packet_len; + } +} + +const Multiplayer::RPCConfig _get_rpc_config(const Node *p_node, const StringName &p_method, uint16_t &r_id) { + const Vector<Multiplayer::RPCConfig> node_config = p_node->get_node_rpc_methods(); + for (int i = 0; i < node_config.size(); i++) { + if (node_config[i].name == p_method) { + r_id = ((uint16_t)i) | (1 << 15); + return node_config[i]; + } + } + if (p_node->get_script_instance()) { + const Vector<Multiplayer::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods(); + for (int i = 0; i < script_config.size(); i++) { + if (script_config[i].name == p_method) { + r_id = (uint16_t)i; + return script_config[i]; + } + } + } + return Multiplayer::RPCConfig(); +} + +const Multiplayer::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_id) { + Vector<Multiplayer::RPCConfig> config; + uint16_t id = p_id; + if (id & (1 << 15)) { + id = id & ~(1 << 15); + config = p_node->get_node_rpc_methods(); + } else if (p_node->get_script_instance()) { + config = p_node->get_script_instance()->get_rpc_methods(); + } + if (id < config.size()) { + return config[id]; + } + return Multiplayer::RPCConfig(); +} + +_FORCE_INLINE_ bool _can_call_mode(Node *p_node, Multiplayer::RPCMode mode, int p_remote_id) { + switch (mode) { + case Multiplayer::RPC_MODE_DISABLED: { + return false; + } break; + case Multiplayer::RPC_MODE_ANY_PEER: { + return true; + } break; + case Multiplayer::RPC_MODE_AUTHORITY: { + return !p_node->is_multiplayer_authority() && p_remote_id == p_node->get_multiplayer_authority(); + } break; + } + + return false; +} + +String RPCManager::get_rpc_md5(const Node *p_node) { + String rpc_list; + const Vector<Multiplayer::RPCConfig> node_config = p_node->get_node_rpc_methods(); + for (int i = 0; i < node_config.size(); i++) { + rpc_list += String(node_config[i].name); + } + if (p_node->get_script_instance()) { + const Vector<Multiplayer::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods(); + for (int i = 0; i < script_config.size(); i++) { + rpc_list += String(script_config[i].name); + } + } + return rpc_list.md5_text(); +} + +Node *RPCManager::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) { + Node *node = nullptr; + + if (p_node_target & 0x80000000) { + // Use full path (not cached yet). + int ofs = p_node_target & 0x7FFFFFFF; + + ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared."); + + String paths; + paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs); + + NodePath np = paths; + + node = multiplayer->get_root_node()->get_node(np); + + if (!node) { + ERR_PRINT("Failed to get path from RPC: " + String(np) + "."); + } + return node; + } else { + // Use cached path. + return multiplayer->get_cached_node(p_from, p_node_target); + } +} + +void RPCManager::process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) { + // Extract packet meta + int packet_min_size = 1; + int name_id_offset = 1; + ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); + // Compute the meta size, which depends on the compression level. + int node_id_compression = (p_packet[0] & NODE_ID_COMPRESSION_FLAG) >> NODE_ID_COMPRESSION_SHIFT; + int name_id_compression = (p_packet[0] & NAME_ID_COMPRESSION_FLAG) >> NAME_ID_COMPRESSION_SHIFT; + + switch (node_id_compression) { + case NETWORK_NODE_ID_COMPRESSION_8: + packet_min_size += 1; + name_id_offset += 1; + break; + case NETWORK_NODE_ID_COMPRESSION_16: + packet_min_size += 2; + name_id_offset += 2; + break; + case NETWORK_NODE_ID_COMPRESSION_32: + packet_min_size += 4; + name_id_offset += 4; + break; + default: + ERR_FAIL_MSG("Was not possible to extract the node id compression mode."); + } + switch (name_id_compression) { + case NETWORK_NAME_ID_COMPRESSION_8: + packet_min_size += 1; + break; + case NETWORK_NAME_ID_COMPRESSION_16: + packet_min_size += 2; + break; + default: + ERR_FAIL_MSG("Was not possible to extract the name id compression mode."); + } + ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); + + uint32_t node_target = 0; + switch (node_id_compression) { + case NETWORK_NODE_ID_COMPRESSION_8: + node_target = p_packet[1]; + break; + case NETWORK_NODE_ID_COMPRESSION_16: + node_target = decode_uint16(p_packet + 1); + break; + case NETWORK_NODE_ID_COMPRESSION_32: + node_target = decode_uint32(p_packet + 1); + break; + default: + // Unreachable, checked before. + CRASH_NOW(); + } + + Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len); + ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found."); + + uint16_t name_id = 0; + switch (name_id_compression) { + case NETWORK_NAME_ID_COMPRESSION_8: + name_id = p_packet[name_id_offset]; + break; + case NETWORK_NAME_ID_COMPRESSION_16: + name_id = decode_uint16(p_packet + name_id_offset); + break; + default: + // Unreachable, checked before. + CRASH_NOW(); + } + + const int packet_len = get_packet_len(node_target, p_packet_len); + _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size); +} + +void RPCManager::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { + ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small."); + + // Check that remote can call the RPC on this node. + const Multiplayer::RPCConfig config = _get_rpc_config_by_id(p_node, p_rpc_method_id); + ERR_FAIL_COND(config.name == StringName()); + + bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from); + ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_multiplayer_authority()) + "."); + + int argc = 0; + bool byte_only = false; + + const bool byte_only_or_no_args = p_packet[0] & BYTE_ONLY_OR_NO_ARGS_FLAG; + if (byte_only_or_no_args) { + if (p_offset < p_packet_len) { + // This packet contains only bytes. + argc = 1; + byte_only = true; + } else { + // This rpc calls a method without parameters. + } + } else { + // Normal variant, takes the argument count from the packet. + ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); + argc = p_packet[p_offset]; + p_offset += 1; + } + + Vector<Variant> args; + Vector<const Variant *> argp; + args.resize(argc); + argp.resize(argc); + +#ifdef DEBUG_ENABLED + _profile_node_data("in_rpc", p_node->get_instance_id()); +#endif + + if (byte_only) { + Vector<uint8_t> pure_data; + const int len = p_packet_len - p_offset; + pure_data.resize(len); + memcpy(pure_data.ptrw(), &p_packet[p_offset], len); + args.write[0] = pure_data; + argp.write[0] = &args[0]; + p_offset += len; + } else { + for (int i = 0; i < argc; i++) { + ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); + + int vlen; + Error err = multiplayer->decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); + ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument."); + + argp.write[i] = &args[i]; + p_offset += vlen; + } + } + + Callable::CallError ce; + + p_node->call(config.name, (const Variant **)argp.ptr(), argc, ce); + if (ce.error != Callable::CallError::CALL_OK) { + String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce); + error = "RPC - " + error; + ERR_PRINT(error); + } +} + +void RPCManager::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) { + Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); + ERR_FAIL_COND_MSG(peer.is_null(), "Attempt to call RPC without active multiplayer peer."); + + ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to call RPC while multiplayer peer is not connected yet."); + + ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to call RPC while multiplayer peer is disconnected."); + + ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments (>255)."); + + if (p_to != 0 && !multiplayer->get_connected_peers().has(ABS(p_to))) { + ERR_FAIL_COND_MSG(p_to == peer->get_unique_id(), "Attempt to call RPC on yourself! Peer unique ID: " + itos(peer->get_unique_id()) + "."); + + ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + "."); + } + + NodePath from_path = (multiplayer->get_root_node()->get_path()).rel_path_to(p_from->get_path()); + ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!"); + + // See if all peers have cached path (if so, call can be fast). + int psc_id; + const bool has_all_peers = multiplayer->send_confirm_path(p_from, from_path, p_to, psc_id); + + // Create base packet, lots of hardcode because it must be tight. + + int ofs = 0; + +#define MAKE_ROOM(m_amount) \ + if (packet_cache.size() < m_amount) \ + packet_cache.resize(m_amount); + + // Encode meta. + uint8_t command_type = MultiplayerAPI::NETWORK_COMMAND_REMOTE_CALL; + uint8_t node_id_compression = UINT8_MAX; + uint8_t name_id_compression = UINT8_MAX; + bool byte_only_or_no_args = false; + + MAKE_ROOM(1); + // The meta is composed along the way, so just set 0 for now. + packet_cache.write[0] = 0; + ofs += 1; + + // Encode Node ID. + if (has_all_peers) { + // Compress the node ID only if all the target peers already know it. + if (psc_id >= 0 && psc_id <= 255) { + // We can encode the id in 1 byte + node_id_compression = NETWORK_NODE_ID_COMPRESSION_8; + MAKE_ROOM(ofs + 1); + packet_cache.write[ofs] = static_cast<uint8_t>(psc_id); + ofs += 1; + } else if (psc_id >= 0 && psc_id <= 65535) { + // We can encode the id in 2 bytes + node_id_compression = NETWORK_NODE_ID_COMPRESSION_16; + MAKE_ROOM(ofs + 2); + encode_uint16(static_cast<uint16_t>(psc_id), &(packet_cache.write[ofs])); + ofs += 2; + } else { + // Too big, let's use 4 bytes. + node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; + MAKE_ROOM(ofs + 4); + encode_uint32(psc_id, &(packet_cache.write[ofs])); + ofs += 4; + } + } else { + // The targets don't know the node yet, so we need to use 32 bits int. + node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; + MAKE_ROOM(ofs + 4); + encode_uint32(psc_id, &(packet_cache.write[ofs])); + ofs += 4; + } + + // Encode method ID + if (p_rpc_id <= UINT8_MAX) { + // The ID fits in 1 byte + name_id_compression = NETWORK_NAME_ID_COMPRESSION_8; + MAKE_ROOM(ofs + 1); + packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id); + ofs += 1; + } else { + // The ID is larger, let's use 2 bytes + name_id_compression = NETWORK_NAME_ID_COMPRESSION_16; + MAKE_ROOM(ofs + 2); + encode_uint16(p_rpc_id, &(packet_cache.write[ofs])); + ofs += 2; + } + + if (p_argcount == 0) { + byte_only_or_no_args = true; + } else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) { + byte_only_or_no_args = true; + // Special optimization when only the byte vector is sent. + const Vector<uint8_t> data = *p_arg[0]; + MAKE_ROOM(ofs + data.size()); + memcpy(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size()); + ofs += data.size(); + } else { + // Arguments + MAKE_ROOM(ofs + 1); + packet_cache.write[ofs] = p_argcount; + ofs += 1; + for (int i = 0; i < p_argcount; i++) { + int len(0); + Error err = multiplayer->encode_and_compress_variant(*p_arg[i], nullptr, len); + ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); + MAKE_ROOM(ofs + len); + multiplayer->encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len); + ofs += len; + } + } + + ERR_FAIL_COND(command_type > 7); + ERR_FAIL_COND(node_id_compression > 3); + ERR_FAIL_COND(name_id_compression > 1); + + // We can now set the meta + packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + (byte_only_or_no_args ? BYTE_ONLY_OR_NO_ARGS_FLAG : 0); + +#ifdef DEBUG_ENABLED + multiplayer->profile_bandwidth("out", ofs); +#endif + + // Take chance and set transfer mode, since all send methods will use it. + peer->set_transfer_channel(p_config.channel); + peer->set_transfer_mode(p_config.transfer_mode); + + if (has_all_peers) { + // They all have verified paths, so send fast. + peer->set_target_peer(p_to); // To all of you. + peer->put_packet(packet_cache.ptr(), ofs); // A message with love. + } else { + // Unreachable because the node ID is never compressed if the peers doesn't know it. + CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32); + + // Not all verified path, so send one by one. + + // Append path at the end, since we will need it for some packets. + CharString pname = String(from_path).utf8(); + int path_len = encode_cstring(pname.get_data(), nullptr); + MAKE_ROOM(ofs + path_len); + encode_cstring(pname.get_data(), &(packet_cache.write[ofs])); + + for (const int &P : multiplayer->get_connected_peers()) { + if (p_to < 0 && P == -p_to) { + continue; // Continue, excluded. + } + + if (p_to > 0 && P != p_to) { + continue; // Continue, not for this peer. + } + + bool confirmed = multiplayer->is_cache_confirmed(from_path, P); + + peer->set_target_peer(P); // To this one specifically. + + if (confirmed) { + // This one confirmed path, so use id. + encode_uint32(psc_id, &(packet_cache.write[1])); + peer->put_packet(packet_cache.ptr(), ofs); + } else { + // This one did not confirm path yet, so use entire path (sorry!). + encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag. + peer->put_packet(packet_cache.ptr(), ofs + path_len); + } + } + } +} + +void RPCManager::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { + Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); + ERR_FAIL_COND_MSG(!peer.is_valid(), "Trying to call an RPC while no multiplayer peer is active."); + ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree."); + ERR_FAIL_COND_MSG(peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a multiplayer peer which is not connected."); + + int node_id = peer->get_unique_id(); + bool call_local_native = false; + bool call_local_script = false; + uint16_t rpc_id = UINT16_MAX; + const Multiplayer::RPCConfig config = _get_rpc_config(p_node, p_method, rpc_id); + ERR_FAIL_COND_MSG(config.name == StringName(), + vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is not marked for RPCs.", p_method, p_node->get_path())); + if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { + if (rpc_id & (1 << 15)) { + call_local_native = config.call_local; + } else { + call_local_script = config.call_local; + } + } + + if (p_peer_id != node_id) { +#ifdef DEBUG_ENABLED + _profile_node_data("out_rpc", p_node->get_instance_id()); +#endif + + _send_rpc(p_node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount); + } + + if (call_local_native) { + Callable::CallError ce; + + multiplayer->set_remote_sender_override(peer->get_unique_id()); + p_node->call(p_method, p_arg, p_argcount, ce); + multiplayer->set_remote_sender_override(0); + + if (ce.error != Callable::CallError::CALL_OK) { + String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce); + error = "rpc() aborted in local call: - " + error + "."; + ERR_PRINT(error); + return; + } + } + + if (call_local_script) { + Callable::CallError ce; + ce.error = Callable::CallError::CALL_OK; + + multiplayer->set_remote_sender_override(peer->get_unique_id()); + p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce); + multiplayer->set_remote_sender_override(0); + + if (ce.error != Callable::CallError::CALL_OK) { + String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce); + error = "rpc() aborted in script local call: - " + error + "."; + ERR_PRINT(error); + return; + } + } + + ERR_FAIL_COND_MSG(p_peer_id == node_id && !config.call_local, "RPC '" + p_method + "' on yourself is not allowed by selected mode."); +} diff --git a/core/multiplayer/rpc_manager.h b/core/multiplayer/rpc_manager.h new file mode 100644 index 0000000000..7b99dee226 --- /dev/null +++ b/core/multiplayer/rpc_manager.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* rpc_manager.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MULTIPLAYER_RPC_H +#define MULTIPLAYER_RPC_H + +#include "core/multiplayer/multiplayer.h" +#include "core/multiplayer/multiplayer_api.h" +#include "core/object/ref_counted.h" + +class RPCManager : public RefCounted { + GDCLASS(RPCManager, RefCounted); + +private: + enum NetworkNodeIdCompression { + NETWORK_NODE_ID_COMPRESSION_8 = 0, + NETWORK_NODE_ID_COMPRESSION_16, + NETWORK_NODE_ID_COMPRESSION_32, + }; + + enum NetworkNameIdCompression { + NETWORK_NAME_ID_COMPRESSION_8 = 0, + NETWORK_NAME_ID_COMPRESSION_16, + }; + + // The RPC meta is composed by a single byte that contains (starting from the least significant bit): + // - `NetworkCommands` in the first four bits. + // - `NetworkNodeIdCompression` in the next 2 bits. + // - `NetworkNameIdCompression` in the next 1 bit. + // - `byte_only_or_no_args` in the next 1 bit. + enum { + NODE_ID_COMPRESSION_SHIFT = MultiplayerAPI::CMD_FLAG_0_SHIFT, // 2 bits for this. + NAME_ID_COMPRESSION_SHIFT = MultiplayerAPI::CMD_FLAG_2_SHIFT, + BYTE_ONLY_OR_NO_ARGS_SHIFT = MultiplayerAPI::CMD_FLAG_3_SHIFT, + }; + + enum { + NODE_ID_COMPRESSION_FLAG = (1 << NODE_ID_COMPRESSION_SHIFT) | (1 << (NODE_ID_COMPRESSION_SHIFT + 1)), // 2 bits for this. + NAME_ID_COMPRESSION_FLAG = (1 << NAME_ID_COMPRESSION_SHIFT), + BYTE_ONLY_OR_NO_ARGS_FLAG = (1 << BYTE_ONLY_OR_NO_ARGS_SHIFT), + }; + + MultiplayerAPI *multiplayer = nullptr; + Vector<uint8_t> packet_cache; + +protected: + _FORCE_INLINE_ void _profile_node_data(const String &p_what, ObjectID p_id); + void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); + + void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount); + Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len); + +public: + // Called by Node.rpc + void rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount); + void process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len); + + String get_rpc_md5(const Node *p_node); + RPCManager(MultiplayerAPI *p_multiplayer) { multiplayer = p_multiplayer; } +}; + +#endif // MULTIPLAYER_RPC_H diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index e268a8d292..4b3c8b123f 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -37,8 +37,6 @@ #define OBJTYPE_RLOCK RWLockRead _rw_lockr_(lock); #define OBJTYPE_WLOCK RWLockWrite _rw_lockw_(lock); -#ifdef DEBUG_METHODS_ENABLED - MethodDefinition D_METHOD(const char *p_name) { MethodDefinition md; md.name = StaticCString::create(p_name); @@ -226,8 +224,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ return md; } -#endif - ClassDB::APIType ClassDB::current_api = API_CORE; void ClassDB::set_current_api(APIType p_api) { @@ -529,7 +525,7 @@ Object *ClassDB::instantiate(const StringName &p_class) { } ERR_FAIL_COND_V_MSG(!ti, nullptr, "Cannot get class '" + String(p_class) + "'."); ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled."); - ERR_FAIL_COND_V(!ti->creation_func, nullptr); + ERR_FAIL_COND_V_MSG(!ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated."); } #ifdef TOOLS_ENABLED if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { @@ -545,6 +541,15 @@ Object *ClassDB::instantiate(const StringName &p_class) { return ti->creation_func(); } +Object *ClassDB::construct_object(Object *(*p_create_func)(), ObjectNativeExtension *p_extension) { + if (p_extension) { + initializing_with_extension = true; + initializing_extension = p_extension; + initializing_extension_instance = p_extension->create_instance(p_extension->class_userdata); + } + return p_create_func(); +} + bool ClassDB::can_instantiate(const StringName &p_class) { OBJTYPE_RLOCK; @@ -580,7 +585,6 @@ void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherit } } -#ifdef DEBUG_METHODS_ENABLED static MethodInfo info_from_bind(MethodBind *p_method) { MethodInfo minfo; minfo.name = p_method->get_name(); @@ -601,7 +605,6 @@ static MethodInfo info_from_bind(MethodBind *p_method) { return minfo; } -#endif void ClassDB::get_method_list(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) { OBJTYPE_RLOCK; @@ -641,9 +644,8 @@ void ClassDB::get_method_list(const StringName &p_class, List<MethodInfo> *p_met while ((K = type->method_map.next(K))) { MethodBind *m = type->method_map[*K]; - MethodInfo mi; - mi.name = m->get_name(); - p_methods->push_back(mi); + MethodInfo minfo = info_from_bind(m); + p_methods->push_back(minfo); } #endif @@ -689,9 +691,8 @@ bool ClassDB::get_method_info(const StringName &p_class, const StringName &p_met if (type->method_map.has(p_method)) { if (r_info) { MethodBind *m = type->method_map[p_method]; - MethodInfo mi; - mi.name = m->get_name(); - *r_info = mi; + MethodInfo minfo = info_from_bind(m); + *r_info = minfo; } return true; } @@ -1028,6 +1029,18 @@ void ClassDB::add_property_subgroup(const StringName &p_class, const String &p_n type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_SUBGROUP)); } +void ClassDB::add_property_array_count(const StringName &p_class, const String &p_label, const StringName &p_count_property, const StringName &p_count_setter, const StringName &p_count_getter, const String &p_array_element_prefix, uint32_t p_count_usage) { + add_property(p_class, PropertyInfo(Variant::INT, p_count_property, PROPERTY_HINT_NONE, "", p_count_usage | PROPERTY_USAGE_ARRAY, vformat("%s,%s", p_label, p_array_element_prefix)), p_count_setter, p_count_getter); +} + +void ClassDB::add_property_array(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix) { + OBJTYPE_WLOCK; + ClassInfo *type = classes.getptr(p_class); + ERR_FAIL_COND(!type); + + type->property_list.push_back(PropertyInfo(Variant::NIL, p_path, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, p_array_element_prefix)); +} + // NOTE: For implementation simplicity reasons, this method doesn't allow setters to have optional arguments at the end. void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index) { lock.read_lock(); @@ -1390,13 +1403,8 @@ void ClassDB::bind_method_custom(const StringName &p_class, MethodBind *p_method type->method_map[p_method->get_name()] = p_method; } -#ifdef DEBUG_METHODS_ENABLED MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount) { StringName mdname = method_name.name; -#else -MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const char *method_name, const Variant **p_defs, int p_defcount) { - StringName mdname = StaticCString::create(method_name); -#endif OBJTYPE_WLOCK; ERR_FAIL_COND_V(!p_bind, nullptr); diff --git a/core/object/class_db.h b/core/object/class_db.h index 166aa35469..d9eec4e4a8 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -45,8 +45,6 @@ #define DEFVAL(m_defval) (m_defval) -#ifdef DEBUG_METHODS_ENABLED - struct MethodDefinition { StringName name; Vector<StringName> args; @@ -72,26 +70,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12); MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12, const char *p_arg13); -#else - -//#define NO_VARIADIC_MACROS - -#ifdef NO_VARIADIC_MACROS - -static _FORCE_INLINE_ const char *D_METHOD(const char *m_name, ...) { - return m_name; -} - -#else - -// When DEBUG_METHODS_ENABLED is set this will let the engine know -// the argument names for easier debugging. -#define D_METHOD(m_c, ...) m_c - -#endif - -#endif - class ClassDB { public: enum APIType { @@ -156,11 +134,7 @@ public: static HashMap<StringName, StringName> resource_base_extensions; static HashMap<StringName, StringName> compat_classes; -#ifdef DEBUG_METHODS_ENABLED static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount); -#else - static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const char *method_name, const Variant **p_defs, int p_defcount); -#endif static APIType current_api; @@ -190,6 +164,7 @@ public: t->creation_func = &creator<T>; t->exposed = true; t->class_ptr = T::get_class_ptr_static(); + t->api = current_api; T::register_custom_data_to_otdb(); } @@ -201,6 +176,7 @@ public: ERR_FAIL_COND(!t); t->exposed = true; t->class_ptr = T::get_class_ptr_static(); + t->api = current_api; //nothing } @@ -221,6 +197,7 @@ public: t->creation_func = &_create_ptr_func<T>; t->exposed = true; t->class_ptr = T::get_class_ptr_static(); + t->api = current_api; T::register_custom_data_to_otdb(); } @@ -234,6 +211,7 @@ public: static bool is_parent_class(const StringName &p_class, const StringName &p_inherits); static bool can_instantiate(const StringName &p_class); static Object *instantiate(const StringName &p_class); + static Object *construct_object(Object *(*p_create_func)(), ObjectNativeExtension *p_extension); static void instance_get_native_extension_data(ObjectNativeExtension **r_extension, GDExtensionClassInstancePtr *r_extension_instance, Object *p_base); static APIType get_api_type(const StringName &p_class); @@ -353,6 +331,8 @@ public: static void add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix = ""); static void add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix = ""); + static void add_property_array_count(const StringName &p_class, const String &p_label, const StringName &p_count_property, const StringName &p_count_setter, const StringName &p_count_getter, const String &p_array_element_prefix, uint32_t p_count_usage = PROPERTY_USAGE_EDITOR); + static void add_property_array(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix); static void add_property(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index = -1); static void set_property_default_value(const StringName &p_class, const StringName &p_name, const Variant &p_default); static void add_linked_property(const StringName &p_class, const String &p_property, const String &p_linked_property); diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp index 4751c69f1e..736e940846 100644 --- a/core/object/message_queue.cpp +++ b/core/object/message_queue.cpp @@ -227,16 +227,16 @@ void MessageQueue::statistics() { print_line("TOTAL BYTES: " + itos(buffer_end)); print_line("NULL count: " + itos(null_count)); - for (Map<StringName, int>::Element *E = set_count.front(); E; E = E->next()) { - print_line("SET " + E->key() + ": " + itos(E->get())); + for (const KeyValue<StringName, int> &E : set_count) { + print_line("SET " + E.key + ": " + itos(E.value)); } - for (Map<Callable, int>::Element *E = call_count.front(); E; E = E->next()) { - print_line("CALL " + E->key() + ": " + itos(E->get())); + for (const KeyValue<Callable, int> &E : call_count) { + print_line("CALL " + E.key + ": " + itos(E.value)); } - for (Map<int, int>::Element *E = notify_count.front(); E; E = E->next()) { - print_line("NOTIFY " + itos(E->key()) + ": " + itos(E->get())); + for (const KeyValue<int, int> &E : notify_count) { + print_line("NOTIFY " + itos(E.key) + ": " + itos(E.value)); } } diff --git a/core/object/method_bind.cpp b/core/object/method_bind.cpp index c53104fe3f..642e27c41d 100644 --- a/core/object/method_bind.cpp +++ b/core/object/method_bind.cpp @@ -63,12 +63,15 @@ uint32_t MethodBind::get_hash() const { return hash; } -#ifdef DEBUG_METHODS_ENABLED PropertyInfo MethodBind::get_argument_info(int p_argument) const { ERR_FAIL_INDEX_V(p_argument, get_argument_count(), PropertyInfo()); PropertyInfo info = _gen_argument_type_info(p_argument); +#ifdef DEBUG_METHODS_ENABLED info.name = p_argument < arg_names.size() ? String(arg_names[p_argument]) : String("arg" + itos(p_argument)); +#else + info.name = String("arg" + itos(p_argument)); +#endif return info; } @@ -76,7 +79,6 @@ PropertyInfo MethodBind::get_return_info() const { return _gen_argument_type_info(-1); } -#endif void MethodBind::_set_const(bool p_const) { _const = p_const; } @@ -109,7 +111,6 @@ void MethodBind::set_default_arguments(const Vector<Variant> &p_defargs) { default_argument_count = default_arguments.size(); } -#ifdef DEBUG_METHODS_ENABLED void MethodBind::_generate_argument_types(int p_count) { set_argument_count(p_count); @@ -123,17 +124,13 @@ void MethodBind::_generate_argument_types(int p_count) { argument_types = argt; } -#endif - MethodBind::MethodBind() { static int last_id = 0; method_id = last_id++; } MethodBind::~MethodBind() { -#ifdef DEBUG_METHODS_ENABLED if (argument_types) { memdelete_arr(argument_types); } -#endif } diff --git a/core/object/method_bind.h b/core/object/method_bind.h index b0b379873e..ee003099a0 100644 --- a/core/object/method_bind.h +++ b/core/object/method_bind.h @@ -64,18 +64,16 @@ class MethodBind { bool _returns = false; protected: -#ifdef DEBUG_METHODS_ENABLED Variant::Type *argument_types = nullptr; +#ifdef DEBUG_METHODS_ENABLED Vector<StringName> arg_names; #endif void _set_const(bool p_const); void _set_returns(bool p_returns); -#ifdef DEBUG_METHODS_ENABLED virtual Variant::Type _gen_argument_type(int p_arg) const = 0; virtual PropertyInfo _gen_argument_type_info(int p_arg) const = 0; void _generate_argument_types(int p_count); -#endif void set_argument_count(int p_count) { argument_count = p_count; } public: @@ -102,7 +100,6 @@ public: } } -#ifdef DEBUG_METHODS_ENABLED _FORCE_INLINE_ Variant::Type get_argument_type(int p_argument) const { ERR_FAIL_COND_V(p_argument < -1 || p_argument > argument_count, Variant::NIL); return argument_types[p_argument + 1]; @@ -111,6 +108,7 @@ public: PropertyInfo get_argument_info(int p_argument) const; PropertyInfo get_return_info() const; +#ifdef DEBUG_METHODS_ENABLED void set_argument_names(const Vector<StringName> &p_names); // Set by ClassDB, can't be inferred otherwise. Vector<StringName> get_argument_names() const; @@ -149,12 +147,9 @@ public: protected: NativeCall call_method = nullptr; -#ifdef DEBUG_METHODS_ENABLED MethodInfo arguments; -#endif public: -#ifdef DEBUG_METHODS_ENABLED virtual PropertyInfo _gen_argument_type_info(int p_arg) const { if (p_arg < 0) { return arguments.return_val; @@ -169,13 +164,10 @@ public: return _gen_argument_type_info(p_arg).type; } +#ifdef DEBUG_METHODS_ENABLED virtual GodotTypeInfo::Metadata get_argument_meta(int) const { return GodotTypeInfo::METADATA_NONE; } -#else - virtual Variant::Type _gen_argument_type(int p_arg) const { - return Variant::NIL; - } #endif virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { @@ -185,25 +177,29 @@ public: void set_method_info(const MethodInfo &p_info, bool p_return_nil_is_variant) { set_argument_count(p_info.arguments.size()); -#ifdef DEBUG_METHODS_ENABLED Variant::Type *at = memnew_arr(Variant::Type, p_info.arguments.size() + 1); at[0] = p_info.return_val.type; if (p_info.arguments.size()) { +#ifdef DEBUG_METHODS_ENABLED Vector<StringName> names; names.resize(p_info.arguments.size()); +#endif for (int i = 0; i < p_info.arguments.size(); i++) { at[i + 1] = p_info.arguments[i].type; +#ifdef DEBUG_METHODS_ENABLED names.write[i] = p_info.arguments[i].name; +#endif } +#ifdef DEBUG_METHODS_ENABLED set_argument_names(names); +#endif } argument_types = at; arguments = p_info; if (p_return_nil_is_variant) { arguments.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; } -#endif } virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { @@ -248,7 +244,6 @@ class MethodBindT : public MethodBind { void (MB_T::*method)(P...); protected: -#ifdef DEBUG_METHODS_ENABLED // GCC raises warnings in the case P = {} as the comparison is always false... #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push @@ -270,7 +265,6 @@ protected: call_get_argument_type_info<P...>(p_arg, pi); return pi; } -#endif public: #ifdef DEBUG_METHODS_ENABLED @@ -298,9 +292,7 @@ public: MethodBindT(void (MB_T::*p_method)(P...)) { method = p_method; -#ifdef DEBUG_METHODS_ENABLED _generate_argument_types(sizeof...(P)); -#endif set_argument_count(sizeof...(P)); } }; @@ -327,7 +319,6 @@ class MethodBindTC : public MethodBind { void (MB_T::*method)(P...) const; protected: -#ifdef DEBUG_METHODS_ENABLED // GCC raises warnings in the case P = {} as the comparison is always false... #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push @@ -349,7 +340,6 @@ protected: call_get_argument_type_info<P...>(p_arg, pi); return pi; } -#endif public: #ifdef DEBUG_METHODS_ENABLED @@ -378,9 +368,7 @@ public: MethodBindTC(void (MB_T::*p_method)(P...) const) { method = p_method; _set_const(true); -#ifdef DEBUG_METHODS_ENABLED _generate_argument_types(sizeof...(P)); -#endif set_argument_count(sizeof...(P)); } }; @@ -408,7 +396,6 @@ class MethodBindTR : public MethodBind { (P...); protected: -#ifdef DEBUG_METHODS_ENABLED // GCC raises warnings in the case P = {} as the comparison is always false... #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push @@ -434,7 +421,6 @@ protected: #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif -#endif public: #ifdef DEBUG_METHODS_ENABLED @@ -468,9 +454,7 @@ public: MethodBindTR(R (MB_T::*p_method)(P...)) { method = p_method; _set_returns(true); -#ifdef DEBUG_METHODS_ENABLED _generate_argument_types(sizeof...(P)); -#endif set_argument_count(sizeof...(P)); } }; @@ -499,7 +483,6 @@ class MethodBindTRC : public MethodBind { (P...) const; protected: -#ifdef DEBUG_METHODS_ENABLED // GCC raises warnings in the case P = {} as the comparison is always false... #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push @@ -525,7 +508,6 @@ protected: #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif -#endif public: #ifdef DEBUG_METHODS_ENABLED @@ -560,9 +542,7 @@ public: method = p_method; _set_returns(true); _set_const(true); -#ifdef DEBUG_METHODS_ENABLED _generate_argument_types(sizeof...(P)); -#endif set_argument_count(sizeof...(P)); } }; diff --git a/core/object/object.cpp b/core/object/object.cpp index 3335942fb3..b5797a4633 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1409,7 +1409,7 @@ void Object::_disconnect(const StringName &p_signal, const Callable &p_callable, if (!p_force) { slot->reference_count--; // by default is zero, if it was not referenced it will go below it - if (slot->reference_count >= 0) { + if (slot->reference_count > 0) { return; } } diff --git a/core/object/object.h b/core/object/object.h index 102776a589..a44d921bff 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -132,6 +132,8 @@ enum PropertyUsageFlags { PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 26, // when loading, the resource for this property can be set at the end of loading PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 27, // For Object properties, instantiate them when creating in editor. PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 28, //for project or editor settings, show when basic settings are selected + PROPERTY_USAGE_READ_ONLY = 1 << 29, // Mark a property as read-only in the inspector. + PROPERTY_USAGE_ARRAY = 1 << 30, // Used in the inspector to group properties as elements of an array. PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK, PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED, @@ -146,6 +148,10 @@ enum PropertyUsageFlags { #define ADD_SUBGROUP(m_name, m_prefix) ::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix) #define ADD_LINKED_PROPERTY(m_property, m_linked_property) ::ClassDB::add_linked_property(get_class_static(), m_property, m_linked_property) +#define ADD_ARRAY_COUNT(m_label, m_count_property, m_count_property_setter, m_count_property_getter, m_prefix) ClassDB::add_property_array_count(get_class_static(), m_label, m_count_property, _scs_create(m_count_property_setter), _scs_create(m_count_property_getter), m_prefix) +#define ADD_ARRAY_COUNT_WITH_USAGE_FLAGS(m_label, m_count_property, m_count_property_setter, m_count_property_getter, m_prefix, m_property_usage_flags) ClassDB::add_property_array_count(get_class_static(), m_label, m_count_property, _scs_create(m_count_property_setter), _scs_create(m_count_property_getter), m_prefix, m_property_usage_flags) +#define ADD_ARRAY(m_array_path, m_prefix) ClassDB::add_property_array(get_class_static(), m_array_path, m_prefix) + struct PropertyInfo { Variant::Type type = Variant::NIL; String name; diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 0fb8c7350c..b0ce46ca2b 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -93,8 +93,8 @@ Dictionary Script::_get_script_constant_map() { Dictionary ret; Map<StringName, Variant> map; get_constants(&map); - for (Map<StringName, Variant>::Element *E = map.front(); E; E = E->next()) { - ret[E->key()] = E->value(); + for (const KeyValue<StringName, Variant> &E : map) { + ret[E.key] = E.value; } return ret; } diff --git a/core/object/script_language.h b/core/object/script_language.h index 385bf79c1a..8d76cbf479 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -32,8 +32,8 @@ #define SCRIPT_LANGUAGE_H #include "core/doc_data.h" -#include "core/io/multiplayer_api.h" #include "core/io/resource.h" +#include "core/multiplayer/multiplayer.h" #include "core/templates/map.h" #include "core/templates/pair.h" @@ -159,7 +159,7 @@ public: virtual bool is_placeholder_fallback_enabled() const { return false; } - virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const = 0; + virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const = 0; Script() {} }; @@ -200,7 +200,7 @@ public: virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid); virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid); - virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const = 0; + virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const = 0; virtual ScriptLanguage *get_language() = 0; virtual ~ScriptInstance(); @@ -419,7 +419,7 @@ public: virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr); virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid = nullptr); - virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const { return Vector<MultiplayerAPI::RPCConfig>(); } + virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const { return Vector<Multiplayer::RPCConfig>(); } PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner); ~PlaceHolderScriptInstance(); diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index b7d2bac96d..9c84c2add7 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -32,6 +32,7 @@ #include "core/io/resource.h" #include "core/os/os.h" +#include "core/templates/local_vector.h" void UndoRedo::_discard_redo() { if (current_action == actions.size() - 1) { @@ -85,10 +86,17 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { current_action = actions.size() - 2; if (p_mode == MERGE_ENDS) { - // Clear all do ops from last action, and delete all object references - List<Operation>::Element *E = actions.write[current_action + 1].do_ops.front(); + // Clear all do ops from last action if they are not forced kept + LocalVector<List<Operation>::Element *> to_remove; + for (List<Operation>::Element *E = actions.write[current_action + 1].do_ops.front(); E; E = E->next()) { + if (!E->get().force_keep_in_merge_ends) { + to_remove.push_back(E); + } + } - while (E) { + for (unsigned int i = 0; i < to_remove.size(); i++) { + List<Operation>::Element *E = to_remove[i]; + // Delete all object references if (E->get().type == Operation::TYPE_REFERENCE) { Object *obj = ObjectDB::get_instance(E->get().object); @@ -96,9 +104,7 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { memdelete(obj); } } - - E = E->next(); - actions.write[current_action + 1].do_ops.pop_front(); + E->erase(); } } @@ -117,6 +123,8 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { } action_level++; + + force_keep_in_merge_ends = false; } void UndoRedo::add_do_method(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) { @@ -146,7 +154,7 @@ void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VAR ERR_FAIL_COND((current_action + 1) >= actions.size()); // No undo if the merge mode is MERGE_ENDS - if (merge_mode == MERGE_ENDS) { + if (!force_keep_in_merge_ends && merge_mode == MERGE_ENDS) { return; } @@ -157,6 +165,7 @@ void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VAR } undo_op.type = Operation::TYPE_METHOD; + undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; undo_op.name = p_method; for (int i = 0; i < VARIANT_ARG_MAX; i++) { @@ -187,7 +196,7 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property, ERR_FAIL_COND((current_action + 1) >= actions.size()); // No undo if the merge mode is MERGE_ENDS - if (merge_mode == MERGE_ENDS) { + if (!force_keep_in_merge_ends && merge_mode == MERGE_ENDS) { return; } @@ -198,6 +207,7 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property, } undo_op.type = Operation::TYPE_PROPERTY; + undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; undo_op.name = p_property; undo_op.args[0] = p_value; actions.write[current_action + 1].undo_ops.push_back(undo_op); @@ -223,7 +233,7 @@ void UndoRedo::add_undo_reference(Object *p_object) { ERR_FAIL_COND((current_action + 1) >= actions.size()); // No undo if the merge mode is MERGE_ENDS - if (merge_mode == MERGE_ENDS) { + if (!force_keep_in_merge_ends && merge_mode == MERGE_ENDS) { return; } @@ -234,9 +244,24 @@ void UndoRedo::add_undo_reference(Object *p_object) { } undo_op.type = Operation::TYPE_REFERENCE; + undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; actions.write[current_action + 1].undo_ops.push_back(undo_op); } +void UndoRedo::start_force_keep_in_merge_ends() { + ERR_FAIL_COND(action_level <= 0); + ERR_FAIL_COND((current_action + 1) >= actions.size()); + + force_keep_in_merge_ends = true; +} + +void UndoRedo::end_force_keep_in_merge_ends() { + ERR_FAIL_COND(action_level <= 0); + ERR_FAIL_COND((current_action + 1) >= actions.size()); + + force_keep_in_merge_ends = false; +} + void UndoRedo::_pop_history_tail() { _discard_redo(); @@ -538,6 +563,9 @@ void UndoRedo::_bind_methods() { ClassDB::bind_method(D_METHOD("add_do_reference", "object"), &UndoRedo::add_do_reference); ClassDB::bind_method(D_METHOD("add_undo_reference", "object"), &UndoRedo::add_undo_reference); + ClassDB::bind_method(D_METHOD("start_force_keep_in_merge_ends"), &UndoRedo::start_force_keep_in_merge_ends); + ClassDB::bind_method(D_METHOD("end_force_keep_in_merge_ends"), &UndoRedo::end_force_keep_in_merge_ends); + ClassDB::bind_method(D_METHOD("get_history_count"), &UndoRedo::get_history_count); ClassDB::bind_method(D_METHOD("get_current_action"), &UndoRedo::get_current_action); ClassDB::bind_method(D_METHOD("get_action_name", "id"), &UndoRedo::get_action_name); diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h index d1ce252d86..a757d154e2 100644 --- a/core/object/undo_redo.h +++ b/core/object/undo_redo.h @@ -61,6 +61,7 @@ private: }; Type type; + bool force_keep_in_merge_ends; Ref<RefCounted> ref; ObjectID object; StringName name; @@ -76,6 +77,7 @@ private: Vector<Action> actions; int current_action = -1; + bool force_keep_in_merge_ends = false; int action_level = 0; MergeMode merge_mode = MERGE_DISABLE; bool merging = false; @@ -109,6 +111,9 @@ public: void add_do_reference(Object *p_object); void add_undo_reference(Object *p_object); + void start_force_keep_in_merge_ends(); + void end_force_keep_in_merge_ends(); + bool is_committing_action() const; void commit_action(bool p_execute = true); diff --git a/core/os/os.cpp b/core/os/os.cpp index 63390919f4..7404ffdcd5 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -36,7 +36,6 @@ #include "core/io/file_access.h" #include "core/os/midi_driver.h" #include "core/version_generated.gen.h" -#include "servers/audio_server.h" #include <stdarg.h> @@ -76,12 +75,12 @@ void OS::add_logger(Logger *p_logger) { } } -void OS::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type) { +void OS::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, Logger::ErrorType p_type) { if (!_stderr_enabled) { return; } - _logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_type); + _logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_editor_notify, p_type); } void OS::print(const char *p_format, ...) { @@ -146,6 +145,10 @@ bool OS::is_stdout_verbose() const { return _verbose_stdout; } +bool OS::is_single_window() const { + return _single_window; +} + bool OS::is_stdout_debug_enabled() const { return _debug_stdout; } @@ -227,6 +230,12 @@ String OS::get_locale() const { return "en"; } +// Non-virtual helper to extract the 2 or 3-letter language code from +// `get_locale()` in a way that's consistent for all platforms. +String OS::get_locale_language() const { + return get_locale().left(3).replace("_", ""); +} + // Helper function to ensure that a dir name/path will be valid on the OS String OS::get_safe_dir_name(const String &p_dir_name, bool p_allow_dir_separator) const { Vector<String> invalid_chars = String(": * ? \" < > |").split(" "); @@ -272,6 +281,11 @@ String OS::get_bundle_resource_dir() const { return "."; } +// Path to macOS .app bundle embedded icon +String OS::get_bundle_icon_path() const { + return String(); +} + // OS specific path for user:// String OS::get_user_data_dir() const { return "."; @@ -357,9 +371,17 @@ void OS::set_has_server_feature_callback(HasServerFeatureCallback p_callback) { } bool OS::has_feature(const String &p_feature) { - if (p_feature == get_name()) { + // Feature tags are always lowercase for consistency. + if (p_feature == get_name().to_lower()) { return true; } + + // Catch-all `linuxbsd` feature tag that matches on both Linux and BSD. + // This is the one exposed in the project settings dialog. + if (p_feature == "linuxbsd" && (get_name() == "Linux" || get_name() == "FreeBSD" || get_name() == "NetBSD" || get_name() == "OpenBSD" || get_name() == "BSD")) { + return true; + } + #ifdef DEBUG_ENABLED if (p_feature == "debug") { return true; diff --git a/core/os/os.h b/core/os/os.h index 55b21266fc..6d7bc47407 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -52,6 +52,7 @@ class OS { int low_processor_usage_mode_sleep_usec = 10000; bool _verbose_stdout = false; bool _debug_stdout = false; + bool _single_window = false; String _local_clipboard; int _exit_code = EXIT_FAILURE; // unexpected exit is marked as failure int _orientation; @@ -109,7 +110,7 @@ public: static OS *get_singleton(); - void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type = Logger::ERR_ERROR); + void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, Logger::ErrorType p_type = Logger::ERR_ERROR); void print(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; @@ -224,6 +225,8 @@ public: void set_stdout_enabled(bool p_enabled); void set_stderr_enabled(bool p_enabled); + bool is_single_window() const; + virtual void disable_crash_handler() {} virtual bool is_disable_crash_handler() const { return false; } virtual void initialize_debugging() {} @@ -240,6 +243,7 @@ public: RenderThreadMode get_render_thread_mode() const { return _render_thread_mode; } virtual String get_locale() const; + String get_locale_language() const; String get_safe_dir_name(const String &p_dir_name, bool p_allow_dir_separator = false) const; virtual String get_godot_dir_name() const; @@ -248,6 +252,7 @@ public: virtual String get_config_path() const; virtual String get_cache_path() const; virtual String get_bundle_resource_dir() const; + virtual String get_bundle_icon_path() const; virtual String get_user_data_dir() const; virtual String get_resource_dir() const; diff --git a/core/os/thread.cpp b/core/os/thread.cpp index 73e31bdb3d..27aefc98de 100644 --- a/core/os/thread.cpp +++ b/core/os/thread.cpp @@ -28,6 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef PLATFORM_THREAD_OVERRIDE // See details in thread.h + #include "thread.h" #include "core/object/script_language.h" @@ -126,3 +128,4 @@ Thread::~Thread() { } #endif +#endif // PLATFORM_THREAD_OVERRIDE diff --git a/core/os/thread.h b/core/os/thread.h index 17ac82c650..59cb58ac57 100644 --- a/core/os/thread.h +++ b/core/os/thread.h @@ -28,6 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +// Define PLATFORM_THREAD_OVERRIDE in your platform's `platform_config.h` +// to use a custom Thread implementation defined in `platform/[your_platform]/platform_thread.h` +// Overriding the platform implementation is required in some proprietary platforms +#ifdef PLATFORM_THREAD_OVERRIDE +#include "platform_thread.h" +#else #ifndef THREAD_H #define THREAD_H @@ -116,3 +122,4 @@ public: }; #endif // THREAD_H +#endif // PLATFORM_THREAD_OVERRIDE diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 6c7d9cbd89..e33c21cc00 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -48,9 +48,6 @@ #include "core/io/image_loader.h" #include "core/io/json.h" #include "core/io/marshalls.h" -#include "core/io/multiplayer_api.h" -#include "core/io/multiplayer_peer.h" -#include "core/io/multiplayer_replicator.h" #include "core/io/packed_data_container.h" #include "core/io/packet_peer.h" #include "core/io/packet_peer_dtls.h" @@ -70,6 +67,9 @@ #include "core/math/geometry_3d.h" #include "core/math/random_number_generator.h" #include "core/math/triangle_mesh.h" +#include "core/multiplayer/multiplayer_api.h" +#include "core/multiplayer/multiplayer_peer.h" +#include "core/multiplayer/multiplayer_replicator.h" #include "core/object/class_db.h" #include "core/object/undo_redo.h" #include "core/os/main_loop.h" @@ -169,11 +169,13 @@ void register_core_types() { GDREGISTER_VIRTUAL_CLASS(IP); GDREGISTER_VIRTUAL_CLASS(StreamPeer); + GDREGISTER_CLASS(StreamPeerExtension); GDREGISTER_CLASS(StreamPeerBuffer); GDREGISTER_CLASS(StreamPeerTCP); GDREGISTER_CLASS(TCPServer); GDREGISTER_VIRTUAL_CLASS(PacketPeer); + GDREGISTER_CLASS(PacketPeerExtension); GDREGISTER_CLASS(PacketPeerStream); GDREGISTER_CLASS(PacketPeerUDP); GDREGISTER_CLASS(UDPServer); @@ -197,6 +199,7 @@ void register_core_types() { ResourceLoader::add_resource_format_loader(resource_format_loader_crypto); GDREGISTER_VIRTUAL_CLASS(MultiplayerPeer); + GDREGISTER_VIRTUAL_CLASS(MultiplayerPeerExtension); GDREGISTER_VIRTUAL_CLASS(MultiplayerReplicator); GDREGISTER_CLASS(MultiplayerAPI); GDREGISTER_CLASS(MainLoop); diff --git a/core/string/optimized_translation.cpp b/core/string/optimized_translation.cpp index 5863bd1c46..f8be564740 100644 --- a/core/string/optimized_translation.cpp +++ b/core/string/optimized_translation.cpp @@ -64,7 +64,6 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) { int idx = 0; int total_compression_size = 0; - int total_string_size = 0; for (const StringName &E : keys) { //hash string @@ -102,7 +101,6 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) { compressed.write[idx] = ps; total_compression_size += ps.compressed.size(); - total_string_size += src_s.size(); idx++; } @@ -147,26 +145,23 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) { uint32_t *btw = (uint32_t *)&btwb[0]; int btindex = 0; - int collisions = 0; for (int i = 0; i < size; i++) { const Map<uint32_t, int> &t = table[i]; if (t.size() == 0) { htw[i] = 0xFFFFFFFF; //nothing continue; - } else if (t.size() > 1) { - collisions += t.size() - 1; } htw[i] = btindex; btw[btindex++] = t.size(); btw[btindex++] = hfunc_table[i]; - for (Map<uint32_t, int>::Element *E = t.front(); E; E = E->next()) { - btw[btindex++] = E->key(); - btw[btindex++] = compressed[E->get()].offset; - btw[btindex++] = compressed[E->get()].compressed.size(); - btw[btindex++] = compressed[E->get()].orig_len; + for (const KeyValue<uint32_t, int> &E : t) { + btw[btindex++] = E.key; + btw[btindex++] = compressed[E.value].offset; + btw[btindex++] = compressed[E.value].compressed.size(); + btw[btindex++] = compressed[E.value].orig_len; } } diff --git a/core/string/translation.cpp b/core/string/translation.cpp index cb7d924556..cf61467d08 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -35,7 +35,6 @@ #include "core/os/os.h" #ifdef TOOLS_ENABLED -#include "editor/editor_settings.h" #include "main/main.h" #endif @@ -810,9 +809,12 @@ static const char *locale_names[] = { // - https://msdn.microsoft.com/en-us/library/windows/desktop/ms693062(v=vs.85).aspx static const char *locale_renames[][2] = { - { "in", "id" }, // Indonesian - { "iw", "he" }, // Hebrew - { "no", "nb" }, // Norwegian Bokmål + { "in", "id" }, // Indonesian + { "iw", "he" }, // Hebrew + { "no", "nb" }, // Norwegian Bokmål + { "C", "en" }, // "C" is the simple/default/untranslated Computer locale. + // ASCII-only, English, no currency symbols. Godot treats this as "en". + // See https://unix.stackexchange.com/a/87763/164141 "The C locale is"... { nullptr, nullptr } }; @@ -820,8 +822,8 @@ static const char *locale_renames[][2] = { Dictionary Translation::_get_messages() const { Dictionary d; - for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) { - d[E->key()] = E->value(); + for (const KeyValue<StringName, StringName> &E : translation_map) { + d[E.key] = E.value; } return d; } @@ -830,8 +832,8 @@ Vector<String> Translation::_get_message_list() const { Vector<String> msgs; msgs.resize(translation_map.size()); int idx = 0; - for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) { - msgs.set(idx, E->key()); + for (const KeyValue<StringName, StringName> &E : translation_map) { + msgs.set(idx, E.key); idx += 1; } @@ -875,6 +877,11 @@ void Translation::add_plural_message(const StringName &p_src_text, const Vector< } StringName Translation::get_message(const StringName &p_src_text, const StringName &p_context) const { + StringName ret; + if (GDVIRTUAL_CALL(_get_message, p_src_text, p_context, ret)) { + return ret; + } + if (p_context != StringName()) { WARN_PRINT("Translation class doesn't handle context. Using context in get_message() on a Translation instance is probably a mistake. \nUse a derived Translation class that handles context, such as TranslationPO class"); } @@ -888,6 +895,11 @@ StringName Translation::get_message(const StringName &p_src_text, const StringNa } StringName Translation::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const { + StringName ret; + if (GDVIRTUAL_CALL(_get_plural_message, p_src_text, p_plural_text, p_n, p_context, ret)) { + return ret; + } + WARN_PRINT("Translation class doesn't handle plural messages. Calling get_plural_message() on a Translation instance is probably a mistake. \nUse a derived Translation class that handles plurals, such as TranslationPO class"); return get_message(p_src_text); } @@ -901,8 +913,8 @@ void Translation::erase_message(const StringName &p_src_text, const StringName & } void Translation::get_message_list(List<StringName> *r_messages) const { - for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) { - r_messages->push_back(E->key()); + for (const KeyValue<StringName, StringName> &E : translation_map) { + r_messages->push_back(E.key); } } @@ -923,6 +935,9 @@ void Translation::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_messages"), &Translation::_set_messages); ClassDB::bind_method(D_METHOD("_get_messages"), &Translation::_get_messages); + GDVIRTUAL_BIND(_get_plural_message, "src_message", "src_plural_message", "n", "context"); + GDVIRTUAL_BIND(_get_message, "src_message", "context"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "messages", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_messages", "_get_messages"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "locale"), "set_locale", "get_locale"); } diff --git a/core/string/translation.h b/core/string/translation.h index 4f179ac0fe..6aec0bb8ea 100644 --- a/core/string/translation.h +++ b/core/string/translation.h @@ -32,6 +32,8 @@ #define TRANSLATION_H #include "core/io/resource.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" class Translation : public Resource { GDCLASS(Translation, Resource); @@ -48,6 +50,9 @@ class Translation : public Resource { protected: static void _bind_methods(); + GDVIRTUAL2RC(StringName, _get_message, StringName, StringName); + GDVIRTUAL4RC(StringName, _get_plural_message, StringName, StringName, int, StringName); + public: void set_locale(const String &p_locale); _FORCE_INLINE_ String get_locale() const { return locale; } diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index a3b5356b1d..daeb7fbd17 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -1551,19 +1551,21 @@ String String::num_real(double p_num, bool p_trailing) { bool neg = p_num < 0; p_num = ABS(p_num); - int intn = (int)p_num; + int64_t intn = (int64_t)p_num; // Decimal part. if (intn != p_num) { - double dec = p_num - (double)(intn); + double dec = p_num - (double)intn; int digit = 0; -#if REAL_T_IS_DOUBLE +#ifdef REAL_T_IS_DOUBLE int decimals = 14; + double tolerance = 1e-14; #else int decimals = 6; + double tolerance = 1e-6; #endif // We want to align the digits to the above sane default, so we only // need to subtract log10 for numbers with a positive power of ten. @@ -1575,16 +1577,21 @@ String String::num_real(double p_num, bool p_trailing) { decimals = MAX_DECIMALS; } - int dec_int = 0; - int dec_max = 0; + // In case the value ends up ending in "99999", we want to add a + // tiny bit to the value we're checking when deciding when to stop, + // so we multiply by slightly above 1 (1 + 1e-7 or 1e-15). + double check_multiplier = 1 + tolerance / 10; + + int64_t dec_int = 0; + int64_t dec_max = 0; while (true) { dec *= 10.0; - dec_int = dec_int * 10 + (int)dec % 10; + dec_int = dec_int * 10 + (int64_t)dec % 10; dec_max = dec_max * 10 + 9; digit++; - if ((dec - (double)((int)dec)) < 1e-6) { + if ((dec - (double)(int64_t)(dec * check_multiplier)) < tolerance) { break; } @@ -1594,7 +1601,7 @@ String String::num_real(double p_num, bool p_trailing) { } dec *= 10; - int last = (int)dec % 10; + int last = (int64_t)dec % 10; if (last > 5) { if (dec_int == dec_max) { @@ -1653,13 +1660,13 @@ String String::num_scientific(double p_num) { #if defined(__GNUC__) || defined(_MSC_VER) -#if (defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER < 1900)) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT) - // MinGW and old MSC require _set_output_format() to conform to C99 output for printf +#if defined(__MINGW32__) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT) + // MinGW requires _set_output_format() to conform to C99 output for printf unsigned int old_exponent_format = _set_output_format(_TWO_DIGIT_EXPONENT); #endif snprintf(buf, 256, "%lg", p_num); -#if (defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER < 1900)) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT) +#if defined(__MINGW32__) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT) _set_output_format(old_exponent_format); #endif @@ -3555,7 +3562,7 @@ String String::strip_edges(bool left, bool right) const { } if (right) { - for (int i = (int)(len - 1); i >= 0; i--) { + for (int i = len - 1; i >= 0; i--) { if (operator[](i) <= 32) { end--; } else { @@ -4329,23 +4336,39 @@ bool String::is_relative_path() const { } String String::get_base_dir() const { - int basepos = find(":/"); - if (basepos == -1) { - basepos = find(":\\"); + int end = 0; + + // url scheme style base + int basepos = find("://"); + if (basepos != -1) { + end = basepos + 3; + } + + // windows top level directory base + if (end == 0) { + basepos = find(":/"); + if (basepos == -1) { + basepos = find(":\\"); + } + if (basepos != -1) { + end = basepos + 2; + } + } + + // unix root directory base + if (end == 0) { + if (begins_with("/")) { + end = 1; + } } + String rs; String base; - if (basepos != -1) { - int end = basepos + 3; + if (end != 0) { rs = substr(end, length()); base = substr(0, end); } else { - if (begins_with("/")) { - rs = substr(1, length()); - base = "/"; - } else { - rs = *this; - } + rs = *this; } int sep = MAX(rs.rfind("/"), rs.rfind("\\")); diff --git a/core/string/ustring.h b/core/string/ustring.h index 6a4b40da60..1d80ccf58d 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -51,11 +51,15 @@ class CharProxy { CowData<T> &_cowdata; static const T _null = 0; - _FORCE_INLINE_ CharProxy(const int &p_index, CowData<T> &cowdata) : + _FORCE_INLINE_ CharProxy(const int &p_index, CowData<T> &p_cowdata) : _index(p_index), - _cowdata(cowdata) {} + _cowdata(p_cowdata) {} public: + _FORCE_INLINE_ CharProxy(const CharProxy<T> &p_other) : + _index(p_other._index), + _cowdata(p_other._cowdata) {} + _FORCE_INLINE_ operator T() const { if (unlikely(_index == _cowdata.size())) { return _null; @@ -68,12 +72,12 @@ public: return _cowdata.ptr() + _index; } - _FORCE_INLINE_ void operator=(const T &other) const { - _cowdata.set(_index, other); + _FORCE_INLINE_ void operator=(const T &p_other) const { + _cowdata.set(_index, p_other); } - _FORCE_INLINE_ void operator=(const CharProxy<T> &other) const { - _cowdata.set(_index, other.operator T()); + _FORCE_INLINE_ void operator=(const CharProxy<T> &p_other) const { + _cowdata.set(_index, p_other.operator T()); } }; @@ -523,10 +527,10 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St #define TTRGET(m_value) TTR(m_value) #else -#define TTR(m_value) (String()) -#define TTRN(m_value) (String()) -#define DTR(m_value) (String()) -#define DTRN(m_value) (String()) +#define TTR(m_value) String() +#define TTRN(m_value) String() +#define DTR(m_value) String() +#define DTRN(m_value) String() #define TTRC(m_value) (m_value) #define TTRGET(m_value) (m_value) #endif diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h index ba9babe0af..9b8c0eb528 100644 --- a/core/templates/cowdata.h +++ b/core/templates/cowdata.h @@ -49,6 +49,12 @@ class VMap; SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t) #endif +// Silence a false positive warning (see GH-52119). +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wplacement-new" +#endif + template <class T> class CowData { template <class TV> @@ -380,4 +386,8 @@ CowData<T>::~CowData() { _unref(_ptr); } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + #endif // COWDATA_H diff --git a/core/templates/hash_map.h b/core/templates/hash_map.h index 1257b54449..1634219c23 100644 --- a/core/templates/hash_map.h +++ b/core/templates/hash_map.h @@ -62,7 +62,9 @@ public: TKey key; TData data; - Pair() {} + Pair(const TKey &p_key) : + key(p_key), + data() {} Pair(const TKey &p_key, const TData &p_data) : key(p_key), data(p_data) { @@ -90,6 +92,12 @@ public: const TData &value() const { return pair.value(); } + + Element(const TKey &p_key) : + pair(p_key) {} + Element(const Element &p_other) : + hash(p_other.hash), + pair(p_other.pair.key, p_other.pair.data) {} }; private: @@ -192,14 +200,12 @@ private: Element *create_element(const TKey &p_key) { /* if element doesn't exist, create it */ - Element *e = memnew(Element); + Element *e = memnew(Element(p_key)); ERR_FAIL_COND_V_MSG(!e, nullptr, "Out of memory."); uint32_t hash = Hasher::hash(p_key); uint32_t index = hash & ((1 << hash_table_power) - 1); e->next = hash_table[index]; e->hash = hash; - e->pair.key = p_key; - e->pair.data = TData(); hash_table[index] = e; elements++; @@ -228,9 +234,7 @@ private: const Element *e = p_t.hash_table[i]; while (e) { - Element *le = memnew(Element); /* local element */ - - *le = *e; /* copy data */ + Element *le = memnew(Element(*e)); /* local element */ /* add to list and reassign pointers */ le->next = hash_table[i]; diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h index 2e932f9f26..c1a7c4146e 100644 --- a/core/templates/hashfuncs.h +++ b/core/templates/hashfuncs.h @@ -74,6 +74,13 @@ static inline uint32_t hash_djb2_one_32(uint32_t p_in, uint32_t p_prev = 5381) { return ((p_prev << 5) + p_prev) + p_in; } +/** + * Thomas Wang's 64-bit to 32-bit Hash function: + * https://web.archive.org/web/20071223173210/https:/www.concentric.net/~Ttwang/tech/inthash.htm + * + * @param p_int - 64-bit unsigned integer key to be hashed + * @return unsigned 32-bit value representing hashcode + */ static inline uint32_t hash_one_uint64(const uint64_t p_int) { uint64_t v = p_int; v = (~v) + (v << 18); // v = (v << 18) - v - 1; @@ -82,7 +89,7 @@ static inline uint32_t hash_one_uint64(const uint64_t p_int) { v = v ^ (v >> 11); v = v + (v << 6); v = v ^ (v >> 22); - return (int)v; + return uint32_t(v); } static inline uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev = 5381) { diff --git a/core/templates/pooled_list.h b/core/templates/pooled_list.h index b4a6d2d1dd..b139dadb75 100644 --- a/core/templates/pooled_list.h +++ b/core/templates/pooled_list.h @@ -28,13 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#pragma once +#ifndef POOLED_LIST_H +#define POOLED_LIST_H + +#include "core/templates/local_vector.h" // Simple template to provide a pool with O(1) allocate and free. // The freelist could alternatively be a linked list placed within the unused elements // to use less memory, however a separate freelist is probably more cache friendly. - -// NOTE : Take great care when using this with non POD types. The construction and destruction +// +// NOTE: Take great care when using this with non POD types. The construction and destruction // is done in the LocalVector, NOT as part of the pool. So requesting a new item does not guarantee // a constructor is run, and free does not guarantee a destructor. // You should generally handle clearing @@ -42,9 +45,6 @@ // This is by design for fastest use in the BVH. If you want a more general pool // that does call constructors / destructors on request / free, this should probably be // a separate template. - -#include "core/templates/local_vector.h" - template <class T, bool force_trivial = false> class PooledList { LocalVector<T, uint32_t, force_trivial> list; @@ -93,3 +93,5 @@ public: _used_size--; } }; + +#endif // POOLED_LIST_H diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h index e947d2211c..71d41eacc4 100644 --- a/core/templates/rid_owner.h +++ b/core/templates/rid_owner.h @@ -151,7 +151,7 @@ public: return _allocate_rid(); } - _FORCE_INLINE_ T *getornull(const RID &p_rid, bool p_initialize = false) { + _FORCE_INLINE_ T *get_or_null(const RID &p_rid, bool p_initialize = false) { if (p_rid == RID()) { return nullptr; } @@ -210,12 +210,12 @@ public: return ptr; } void initialize_rid(RID p_rid) { - T *mem = getornull(p_rid, true); + T *mem = get_or_null(p_rid, true); ERR_FAIL_COND(!mem); memnew_placement(mem, T); } void initialize_rid(RID p_rid, const T &p_value) { - T *mem = getornull(p_rid, true); + T *mem = get_or_null(p_rid, true); ERR_FAIL_COND(!mem); memnew_placement(mem, T(p_value)); } @@ -399,8 +399,8 @@ public: alloc.initialize_rid(p_rid, p_ptr); } - _FORCE_INLINE_ T *getornull(const RID &p_rid) { - T **ptr = alloc.getornull(p_rid); + _FORCE_INLINE_ T *get_or_null(const RID &p_rid) { + T **ptr = alloc.get_or_null(p_rid); if (unlikely(!ptr)) { return nullptr; } @@ -408,7 +408,7 @@ public: } _FORCE_INLINE_ void replace(const RID &p_rid, T *p_new_ptr) { - T **ptr = alloc.getornull(p_rid); + T **ptr = alloc.get_or_null(p_rid); ERR_FAIL_COND(!ptr); *ptr = p_new_ptr; } @@ -469,8 +469,8 @@ public: alloc.initialize_rid(p_rid, p_ptr); } - _FORCE_INLINE_ T *getornull(const RID &p_rid) { - return alloc.getornull(p_rid); + _FORCE_INLINE_ T *get_or_null(const RID &p_rid) { + return alloc.get_or_null(p_rid); } _FORCE_INLINE_ bool owns(const RID &p_rid) { diff --git a/core/templates/search_array.h b/core/templates/search_array.h new file mode 100644 index 0000000000..8efc32df82 --- /dev/null +++ b/core/templates/search_array.h @@ -0,0 +1,67 @@ +/*************************************************************************/ +/* search_array.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SEARCH_ARRAY_H +#define SEARCH_ARRAY_H + +#include <core/templates/sort_array.h> + +template <class T, class Comparator = _DefaultComparator<T>> +class SearchArray { +public: + Comparator compare; + + inline int bisect(const T *p_array, int p_len, const T &p_value, bool p_before) const { + int lo = 0; + int hi = p_len; + if (p_before) { + while (lo < hi) { + const int mid = (lo + hi) / 2; + if (compare(p_array[mid], p_value)) { + lo = mid + 1; + } else { + hi = mid; + } + } + } else { + while (lo < hi) { + const int mid = (lo + hi) / 2; + if (compare(p_value, p_array[mid])) { + hi = mid; + } else { + lo = mid + 1; + } + } + } + return lo; + } +}; + +#endif // SEARCH_ARRAY_H diff --git a/core/templates/vector.h b/core/templates/vector.h index 033345d04c..4b008a45a4 100644 --- a/core/templates/vector.h +++ b/core/templates/vector.h @@ -40,6 +40,7 @@ #include "core/error/error_macros.h" #include "core/os/memory.h" #include "core/templates/cowdata.h" +#include "core/templates/search_array.h" #include "core/templates/sort_array.h" template <class T> @@ -92,7 +93,7 @@ public: void append_array(Vector<T> p_other); - bool has(const T &p_val) { + bool has(const T &p_val) const { return find(p_val, 0) != -1; } @@ -112,6 +113,11 @@ public: sort_custom<_DefaultComparator<T>>(); } + int bsearch(const T &p_value, bool p_before) { + SearchArray<T> search; + return search.bisect(ptrw(), size(), p_value, p_before); + } + Vector<T> duplicate() { return *this; } diff --git a/core/typedefs.h b/core/typedefs.h index dde254af23..8ca3d13e63 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -62,9 +62,9 @@ #endif #endif -// Should always inline, except in debug builds because it makes debugging harder. +// Should always inline, except in dev builds because it makes debugging harder. #ifndef _FORCE_INLINE_ -#ifdef DISABLE_FORCED_INLINE +#ifdef DEV_ENABLED #define _FORCE_INLINE_ inline #else #define _FORCE_INLINE_ _ALWAYS_INLINE_ diff --git a/core/variant/array.cpp b/core/variant/array.cpp index 8373cbd4e8..b4d6dffc6f 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -34,6 +34,7 @@ #include "core/object/class_db.h" #include "core/object/script_language.h" #include "core/templates/hashfuncs.h" +#include "core/templates/search_array.h" #include "core/templates/vector.h" #include "core/variant/callable.h" #include "core/variant/variant.h" @@ -484,44 +485,19 @@ void Array::shuffle() { } } -template <typename Less> -_FORCE_INLINE_ int bisect(const Vector<Variant> &p_array, const Variant &p_value, bool p_before, const Less &p_less) { - int lo = 0; - int hi = p_array.size(); - if (p_before) { - while (lo < hi) { - const int mid = (lo + hi) / 2; - if (p_less(p_array.get(mid), p_value)) { - lo = mid + 1; - } else { - hi = mid; - } - } - } else { - while (lo < hi) { - const int mid = (lo + hi) / 2; - if (p_less(p_value, p_array.get(mid))) { - hi = mid; - } else { - lo = mid + 1; - } - } - } - return lo; -} - int Array::bsearch(const Variant &p_value, bool p_before) { ERR_FAIL_COND_V(!_p->typed.validate(p_value, "binary search"), -1); - return bisect(_p->array, p_value, p_before, _ArrayVariantSort()); + SearchArray<Variant, _ArrayVariantSort> avs; + return avs.bisect(_p->array.ptrw(), _p->array.size(), p_value, p_before); } int Array::bsearch_custom(const Variant &p_value, Callable p_callable, bool p_before) { ERR_FAIL_COND_V(!_p->typed.validate(p_value, "custom binary search"), -1); - _ArrayVariantSortCustom less; - less.func = p_callable; + SearchArray<Variant, _ArrayVariantSortCustom> avs; + avs.compare.func = p_callable; - return bisect(_p->array, p_value, p_before, less); + return avs.bisect(_p->array.ptrw(), _p->array.size(), p_value, p_before); } void Array::reverse() { diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index 3b2c837096..8592a1dc62 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -563,13 +563,11 @@ void call_with_validated_variant_args_static_method_ret(R (*p_method)(P...), con // GCC raises "parameter 'p_args' set but not used" when P = {}, // it's not clever enough to treat other P values as making this branch valid. -#if defined(DEBUG_METHODS_ENABLED) && defined(__GNUC__) && !defined(__clang__) +#if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-but-set-parameter" #endif -#ifdef DEBUG_METHODS_ENABLED - template <class Q> void call_get_argument_type_helper(int p_arg, int &index, Variant::Type &type) { if (p_arg == index) { @@ -608,6 +606,7 @@ void call_get_argument_type_info(int p_arg, PropertyInfo &info) { (void)index; // Suppress GCC warning. } +#ifdef DEBUG_METHODS_ENABLED template <class Q> void call_get_argument_metadata_helper(int p_arg, int &index, GodotTypeInfo::Metadata &md) { if (p_arg == index) { @@ -629,13 +628,6 @@ GodotTypeInfo::Metadata call_get_argument_metadata(int p_arg) { return md; } -#else - -template <class... P> -Variant::Type call_get_argument_type(int p_arg) { - return Variant::NIL; -} - #endif // DEBUG_METHODS_ENABLED ////////////////////// @@ -915,7 +907,7 @@ void call_with_variant_args_static_dv(void (*p_method)(P...), const Variant **p_ call_with_variant_args_static(p_method, args, r_error, BuildIndexSequence<sizeof...(P)>{}); } -#if defined(DEBUG_METHODS_ENABLED) && defined(__GNUC__) && !defined(__clang__) +#if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp index f487e718f4..dcded6e61f 100644 --- a/core/variant/callable.cpp +++ b/core/variant/callable.cpp @@ -377,11 +377,11 @@ Error Signal::emit(const Variant **p_arguments, int p_argcount) const { return obj->emit_signal(name, p_arguments, p_argcount); } -Error Signal::connect(const Callable &p_callable, const Vector<Variant> &p_binds, uint32_t p_flags) { +Error Signal::connect(const Callable &p_callable, uint32_t p_flags) { Object *object = get_object(); ERR_FAIL_COND_V(!object, ERR_UNCONFIGURED); - return object->connect(name, p_callable, p_binds, p_flags); + return object->connect(name, p_callable, varray(), p_flags); } void Signal::disconnect(const Callable &p_callable) { diff --git a/core/variant/callable.h b/core/variant/callable.h index 52094af3aa..de886492ea 100644 --- a/core/variant/callable.h +++ b/core/variant/callable.h @@ -159,7 +159,7 @@ public: operator String() const; Error emit(const Variant **p_arguments, int p_argcount) const; - Error connect(const Callable &p_callable, const Vector<Variant> &p_binds = Vector<Variant>(), uint32_t p_flags = 0); + Error connect(const Callable &p_callable, uint32_t p_flags = 0); void disconnect(const Callable &p_callable); bool is_connected(const Callable &p_callable) const; diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp index 10446a5ec1..56eda6e703 100644 --- a/core/variant/callable_bind.cpp +++ b/core/variant/callable_bind.cpp @@ -169,7 +169,8 @@ CallableCustomUnbind::~CallableCustomUnbind() { } Callable callable_bind(const Callable &p_callable, const Variant &p_arg1) { - return p_callable.bind((const Variant **)&p_arg1, 1); + const Variant *args[1] = { &p_arg1 }; + return p_callable.bind(args, 1); } Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2) { diff --git a/core/variant/native_ptr.h b/core/variant/native_ptr.h index b4ec0df7d6..913d4d8f7c 100644 --- a/core/variant/native_ptr.h +++ b/core/variant/native_ptr.h @@ -55,7 +55,7 @@ struct GDNativePtr { #define GDVIRTUAL_NATIVE_PTR(m_type) \ template <> \ - struct GDNativeConstPtr<m_type> { \ + struct GDNativeConstPtr<const m_type> { \ const m_type *data = nullptr; \ GDNativeConstPtr(const m_type *p_assign) { data = p_assign; } \ static const char *get_name() { return "const " #m_type; } \ @@ -117,6 +117,7 @@ GDVIRTUAL_NATIVE_PTR(char16_t) GDVIRTUAL_NATIVE_PTR(char32_t) GDVIRTUAL_NATIVE_PTR(wchar_t) GDVIRTUAL_NATIVE_PTR(uint8_t) +GDVIRTUAL_NATIVE_PTR(uint8_t *) GDVIRTUAL_NATIVE_PTR(int8_t) GDVIRTUAL_NATIVE_PTR(uint16_t) GDVIRTUAL_NATIVE_PTR(int16_t) diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index c5cada674f..3214fc125d 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -1624,6 +1624,19 @@ Variant::operator String() const { return stringify(stack); } +template <class T> +String stringify_vector(const T &vec, List<const void *> &stack) { + String str("["); + for (int i = 0; i < vec.size(); i++) { + if (i > 0) { + str += ", "; + } + str = str + Variant(vec[i]).stringify(stack); + } + str += "]"; + return str; +} + String Variant::stringify(List<const void *> &stack) const { switch (type) { case NIL: @@ -1703,88 +1716,31 @@ String Variant::stringify(List<const void *> &stack) const { return str; } break; case PACKED_VECTOR2_ARRAY: { - Vector<Vector2> vec = operator Vector<Vector2>(); - String str("["); - for (int i = 0; i < vec.size(); i++) { - if (i > 0) { - str += ", "; - } - str = str + Variant(vec[i]); - } - str += "]"; - return str; + return stringify_vector(operator Vector<Vector2>(), stack); } break; case PACKED_VECTOR3_ARRAY: { - Vector<Vector3> vec = operator Vector<Vector3>(); - String str("["); - for (int i = 0; i < vec.size(); i++) { - if (i > 0) { - str += ", "; - } - str = str + Variant(vec[i]); - } - str += "]"; - return str; + return stringify_vector(operator Vector<Vector3>(), stack); + } break; + case PACKED_COLOR_ARRAY: { + return stringify_vector(operator Vector<Color>(), stack); } break; case PACKED_STRING_ARRAY: { - Vector<String> vec = operator Vector<String>(); - String str("["); - for (int i = 0; i < vec.size(); i++) { - if (i > 0) { - str += ", "; - } - str = str + vec[i]; - } - str += "]"; - return str; + return stringify_vector(operator Vector<String>(), stack); + } break; + case PACKED_BYTE_ARRAY: { + return stringify_vector(operator Vector<uint8_t>(), stack); } break; case PACKED_INT32_ARRAY: { - Vector<int32_t> vec = operator Vector<int32_t>(); - String str("["); - for (int i = 0; i < vec.size(); i++) { - if (i > 0) { - str += ", "; - } - str = str + itos(vec[i]); - } - str += "]"; - return str; + return stringify_vector(operator Vector<int32_t>(), stack); } break; case PACKED_INT64_ARRAY: { - Vector<int64_t> vec = operator Vector<int64_t>(); - String str("["); - for (int i = 0; i < vec.size(); i++) { - if (i > 0) { - str += ", "; - } - str = str + itos(vec[i]); - } - str += "]"; - return str; + return stringify_vector(operator Vector<int64_t>(), stack); } break; case PACKED_FLOAT32_ARRAY: { - Vector<float> vec = operator Vector<float>(); - String str("["); - for (int i = 0; i < vec.size(); i++) { - if (i > 0) { - str += ", "; - } - str = str + rtos(vec[i]); - } - str += "]"; - return str; + return stringify_vector(operator Vector<float>(), stack); } break; case PACKED_FLOAT64_ARRAY: { - Vector<double> vec = operator Vector<double>(); - String str("["); - for (int i = 0; i < vec.size(); i++) { - if (i > 0) { - str += ", "; - } - str = str + rtos(vec[i]); - } - str += "]"; - return str; + return stringify_vector(operator Vector<double>(), stack); } break; case ARRAY: { Array arr = operator Array(); @@ -1793,16 +1749,8 @@ String Variant::stringify(List<const void *> &stack) const { } stack.push_back(arr.id()); - String str("["); - for (int i = 0; i < arr.size(); i++) { - if (i) { - str += ", "; - } + String str = stringify_vector(arr, stack); - str += arr[i].stringify(stack); - } - - str += "]"; stack.erase(arr.id()); return str; @@ -2832,7 +2780,7 @@ uint32_t Variant::hash() const { return _data._bool ? 1 : 0; } break; case INT: { - return _data._int; + return hash_one_uint64((uint64_t)_data._int); } break; case FLOAT: { return hash_djb2_one_float(_data._float); @@ -2847,8 +2795,8 @@ uint32_t Variant::hash() const { return hash_djb2_one_float(reinterpret_cast<const Vector2 *>(_data._mem)->y, hash); } break; case VECTOR2I: { - uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Vector2i *>(_data._mem)->x); - return hash_djb2_one_32(reinterpret_cast<const Vector2i *>(_data._mem)->y, hash); + uint32_t hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector2i *>(_data._mem)->x); + return hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector2i *>(_data._mem)->y, hash); } break; case RECT2: { uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Rect2 *>(_data._mem)->position.x); @@ -2857,10 +2805,10 @@ uint32_t Variant::hash() const { return hash_djb2_one_float(reinterpret_cast<const Rect2 *>(_data._mem)->size.y, hash); } break; case RECT2I: { - uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->position.x); - hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->position.y, hash); - hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->size.x, hash); - return hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->size.y, hash); + uint32_t hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->position.x); + hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->position.y, hash); + hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->size.x, hash); + return hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->size.y, hash); } break; case TRANSFORM2D: { uint32_t hash = 5831; @@ -2878,9 +2826,9 @@ uint32_t Variant::hash() const { return hash_djb2_one_float(reinterpret_cast<const Vector3 *>(_data._mem)->z, hash); } break; case VECTOR3I: { - uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->x); - hash = hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->y, hash); - return hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->z, hash); + uint32_t hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector3i *>(_data._mem)->x); + hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector3i *>(_data._mem)->y, hash); + return hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector3i *>(_data._mem)->z, hash); } break; case PLANE: { uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Plane *>(_data._mem)->normal.x); @@ -3145,10 +3093,18 @@ bool Variant::hash_compare(const Variant &p_variant) const { } switch (type) { + case INT: { + return _data._int == p_variant._data._int; + } break; + case FLOAT: { return hash_compare_scalar(_data._float, p_variant._data._float); } break; + case STRING: { + return *reinterpret_cast<const String *>(_data._mem) == *reinterpret_cast<const String *>(p_variant._data._mem); + } break; + case VECTOR2: { const Vector2 *l = reinterpret_cast<const Vector2 *>(_data._mem); const Vector2 *r = reinterpret_cast<const Vector2 *>(p_variant._data._mem); diff --git a/core/variant/variant.h b/core/variant/variant.h index 9ec131a1b8..d3f694e7ca 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -641,6 +641,7 @@ public: static UtilityFunctionType get_utility_function_type(const StringName &p_name); + static MethodInfo get_utility_function_info(const StringName &p_name); static int get_utility_function_argument_count(const StringName &p_name); static Variant::Type get_utility_function_argument_type(const StringName &p_name, int p_arg); static String get_utility_function_argument_name(const StringName &p_name, int p_arg); diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 39207df9e7..6284caae2d 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1241,8 +1241,8 @@ void Variant::get_constants_for_type(Variant::Type p_type, List<StringName> *p_c for (List<StringName>::Element *E = cd.value_ordered.front(); E; E = E->next()) { p_constants->push_back(E->get()); #else - for (Map<StringName, int>::Element *E = cd.value.front(); E; E = E->next()) { - p_constants->push_back(E->key()); + for (const KeyValue<StringName, int> &E : cd.value) { + p_constants->push_back(E.key); #endif } @@ -1250,8 +1250,8 @@ void Variant::get_constants_for_type(Variant::Type p_type, List<StringName> *p_c for (List<StringName>::Element *E = cd.variant_value_ordered.front(); E; E = E->next()) { p_constants->push_back(E->get()); #else - for (Map<StringName, Variant>::Element *E = cd.variant_value.front(); E; E = E->next()) { - p_constants->push_back(E->key()); + for (const KeyValue<StringName, Variant> &E : cd.variant_value) { + p_constants->push_back(E.key); #endif } } @@ -1513,6 +1513,7 @@ static void _register_variant_builtin_methods() { /* Rect2 */ + bind_method(Rect2, get_center, sarray(), varray()); bind_method(Rect2, get_area, sarray(), varray()); bind_method(Rect2, has_no_area, sarray(), varray()); bind_method(Rect2, has_point, sarray("point"), varray()); @@ -1529,6 +1530,7 @@ static void _register_variant_builtin_methods() { /* Rect2i */ + bind_method(Rect2i, get_center, sarray(), varray()); bind_method(Rect2i, get_area, sarray(), varray()); bind_method(Rect2i, has_no_area, sarray(), varray()); bind_method(Rect2i, has_point, sarray("point"), varray()); @@ -1568,7 +1570,6 @@ static void _register_variant_builtin_methods() { bind_method(Vector3, dot, sarray("with"), varray()); bind_method(Vector3, cross, sarray("with"), varray()); bind_method(Vector3, outer, sarray("with"), varray()); - bind_method(Vector3, to_diagonal_matrix, sarray(), varray()); bind_method(Vector3, abs, sarray(), varray()); bind_method(Vector3, floor, sarray(), varray()); bind_method(Vector3, ceil, sarray(), varray()); @@ -1689,7 +1690,7 @@ static void _register_variant_builtin_methods() { bind_method(Signal, get_object_id, sarray(), varray()); bind_method(Signal, get_name, sarray(), varray()); - bind_method(Signal, connect, sarray("callable", "binds", "flags"), varray(Array(), 0)); + bind_method(Signal, connect, sarray("callable", "flags"), varray(0)); bind_method(Signal, disconnect, sarray("callable"), varray()); bind_method(Signal, is_connected, sarray("callable"), varray()); bind_method(Signal, get_connections, sarray(), varray()); @@ -1703,6 +1704,7 @@ static void _register_variant_builtin_methods() { bind_method(Transform2D, get_rotation, sarray(), varray()); bind_method(Transform2D, get_origin, sarray(), varray()); bind_method(Transform2D, get_scale, sarray(), varray()); + bind_method(Transform2D, get_skew, sarray(), varray()); bind_method(Transform2D, orthonormalized, sarray(), varray()); bind_method(Transform2D, rotated, sarray("phi"), varray()); bind_method(Transform2D, scaled, sarray("scale"), varray()); @@ -1712,6 +1714,8 @@ static void _register_variant_builtin_methods() { bind_method(Transform2D, interpolate_with, sarray("xform", "weight"), varray()); bind_method(Transform2D, is_equal_approx, sarray("xform"), varray()); bind_method(Transform2D, set_rotation, sarray("rotation"), varray()); + bind_method(Transform2D, set_scale, sarray("scale"), varray()); + bind_method(Transform2D, set_skew, sarray("skew"), varray()); bind_method(Transform2D, looking_at, sarray("target"), varray(Vector2())); /* Basis */ @@ -1732,10 +1736,12 @@ static void _register_variant_builtin_methods() { bind_method(Basis, is_equal_approx, sarray("b"), varray()); bind_method(Basis, get_rotation_quaternion, sarray(), varray()); bind_static_method(Basis, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0))); + bind_static_method(Basis, from_scale, sarray("scale"), varray()); /* AABB */ bind_method(AABB, abs, sarray(), varray()); + bind_method(AABB, get_center, sarray(), varray()); bind_method(AABB, get_area, sarray(), varray()); bind_method(AABB, has_no_area, sarray(), varray()); bind_method(AABB, has_no_surface, sarray(), varray()); @@ -1839,6 +1845,7 @@ static void _register_variant_builtin_methods() { bind_method(PackedByteArray, reverse, sarray(), varray()); bind_method(PackedByteArray, subarray, sarray("from", "to"), varray()); bind_method(PackedByteArray, sort, sarray(), varray()); + bind_method(PackedByteArray, bsearch, sarray("value", "before"), varray(true)); bind_method(PackedByteArray, duplicate, sarray(), varray()); bind_function(PackedByteArray, get_string_from_ascii, _VariantCall::func_PackedByteArray_get_string_from_ascii, sarray(), varray()); @@ -1900,6 +1907,7 @@ static void _register_variant_builtin_methods() { bind_method(PackedInt32Array, subarray, sarray("from", "to"), varray()); bind_method(PackedInt32Array, to_byte_array, sarray(), varray()); bind_method(PackedInt32Array, sort, sarray(), varray()); + bind_method(PackedInt32Array, bsearch, sarray("value", "before"), varray(true)); bind_method(PackedInt32Array, duplicate, sarray(), varray()); /* Int64 Array */ @@ -1919,6 +1927,7 @@ static void _register_variant_builtin_methods() { bind_method(PackedInt64Array, subarray, sarray("from", "to"), varray()); bind_method(PackedInt64Array, to_byte_array, sarray(), varray()); bind_method(PackedInt64Array, sort, sarray(), varray()); + bind_method(PackedInt64Array, bsearch, sarray("value", "before"), varray(true)); bind_method(PackedInt64Array, duplicate, sarray(), varray()); /* Float32 Array */ @@ -1938,6 +1947,7 @@ static void _register_variant_builtin_methods() { bind_method(PackedFloat32Array, subarray, sarray("from", "to"), varray()); bind_method(PackedFloat32Array, to_byte_array, sarray(), varray()); bind_method(PackedFloat32Array, sort, sarray(), varray()); + bind_method(PackedFloat32Array, bsearch, sarray("value", "before"), varray(true)); bind_method(PackedFloat32Array, duplicate, sarray(), varray()); /* Float64 Array */ @@ -1957,6 +1967,7 @@ static void _register_variant_builtin_methods() { bind_method(PackedFloat64Array, subarray, sarray("from", "to"), varray()); bind_method(PackedFloat64Array, to_byte_array, sarray(), varray()); bind_method(PackedFloat64Array, sort, sarray(), varray()); + bind_method(PackedFloat64Array, bsearch, sarray("value", "before"), varray(true)); bind_method(PackedFloat64Array, duplicate, sarray(), varray()); /* String Array */ @@ -1976,6 +1987,7 @@ static void _register_variant_builtin_methods() { bind_method(PackedStringArray, subarray, sarray("from", "to"), varray()); bind_method(PackedStringArray, to_byte_array, sarray(), varray()); bind_method(PackedStringArray, sort, sarray(), varray()); + bind_method(PackedStringArray, bsearch, sarray("value", "before"), varray(true)); bind_method(PackedStringArray, duplicate, sarray(), varray()); /* Vector2 Array */ @@ -1995,6 +2007,7 @@ static void _register_variant_builtin_methods() { bind_method(PackedVector2Array, subarray, sarray("from", "to"), varray()); bind_method(PackedVector2Array, to_byte_array, sarray(), varray()); bind_method(PackedVector2Array, sort, sarray(), varray()); + bind_method(PackedVector2Array, bsearch, sarray("value", "before"), varray(true)); bind_method(PackedVector2Array, duplicate, sarray(), varray()); /* Vector3 Array */ @@ -2014,6 +2027,7 @@ static void _register_variant_builtin_methods() { bind_method(PackedVector3Array, subarray, sarray("from", "to"), varray()); bind_method(PackedVector3Array, to_byte_array, sarray(), varray()); bind_method(PackedVector3Array, sort, sarray(), varray()); + bind_method(PackedVector3Array, bsearch, sarray("value", "before"), varray(true)); bind_method(PackedVector3Array, duplicate, sarray(), varray()); /* Color Array */ @@ -2033,6 +2047,7 @@ static void _register_variant_builtin_methods() { bind_method(PackedColorArray, subarray, sarray("from", "to"), varray()); bind_method(PackedColorArray, to_byte_array, sarray(), varray()); bind_method(PackedColorArray, sort, sarray(), varray()); + bind_method(PackedColorArray, bsearch, sarray("value", "before"), varray(true)); bind_method(PackedColorArray, duplicate, sarray(), varray()); /* Register constants */ diff --git a/core/variant/variant_construct.cpp b/core/variant/variant_construct.cpp index a1a2bec369..6aba7d7d58 100644 --- a/core/variant/variant_construct.cpp +++ b/core/variant/variant_construct.cpp @@ -114,12 +114,14 @@ void Variant::_register_variant_constructors() { add_constructor<VariantConstructNoArgs<Transform2D>>(sarray()); add_constructor<VariantConstructor<Transform2D, Transform2D>>(sarray("from")); add_constructor<VariantConstructor<Transform2D, float, Vector2>>(sarray("rotation", "position")); + add_constructor<VariantConstructor<Transform2D, float, Size2, float, Vector2>>(sarray("rotation", "scale", "skew", "position")); add_constructor<VariantConstructor<Transform2D, Vector2, Vector2, Vector2>>(sarray("x_axis", "y_axis", "origin")); add_constructor<VariantConstructNoArgs<Plane>>(sarray()); add_constructor<VariantConstructor<Plane, Plane>>(sarray("from")); + add_constructor<VariantConstructor<Plane, Vector3>>(sarray("normal")); add_constructor<VariantConstructor<Plane, Vector3, double>>(sarray("normal", "d")); - add_constructor<VariantConstructor<Plane, Vector3, Vector3>>(sarray("point", "normal")); + add_constructor<VariantConstructor<Plane, Vector3, Vector3>>(sarray("normal", "point")); add_constructor<VariantConstructor<Plane, Vector3, Vector3, Vector3>>(sarray("point1", "point2", "point3")); add_constructor<VariantConstructor<Plane, double, double, double, double>>(sarray("a", "b", "c", "d")); diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index 40c8a1bfde..37383ff2ec 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -1301,12 +1301,12 @@ struct VariantZeroAssigner<Signal> { template <> struct VariantZeroAssigner<Dictionary> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_dictionary(v) = Dictionary(); } }; template <> struct VariantZeroAssigner<Array> { - static _FORCE_INLINE_ void zero(Variant *v) {} + static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_array(v) = Array(); } }; template <> diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp index a245aff35b..b85ece338c 100644 --- a/core/variant/variant_op.cpp +++ b/core/variant/variant_op.cpp @@ -314,6 +314,74 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorEqual<PackedVector3Array, PackedVector3Array>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::PACKED_VECTOR3_ARRAY); register_op<OperatorEvaluatorEqual<PackedColorArray, PackedColorArray>>(Variant::OP_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::PACKED_COLOR_ARRAY); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::BOOL, Variant::NIL>>(Variant::OP_EQUAL, Variant::BOOL, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::INT, Variant::NIL>>(Variant::OP_EQUAL, Variant::INT, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::FLOAT, Variant::NIL>>(Variant::OP_EQUAL, Variant::FLOAT, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::STRING, Variant::NIL>>(Variant::OP_EQUAL, Variant::STRING, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR2, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR2, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR2I, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR2I, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::RECT2, Variant::NIL>>(Variant::OP_EQUAL, Variant::RECT2, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::RECT2I, Variant::NIL>>(Variant::OP_EQUAL, Variant::RECT2I, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR3, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR3, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR3I, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR3I, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::TRANSFORM2D, Variant::NIL>>(Variant::OP_EQUAL, Variant::TRANSFORM2D, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PLANE, Variant::NIL>>(Variant::OP_EQUAL, Variant::PLANE, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::QUATERNION, Variant::NIL>>(Variant::OP_EQUAL, Variant::QUATERNION, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::AABB, Variant::NIL>>(Variant::OP_EQUAL, Variant::AABB, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::BASIS, Variant::NIL>>(Variant::OP_EQUAL, Variant::BASIS, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::TRANSFORM3D, Variant::NIL>>(Variant::OP_EQUAL, Variant::TRANSFORM3D, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::COLOR, Variant::NIL>>(Variant::OP_EQUAL, Variant::COLOR, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::STRING_NAME, Variant::NIL>>(Variant::OP_EQUAL, Variant::STRING_NAME, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NODE_PATH, Variant::NIL>>(Variant::OP_EQUAL, Variant::NODE_PATH, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::RID, Variant::NIL>>(Variant::OP_EQUAL, Variant::RID, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::CALLABLE, Variant::NIL>>(Variant::OP_EQUAL, Variant::CALLABLE, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::SIGNAL, Variant::NIL>>(Variant::OP_EQUAL, Variant::SIGNAL, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::DICTIONARY, Variant::NIL>>(Variant::OP_EQUAL, Variant::DICTIONARY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_BYTE_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_BYTE_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_INT32_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_INT32_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_INT64_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_INT64_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_FLOAT32_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_FLOAT32_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_FLOAT64_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_FLOAT64_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_STRING_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_STRING_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL); + + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::BOOL>>(Variant::OP_EQUAL, Variant::NIL, Variant::BOOL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::INT>>(Variant::OP_EQUAL, Variant::NIL, Variant::INT); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::FLOAT>>(Variant::OP_EQUAL, Variant::NIL, Variant::FLOAT); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::STRING>>(Variant::OP_EQUAL, Variant::NIL, Variant::STRING); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR2>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR2); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR2I>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR2I); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::RECT2>>(Variant::OP_EQUAL, Variant::NIL, Variant::RECT2); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::RECT2I>>(Variant::OP_EQUAL, Variant::NIL, Variant::RECT2I); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3I>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3I); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::TRANSFORM2D>>(Variant::OP_EQUAL, Variant::NIL, Variant::TRANSFORM2D); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PLANE>>(Variant::OP_EQUAL, Variant::NIL, Variant::PLANE); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::QUATERNION>>(Variant::OP_EQUAL, Variant::NIL, Variant::QUATERNION); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::AABB>>(Variant::OP_EQUAL, Variant::NIL, Variant::AABB); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::BASIS>>(Variant::OP_EQUAL, Variant::NIL, Variant::BASIS); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::TRANSFORM3D>>(Variant::OP_EQUAL, Variant::NIL, Variant::TRANSFORM3D); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::COLOR>>(Variant::OP_EQUAL, Variant::NIL, Variant::COLOR); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::STRING_NAME>>(Variant::OP_EQUAL, Variant::NIL, Variant::STRING_NAME); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::NODE_PATH>>(Variant::OP_EQUAL, Variant::NIL, Variant::NODE_PATH); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::RID>>(Variant::OP_EQUAL, Variant::NIL, Variant::RID); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::CALLABLE>>(Variant::OP_EQUAL, Variant::NIL, Variant::CALLABLE); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::SIGNAL>>(Variant::OP_EQUAL, Variant::NIL, Variant::SIGNAL); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::DICTIONARY>>(Variant::OP_EQUAL, Variant::NIL, Variant::DICTIONARY); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::ARRAY); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_BYTE_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_BYTE_ARRAY); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_INT32_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_INT32_ARRAY); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_INT64_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_INT64_ARRAY); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_FLOAT32_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_FLOAT32_ARRAY); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_FLOAT64_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_FLOAT64_ARRAY); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_STRING_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_STRING_ARRAY); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY); + register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NIL); register_op<OperatorEvaluatorNotEqual<bool, bool>>(Variant::OP_NOT_EQUAL, Variant::BOOL, Variant::BOOL); register_op<OperatorEvaluatorNotEqual<int64_t, int64_t>>(Variant::OP_NOT_EQUAL, Variant::INT, Variant::INT); @@ -360,6 +428,74 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorNotEqual<PackedVector3Array, PackedVector3Array>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::PACKED_VECTOR3_ARRAY); register_op<OperatorEvaluatorNotEqual<PackedColorArray, PackedColorArray>>(Variant::OP_NOT_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::PACKED_COLOR_ARRAY); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::BOOL, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::BOOL, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::INT, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::INT, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::FLOAT, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::FLOAT, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::STRING, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::STRING, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR2, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR2, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR2I, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR2I, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::RECT2, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::RECT2, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::RECT2I, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::RECT2I, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR3, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR3, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR3I, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR3I, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::TRANSFORM2D, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::TRANSFORM2D, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PLANE, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PLANE, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::QUATERNION, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::QUATERNION, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::AABB, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::AABB, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::BASIS, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::BASIS, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::TRANSFORM3D, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::TRANSFORM3D, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::COLOR, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::COLOR, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::STRING_NAME, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::STRING_NAME, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NODE_PATH, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::NODE_PATH, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::RID, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::RID, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::CALLABLE, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::CALLABLE, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::SIGNAL, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::SIGNAL, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::DICTIONARY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::DICTIONARY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_BYTE_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_BYTE_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_INT32_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_INT32_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_INT64_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_INT64_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_FLOAT32_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_FLOAT32_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_FLOAT64_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_FLOAT64_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_STRING_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_STRING_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL); + + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::BOOL>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::BOOL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::INT>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::INT); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::FLOAT>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::FLOAT); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::STRING>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::STRING); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR2>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR2); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR2I>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR2I); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RECT2>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RECT2); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RECT2I>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RECT2I); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3I>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3I); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::TRANSFORM2D>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::TRANSFORM2D); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PLANE>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PLANE); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::QUATERNION>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::QUATERNION); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::AABB>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::AABB); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::BASIS>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::BASIS); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::TRANSFORM3D>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::TRANSFORM3D); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::COLOR>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::COLOR); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::STRING_NAME>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::STRING_NAME); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NODE_PATH>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NODE_PATH); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RID>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RID); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::CALLABLE>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::CALLABLE); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::SIGNAL>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::SIGNAL); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::DICTIONARY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::DICTIONARY); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::ARRAY); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_BYTE_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_BYTE_ARRAY); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_INT32_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_INT32_ARRAY); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_INT64_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_INT64_ARRAY); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_FLOAT32_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_FLOAT32_ARRAY); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_FLOAT64_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_FLOAT64_ARRAY); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_STRING_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_STRING_ARRAY); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY); + register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY); + register_op<OperatorEvaluatorLess<bool, bool>>(Variant::OP_LESS, Variant::BOOL, Variant::BOOL); register_op<OperatorEvaluatorLess<int64_t, int64_t>>(Variant::OP_LESS, Variant::INT, Variant::INT); register_op<OperatorEvaluatorLess<int64_t, double>>(Variant::OP_LESS, Variant::INT, Variant::FLOAT); @@ -505,7 +641,10 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorNotFloat>(Variant::OP_NOT, Variant::FLOAT, Variant::NIL); register_op<OperatorEvaluatorNotObject>(Variant::OP_NOT, Variant::OBJECT, Variant::NIL); - register_op<OperatorEvaluatorInStringFind>(Variant::OP_IN, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorInStringFind<String>>(Variant::OP_IN, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorInStringFind<StringName>>(Variant::OP_IN, Variant::STRING_NAME, Variant::STRING); + register_op<OperatorEvaluatorInStringNameFind<String>>(Variant::OP_IN, Variant::STRING, Variant::STRING_NAME); + register_op<OperatorEvaluatorInStringNameFind<StringName>>(Variant::OP_IN, Variant::STRING_NAME, Variant::STRING_NAME); register_op<OperatorEvaluatorInDictionaryHasNil>(Variant::OP_IN, Variant::NIL, Variant::DICTIONARY); register_op<OperatorEvaluatorInDictionaryHas<bool>>(Variant::OP_IN, Variant::BOOL, Variant::DICTIONARY); diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h index 3c9f849a4f..353524469a 100644 --- a/core/variant/variant_op.h +++ b/core/variant/variant_op.h @@ -1213,22 +1213,44 @@ public: //// +template <class Left> class OperatorEvaluatorInStringFind { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - const String &str_a = *VariantGetInternalPtr<String>::get_ptr(&p_left); + const Left &str_a = *VariantGetInternalPtr<Left>::get_ptr(&p_left); const String &str_b = *VariantGetInternalPtr<String>::get_ptr(&p_right); *r_ret = str_b.find(str_a) != -1; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - const String &str_a = *VariantGetInternalPtr<String>::get_ptr(left); + const Left &str_a = *VariantGetInternalPtr<Left>::get_ptr(left); const String &str_b = *VariantGetInternalPtr<String>::get_ptr(right); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = str_b.find(str_a) != -1; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { - PtrToArg<bool>::encode(PtrToArg<String>::convert(right).find(PtrToArg<String>::convert(left)) != -1, r_ret); + PtrToArg<bool>::encode(PtrToArg<String>::convert(right).find(PtrToArg<Left>::convert(left)) != -1, r_ret); + } + static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +template <class Left> +class OperatorEvaluatorInStringNameFind { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Left &str_a = *VariantGetInternalPtr<Left>::get_ptr(&p_left); + const String str_b = VariantGetInternalPtr<StringName>::get_ptr(&p_right)->operator String(); + + *r_ret = str_b.find(str_a) != -1; + r_valid = true; + } + static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + const Left &str_a = *VariantGetInternalPtr<Left>::get_ptr(left); + const String str_b = VariantGetInternalPtr<StringName>::get_ptr(right)->operator String(); + *VariantGetInternalPtr<bool>::get_ptr(r_ret) = str_b.find(str_a) != -1; + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<bool>::encode(PtrToArg<StringName>::convert(right).operator String().find(PtrToArg<Left>::convert(left)) != -1, r_ret); } static Variant::Type get_return_type() { return Variant::BOOL; } }; diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index a214a238a6..221a8c4f98 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -90,6 +90,17 @@ const char *VariantParser::tk_name[TK_MAX] = { "ERROR" }; +static double stor_fix(const String &p_str) { + if (p_str == "inf") { + return INFINITY; + } else if (p_str == "inf_neg") { + return -INFINITY; + } else if (p_str == "nan") { + return NAN; + } + return -1; +} + Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, String &r_err_str) { bool string_name = false; @@ -469,8 +480,19 @@ Error VariantParser::_parse_construct(Stream *p_stream, Vector<T> &r_construct, if (first && token.type == TK_PARENTHESIS_CLOSE) { break; } else if (token.type != TK_NUMBER) { - r_err_str = "Expected float in constructor"; - return ERR_PARSE_ERROR; + bool valid = false; + if (token.type == TK_IDENTIFIER) { + double real = stor_fix(token.value); + if (real != -1) { + token.type = TK_NUMBER; + token.value = real; + valid = true; + } + } + if (!valid) { + r_err_str = "Expected float in constructor"; + return ERR_PARSE_ERROR; + } } r_construct.push_back(token.value); @@ -507,6 +529,8 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, value = Variant(); } else if (id == "inf") { value = INFINITY; + } else if (id == "inf_neg") { + value = -INFINITY; } else if (id == "nan") { value = NAN; } else if (id == "Vector2") { @@ -1401,11 +1425,19 @@ Error VariantParser::parse(Stream *p_stream, Variant &r_ret, String &r_err_str, ////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// -static String rtosfix(double p_value) { +static String rtos_fix(double p_value) { if (p_value == 0.0) { return "0"; //avoid negative zero (-0) being written, which may annoy git, svn, etc. for changes when they don't exist. + } else if (isnan(p_value)) { + return "nan"; + } else if (isinf(p_value)) { + if (p_value > 0) { + return "inf"; + } else { + return "inf_neg"; + } } else { return rtoss(p_value); } @@ -1423,8 +1455,8 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, itos(p_variant.operator int64_t())); } break; case Variant::FLOAT: { - String s = rtosfix(p_variant.operator double()); - if (s != "inf" && s != "nan") { + String s = rtos_fix(p_variant.operator double()); + if (s != "inf" && s != "inf_neg" && s != "nan") { if (s.find(".") == -1 && s.find("e") == -1) { s += ".0"; } @@ -1439,7 +1471,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::VECTOR2: { Vector2 v = p_variant; - p_store_string_func(p_store_string_ud, "Vector2(" + rtosfix(v.x) + ", " + rtosfix(v.y) + ")"); + p_store_string_func(p_store_string_ud, "Vector2(" + rtos_fix(v.x) + ", " + rtos_fix(v.y) + ")"); } break; case Variant::VECTOR2I: { Vector2i v = p_variant; @@ -1447,7 +1479,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::RECT2: { Rect2 aabb = p_variant; - p_store_string_func(p_store_string_ud, "Rect2(" + rtosfix(aabb.position.x) + ", " + rtosfix(aabb.position.y) + ", " + rtosfix(aabb.size.x) + ", " + rtosfix(aabb.size.y) + ")"); + p_store_string_func(p_store_string_ud, "Rect2(" + rtos_fix(aabb.position.x) + ", " + rtos_fix(aabb.position.y) + ", " + rtos_fix(aabb.size.x) + ", " + rtos_fix(aabb.size.y) + ")"); } break; case Variant::RECT2I: { @@ -1457,7 +1489,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::VECTOR3: { Vector3 v = p_variant; - p_store_string_func(p_store_string_ud, "Vector3(" + rtosfix(v.x) + ", " + rtosfix(v.y) + ", " + rtosfix(v.z) + ")"); + p_store_string_func(p_store_string_ud, "Vector3(" + rtos_fix(v.x) + ", " + rtos_fix(v.y) + ", " + rtos_fix(v.z) + ")"); } break; case Variant::VECTOR3I: { Vector3i v = p_variant; @@ -1465,17 +1497,17 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::PLANE: { Plane p = p_variant; - p_store_string_func(p_store_string_ud, "Plane(" + rtosfix(p.normal.x) + ", " + rtosfix(p.normal.y) + ", " + rtosfix(p.normal.z) + ", " + rtosfix(p.d) + ")"); + p_store_string_func(p_store_string_ud, "Plane(" + rtos_fix(p.normal.x) + ", " + rtos_fix(p.normal.y) + ", " + rtos_fix(p.normal.z) + ", " + rtos_fix(p.d) + ")"); } break; case Variant::AABB: { AABB aabb = p_variant; - p_store_string_func(p_store_string_ud, "AABB(" + rtosfix(aabb.position.x) + ", " + rtosfix(aabb.position.y) + ", " + rtosfix(aabb.position.z) + ", " + rtosfix(aabb.size.x) + ", " + rtosfix(aabb.size.y) + ", " + rtosfix(aabb.size.z) + ")"); + p_store_string_func(p_store_string_ud, "AABB(" + rtos_fix(aabb.position.x) + ", " + rtos_fix(aabb.position.y) + ", " + rtos_fix(aabb.position.z) + ", " + rtos_fix(aabb.size.x) + ", " + rtos_fix(aabb.size.y) + ", " + rtos_fix(aabb.size.z) + ")"); } break; case Variant::QUATERNION: { Quaternion quaternion = p_variant; - p_store_string_func(p_store_string_ud, "Quaternion(" + rtosfix(quaternion.x) + ", " + rtosfix(quaternion.y) + ", " + rtosfix(quaternion.z) + ", " + rtosfix(quaternion.w) + ")"); + p_store_string_func(p_store_string_ud, "Quaternion(" + rtos_fix(quaternion.x) + ", " + rtos_fix(quaternion.y) + ", " + rtos_fix(quaternion.z) + ", " + rtos_fix(quaternion.w) + ")"); } break; case Variant::TRANSFORM2D: { @@ -1486,7 +1518,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i != 0 || j != 0) { s += ", "; } - s += rtosfix(m3.elements[i][j]); + s += rtos_fix(m3.elements[i][j]); } } @@ -1501,7 +1533,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i != 0 || j != 0) { s += ", "; } - s += rtosfix(m3.elements[i][j]); + s += rtos_fix(m3.elements[i][j]); } } @@ -1517,11 +1549,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i != 0 || j != 0) { s += ", "; } - s += rtosfix(m3.elements[i][j]); + s += rtos_fix(m3.elements[i][j]); } } - s = s + ", " + rtosfix(t.origin.x) + ", " + rtosfix(t.origin.y) + ", " + rtosfix(t.origin.z); + s = s + ", " + rtos_fix(t.origin.x) + ", " + rtos_fix(t.origin.y) + ", " + rtos_fix(t.origin.z); p_store_string_func(p_store_string_ud, s + ")"); } break; @@ -1529,7 +1561,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str // misc types case Variant::COLOR: { Color c = p_variant; - p_store_string_func(p_store_string_ud, "Color(" + rtosfix(c.r) + ", " + rtosfix(c.g) + ", " + rtosfix(c.b) + ", " + rtosfix(c.a) + ")"); + p_store_string_func(p_store_string_ud, "Color(" + rtos_fix(c.r) + ", " + rtos_fix(c.g) + ", " + rtos_fix(c.b) + ", " + rtos_fix(c.a) + ")"); } break; case Variant::STRING_NAME: { @@ -1707,7 +1739,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i > 0) { p_store_string_func(p_store_string_ud, ", "); } - p_store_string_func(p_store_string_ud, rtosfix(ptr[i])); + p_store_string_func(p_store_string_ud, rtos_fix(ptr[i])); } p_store_string_func(p_store_string_ud, ")"); @@ -1723,7 +1755,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i > 0) { p_store_string_func(p_store_string_ud, ", "); } - p_store_string_func(p_store_string_ud, rtosfix(ptr[i])); + p_store_string_func(p_store_string_ud, rtos_fix(ptr[i])); } p_store_string_func(p_store_string_ud, ")"); @@ -1759,7 +1791,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i > 0) { p_store_string_func(p_store_string_ud, ", "); } - p_store_string_func(p_store_string_ud, rtosfix(ptr[i].x) + ", " + rtosfix(ptr[i].y)); + p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].x) + ", " + rtos_fix(ptr[i].y)); } p_store_string_func(p_store_string_ud, ")"); @@ -1775,7 +1807,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i > 0) { p_store_string_func(p_store_string_ud, ", "); } - p_store_string_func(p_store_string_ud, rtosfix(ptr[i].x) + ", " + rtosfix(ptr[i].y) + ", " + rtosfix(ptr[i].z)); + p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].x) + ", " + rtos_fix(ptr[i].y) + ", " + rtos_fix(ptr[i].z)); } p_store_string_func(p_store_string_ud, ")"); @@ -1792,7 +1824,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, ", "); } - p_store_string_func(p_store_string_ud, rtosfix(ptr[i].r) + ", " + rtosfix(ptr[i].g) + ", " + rtosfix(ptr[i].b) + ", " + rtosfix(ptr[i].a)); + p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].r) + ", " + rtos_fix(ptr[i].g) + ", " + rtos_fix(ptr[i].b) + ", " + rtos_fix(ptr[i].a)); } p_store_string_func(p_store_string_ud, ")"); diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp index ae3c7685fd..4abb51ca7c 100644 --- a/core/variant/variant_setget.cpp +++ b/core/variant/variant_setget.cpp @@ -239,7 +239,8 @@ void Variant::set_named(const StringName &p_member, const Variant &p_value, bool *v = p_value; r_valid = true; } else { - r_valid = false; + VariantGetInternalPtr<Dictionary>::get_ptr(this)->operator[](p_member) = p_value; + r_valid = true; } } else { @@ -661,6 +662,91 @@ struct VariantIndexedSetGet_Array { static uint64_t get_indexed_size(const Variant *base) { return 0; } }; +struct VariantIndexedSetGet_String { + static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { + int64_t length = VariantGetInternalPtr<String>::get_ptr(base)->length(); + if (index < 0) { + index += length; + } + if (index < 0 || index >= length) { + *oob = true; + return; + } + char32_t result = (*VariantGetInternalPtr<String>::get_ptr(base))[index]; + *value = String(&result, 1); + *oob = false; + } + static void ptr_get(const void *base, int64_t index, void *member) { + /* avoid ptrconvert for performance*/ + const String &v = *reinterpret_cast<const String *>(base); + if (index < 0) { + index += v.length(); + } + OOB_TEST(index, v.length()); + char32_t c = v[index]; + PtrToArg<String>::encode(String(&c, 1), member); + } + static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { + if (value->get_type() != Variant::STRING) { + *oob = false; + *valid = false; + return; + } + int64_t length = VariantGetInternalPtr<String>::get_ptr(base)->length(); + if (index < 0) { + index += length; + } + if (index < 0 || index >= length) { + *oob = true; + *valid = false; + return; + } + String *b = VariantGetInternalPtr<String>::get_ptr(base); + const String *v = VariantInternal::get_string(value); + if (v->length() == 0) { + b->remove(index); + } else { + b->set(index, v->get(0)); + } + *oob = false; + *valid = true; + } + static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { + int64_t length = VariantGetInternalPtr<String>::get_ptr(base)->length(); + if (index < 0) { + index += length; + } + if (index < 0 || index >= length) { + *oob = true; + return; + } + String *b = VariantGetInternalPtr<String>::get_ptr(base); + const String *v = VariantInternal::get_string(value); + if (v->length() == 0) { + b->remove(index); + } else { + b->set(index, v->get(0)); + } + *oob = false; + } + static void ptr_set(void *base, int64_t index, const void *member) { + /* avoid ptrconvert for performance*/ + String &v = *reinterpret_cast<String *>(base); + if (index < 0) { + index += v.length(); + } + OOB_TEST(index, v.length()); + const String &m = *reinterpret_cast<const String *>(member); + if (unlikely(m.length() == 0)) { + v.remove(index); + } else { + v.set(index, m.unicode_at(0)); + } + } + static Variant::Type get_index_type() { return Variant::STRING; } + static uint64_t get_indexed_size(const Variant *base) { return VariantInternal::get_string(base)->length(); } +}; + #define INDEXED_SETGET_STRUCT_DICT(m_base_type) \ struct VariantIndexedSetGet_##m_base_type { \ static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \ @@ -758,6 +844,7 @@ static void register_indexed_member(Variant::Type p_type) { void register_indexed_setters_getters() { #define REGISTER_INDEXED_MEMBER(m_base_type) register_indexed_member<VariantIndexedSetGet_##m_base_type>(GetTypeInfo<m_base_type>::VARIANT_TYPE) + REGISTER_INDEXED_MEMBER(String); REGISTER_INDEXED_MEMBER(Vector2); REGISTER_INDEXED_MEMBER(Vector2i); REGISTER_INDEXED_MEMBER(Vector3); diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 232054d0ca..666b582e39 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -487,10 +487,6 @@ struct VariantUtilityFunctions { } static inline void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - } String str; for (int i = 0; i < p_arg_count; i++) { String os = p_args[i]->operator String(); @@ -506,11 +502,29 @@ struct VariantUtilityFunctions { r_error.error = Callable::CallError::CALL_OK; } - static inline void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; + static inline void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (OS::get_singleton()->is_stdout_verbose()) { + String str; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + str = os; + } else { + str += os; + } + } + + // No need to use `print_verbose()` as this call already only happens + // when verbose mode is enabled. This avoids performing string argument concatenation + // when not needed. + print_line(str); } + + r_error.error = Callable::CallError::CALL_OK; + } + + static inline void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { String str; for (int i = 0; i < p_arg_count; i++) { String os = p_args[i]->operator String(); @@ -527,10 +541,6 @@ struct VariantUtilityFunctions { } static inline void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - } String str; for (int i = 0; i < p_arg_count; i++) { if (i) { @@ -544,10 +554,6 @@ struct VariantUtilityFunctions { } static inline void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - } String str; for (int i = 0; i < p_arg_count; i++) { if (i) { @@ -561,10 +567,6 @@ struct VariantUtilityFunctions { } static inline void printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - } String str; for (int i = 0; i < p_arg_count; i++) { String os = p_args[i]->operator String(); @@ -1246,6 +1248,7 @@ void Variant::_register_variant_utility_functions() { FUNCBINDVARARGV(printt, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(prints, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(printraw, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDVARARGV(print_verbose, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(push_error, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(push_warning, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); @@ -1330,6 +1333,28 @@ Variant::UtilityFunctionType Variant::get_utility_function_type(const StringName return bfi->type; } +MethodInfo Variant::get_utility_function_info(const StringName &p_name) { + MethodInfo info; + const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); + if (bfi) { + info.name = p_name; + if (bfi->returns_value && bfi->return_type == Variant::NIL) { + info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + } + info.return_val.type = bfi->return_type; + if (bfi->is_vararg) { + info.flags |= METHOD_FLAG_VARARG; + } + for (int i = 0; i < bfi->argnames.size(); ++i) { + PropertyInfo arg; + arg.type = bfi->get_arg_type(i); + arg.name = bfi->argnames[i]; + info.arguments.push_back(arg); + } + } + return info; +} + int Variant::get_utility_function_argument_count(const StringName &p_name) { const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); if (!bfi) { |