diff options
Diffstat (limited to 'core')
115 files changed, 2163 insertions, 3476 deletions
diff --git a/core/SCsub b/core/SCsub index c12dd4e60e..1379e9df9b 100644 --- a/core/SCsub +++ b/core/SCsub @@ -147,6 +147,7 @@ env.core_sources += thirdparty_obj env.add_source_files(env.core_sources, "*.cpp") env.add_source_files(env.core_sources, "script_encryption_key.gen.cpp") +env.add_source_files(env.core_sources, "version_hash.gen.cpp") # Certificates env.Depends( diff --git a/core/config/engine.cpp b/core/config/engine.cpp index d9abf5e5e9..ff8a8d283f 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -35,7 +35,6 @@ #include "core/donors.gen.h" #include "core/license.gen.h" #include "core/version.h" -#include "core/version_hash.gen.h" void Engine::set_physics_ticks_per_second(int p_ips) { ERR_FAIL_COND_MSG(p_ips <= 0, "Engine iterations per second must be greater than 0."); @@ -95,8 +94,8 @@ Dictionary Engine::get_version_info() const { dict["build"] = VERSION_BUILD; dict["year"] = VERSION_YEAR; - String hash = VERSION_HASH; - dict["hash"] = hash.length() == 0 ? String("unknown") : hash; + String hash = String(VERSION_HASH); + dict["hash"] = hash.is_empty() ? String("unknown") : hash; String stringver = String(dict["major"]) + "." + String(dict["minor"]); if ((int)dict["patch"] != 0) { diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 45776c03e4..887051f18f 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -615,7 +615,11 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo bool ProjectSettings::has_setting(String p_var) const { _THREAD_SAFE_METHOD_ - return props.has(p_var); + StringName name = p_var; + if (!disable_feature_overrides && feature_overrides.has(name)) { + name = feature_overrides[name]; + } + return props.has(name); } Error ProjectSettings::_load_settings_binary(const String &p_path) { @@ -1235,8 +1239,8 @@ ProjectSettings::ProjectSettings() { // Keep the enum values in sync with the `DisplayServer::VSyncMode` enum. custom_prop_info["display/window/vsync/vsync_mode"] = PropertyInfo(Variant::INT, "display/window/vsync/vsync_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Adaptive,Mailbox"); custom_prop_info["rendering/driver/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/driver/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded"); - GLOBAL_DEF("physics/2d/run_on_thread", false); - GLOBAL_DEF("physics/3d/run_on_thread", false); + GLOBAL_DEF("physics/2d/run_on_separate_thread", false); + GLOBAL_DEF("physics/3d/run_on_separate_thread", false); GLOBAL_DEF("debug/settings/profiler/max_functions", 16384); custom_prop_info["debug/settings/profiler/max_functions"] = PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1"); diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 8d03f35617..195292f7e6 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -2376,21 +2376,18 @@ bool EngineDebugger::is_active() { return ::EngineDebugger::is_active(); } -void EngineDebugger::register_profiler(const StringName &p_name, const Callable &p_toggle, const Callable &p_add, const Callable &p_tick) { - ERR_FAIL_COND_MSG(profilers.has(p_name) || has_profiler(p_name), "Profiler already registered: " + p_name); - profilers.insert(p_name, ProfilerCallable(p_toggle, p_add, p_tick)); - ProfilerCallable &p = profilers[p_name]; - ::EngineDebugger::Profiler profiler( - &p, - &EngineDebugger::call_toggle, - &EngineDebugger::call_add, - &EngineDebugger::call_tick); - ::EngineDebugger::register_profiler(p_name, profiler); +void EngineDebugger::register_profiler(const StringName &p_name, Ref<EngineProfiler> p_profiler) { + ERR_FAIL_COND(p_profiler.is_null()); + ERR_FAIL_COND_MSG(p_profiler->is_bound(), "Profiler already registered."); + ERR_FAIL_COND_MSG(profilers.has(p_name) || has_profiler(p_name), "Profiler name already in use: " + p_name); + Error err = p_profiler->bind(p_name); + ERR_FAIL_COND_MSG(err != OK, "Profiler failed to register with error: " + itos(err)); + profilers.insert(p_name, p_profiler); } void EngineDebugger::unregister_profiler(const StringName &p_name) { ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + p_name); - ::EngineDebugger::unregister_profiler(p_name); + profilers[p_name]->unbind(); profilers.erase(p_name); } @@ -2435,45 +2432,6 @@ void EngineDebugger::send_message(const String &p_msg, const Array &p_data) { ::EngineDebugger::get_singleton()->send_message(p_msg, p_data); } -void EngineDebugger::call_toggle(void *p_user, bool p_enable, const Array &p_opts) { - Callable &toggle = ((ProfilerCallable *)p_user)->callable_toggle; - if (toggle.is_null()) { - return; - } - Variant enable = p_enable, opts = p_opts; - const Variant *args[2] = { &enable, &opts }; - Variant retval; - Callable::CallError err; - toggle.call(args, 2, retval, err); - ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'toggle' to callable: " + Variant::get_callable_error_text(toggle, args, 2, err)); -} - -void EngineDebugger::call_add(void *p_user, const Array &p_data) { - Callable &add = ((ProfilerCallable *)p_user)->callable_add; - if (add.is_null()) { - return; - } - Variant data = p_data; - const Variant *args[1] = { &data }; - Variant retval; - Callable::CallError err; - add.call(args, 1, retval, err); - ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'add' to callable: " + Variant::get_callable_error_text(add, args, 1, err)); -} - -void EngineDebugger::call_tick(void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { - Callable &tick = ((ProfilerCallable *)p_user)->callable_tick; - if (tick.is_null()) { - return; - } - Variant frame_time = p_frame_time, idle_time = p_idle_time, physics_time = p_physics_time, physics_frame_time = p_physics_frame_time; - const Variant *args[4] = { &frame_time, &idle_time, &physics_time, &physics_frame_time }; - Variant retval; - Callable::CallError err; - tick.call(args, 4, retval, err); - ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'tick' to callable: " + Variant::get_callable_error_text(tick, args, 4, err)); -} - Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) { Callable &capture = *(Callable *)p_user; if (capture.is_null()) { @@ -2495,10 +2453,6 @@ EngineDebugger::~EngineDebugger() { ::EngineDebugger::unregister_message_capture(E.key); } captures.clear(); - for (const KeyValue<StringName, ProfilerCallable> &E : profilers) { - ::EngineDebugger::unregister_profiler(E.key); - } - profilers.clear(); } EngineDebugger *EngineDebugger::singleton = nullptr; @@ -2506,8 +2460,9 @@ EngineDebugger *EngineDebugger::singleton = nullptr; void EngineDebugger::_bind_methods() { ClassDB::bind_method(D_METHOD("is_active"), &EngineDebugger::is_active); - ClassDB::bind_method(D_METHOD("register_profiler", "name", "toggle", "add", "tick"), &EngineDebugger::register_profiler); + ClassDB::bind_method(D_METHOD("register_profiler", "name", "profiler"), &EngineDebugger::register_profiler); ClassDB::bind_method(D_METHOD("unregister_profiler", "name"), &EngineDebugger::unregister_profiler); + ClassDB::bind_method(D_METHOD("is_profiling", "name"), &EngineDebugger::is_profiling); ClassDB::bind_method(D_METHOD("has_profiler", "name"), &EngineDebugger::has_profiler); diff --git a/core/core_bind.h b/core/core_bind.h index ac0e92a87a..21a1fc2077 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -31,6 +31,7 @@ #ifndef CORE_BIND_H #define CORE_BIND_H +#include "core/debugger/engine_profiler.h" #include "core/io/compression.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" @@ -673,25 +674,8 @@ public: class EngineDebugger : public Object { GDCLASS(EngineDebugger, Object); - class ProfilerCallable { - friend class EngineDebugger; - - Callable callable_toggle; - Callable callable_add; - Callable callable_tick; - - public: - ProfilerCallable() {} - - ProfilerCallable(const Callable &p_toggle, const Callable &p_add, const Callable &p_tick) { - callable_toggle = p_toggle; - callable_add = p_add; - callable_tick = p_tick; - } - }; - Map<StringName, Callable> captures; - Map<StringName, ProfilerCallable> profilers; + Map<StringName, Ref<EngineProfiler>> profilers; protected: static void _bind_methods(); @@ -702,7 +686,7 @@ public: bool is_active(); - void register_profiler(const StringName &p_name, const Callable &p_toggle, const Callable &p_add, const Callable &p_tick); + void register_profiler(const StringName &p_name, Ref<EngineProfiler> p_profiler); void unregister_profiler(const StringName &p_name); bool is_profiling(const StringName &p_name); bool has_profiler(const StringName &p_name); @@ -715,9 +699,6 @@ public: void send_message(const String &p_msg, const Array &p_data); - static void call_toggle(void *p_user, bool p_enable, const Array &p_opts); - static void call_add(void *p_user, const Array &p_data); - static void call_tick(void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time); static Error call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured); EngineDebugger() { singleton = this; } diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 2f5fd05e6a..63e7323f7a 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -165,6 +165,9 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_CENTER); BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_BOTTOM); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_IMAGE_MASK); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TEXT_MASK); + BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SPECIAL); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ESCAPE); BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, TAB); diff --git a/core/debugger/debugger_marshalls.cpp b/core/debugger/debugger_marshalls.cpp index 1a746d59a3..4c69290c2e 100644 --- a/core/debugger/debugger_marshalls.cpp +++ b/core/debugger/debugger_marshalls.cpp @@ -35,159 +35,6 @@ #define CHECK_SIZE(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() < (uint32_t)(expected), false, String("Malformed ") + what + " message from script debugger, message too short. Expected size: " + itos(expected) + ", actual size: " + itos(arr.size())) #define CHECK_END(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() > (uint32_t)expected, false, String("Malformed ") + what + " message from script debugger, message too long. Expected size: " + itos(expected) + ", actual size: " + itos(arr.size())) -Array DebuggerMarshalls::ResourceUsage::serialize() { - infos.sort(); - - Array arr; - arr.push_back(infos.size() * 4); - for (const ResourceInfo &E : infos) { - arr.push_back(E.path); - arr.push_back(E.format); - arr.push_back(E.type); - arr.push_back(E.vram); - } - return arr; -} - -bool DebuggerMarshalls::ResourceUsage::deserialize(const Array &p_arr) { - CHECK_SIZE(p_arr, 1, "ResourceUsage"); - uint32_t size = p_arr[0]; - CHECK_SIZE(p_arr, size, "ResourceUsage"); - int idx = 1; - for (uint32_t i = 0; i < size / 4; i++) { - ResourceInfo info; - info.path = p_arr[idx]; - info.format = p_arr[idx + 1]; - info.type = p_arr[idx + 2]; - info.vram = p_arr[idx + 3]; - infos.push_back(info); - } - CHECK_END(p_arr, idx, "ResourceUsage"); - return true; -} - -Array DebuggerMarshalls::ScriptFunctionSignature::serialize() { - Array arr; - arr.push_back(name); - arr.push_back(id); - return arr; -} - -bool DebuggerMarshalls::ScriptFunctionSignature::deserialize(const Array &p_arr) { - CHECK_SIZE(p_arr, 2, "ScriptFunctionSignature"); - name = p_arr[0]; - id = p_arr[1]; - CHECK_END(p_arr, 2, "ScriptFunctionSignature"); - return true; -} - -Array DebuggerMarshalls::NetworkProfilerFrame::serialize() { - Array arr; - arr.push_back(infos.size() * 6); - for (int i = 0; i < infos.size(); ++i) { - arr.push_back(uint64_t(infos[i].node)); - arr.push_back(infos[i].node_path); - arr.push_back(infos[i].incoming_rpc); - arr.push_back(infos[i].incoming_rset); - arr.push_back(infos[i].outgoing_rpc); - arr.push_back(infos[i].outgoing_rset); - } - return arr; -} - -bool DebuggerMarshalls::NetworkProfilerFrame::deserialize(const Array &p_arr) { - CHECK_SIZE(p_arr, 1, "NetworkProfilerFrame"); - uint32_t size = p_arr[0]; - CHECK_SIZE(p_arr, size, "NetworkProfilerFrame"); - infos.resize(size); - int idx = 1; - for (uint32_t i = 0; i < size / 6; ++i) { - infos.write[i].node = uint64_t(p_arr[idx]); - infos.write[i].node_path = p_arr[idx + 1]; - infos.write[i].incoming_rpc = p_arr[idx + 2]; - infos.write[i].incoming_rset = p_arr[idx + 3]; - infos.write[i].outgoing_rpc = p_arr[idx + 4]; - infos.write[i].outgoing_rset = p_arr[idx + 5]; - } - CHECK_END(p_arr, idx, "NetworkProfilerFrame"); - return true; -} - -Array DebuggerMarshalls::ServersProfilerFrame::serialize() { - Array arr; - arr.push_back(frame_number); - arr.push_back(frame_time); - arr.push_back(idle_time); - arr.push_back(physics_time); - arr.push_back(physics_frame_time); - arr.push_back(script_time); - - arr.push_back(servers.size()); - for (int i = 0; i < servers.size(); i++) { - ServerInfo &s = servers[i]; - arr.push_back(s.name); - arr.push_back(s.functions.size() * 2); - for (int j = 0; j < s.functions.size(); j++) { - ServerFunctionInfo &f = s.functions[j]; - arr.push_back(f.name); - arr.push_back(f.time); - } - } - - arr.push_back(script_functions.size() * 4); - for (int i = 0; i < script_functions.size(); i++) { - arr.push_back(script_functions[i].sig_id); - arr.push_back(script_functions[i].call_count); - arr.push_back(script_functions[i].self_time); - arr.push_back(script_functions[i].total_time); - } - return arr; -} - -bool DebuggerMarshalls::ServersProfilerFrame::deserialize(const Array &p_arr) { - CHECK_SIZE(p_arr, 7, "ServersProfilerFrame"); - frame_number = p_arr[0]; - frame_time = p_arr[1]; - idle_time = p_arr[2]; - physics_time = p_arr[3]; - physics_frame_time = p_arr[4]; - script_time = p_arr[5]; - int servers_size = p_arr[6]; - int idx = 7; - while (servers_size) { - CHECK_SIZE(p_arr, idx + 2, "ServersProfilerFrame"); - servers_size--; - ServerInfo si; - si.name = p_arr[idx]; - int sub_data_size = p_arr[idx + 1]; - idx += 2; - CHECK_SIZE(p_arr, idx + sub_data_size, "ServersProfilerFrame"); - for (int j = 0; j < sub_data_size / 2; j++) { - ServerFunctionInfo sf; - sf.name = p_arr[idx]; - sf.time = p_arr[idx + 1]; - idx += 2; - si.functions.push_back(sf); - } - servers.push_back(si); - } - CHECK_SIZE(p_arr, idx + 1, "ServersProfilerFrame"); - int func_size = p_arr[idx]; - idx += 1; - CHECK_SIZE(p_arr, idx + func_size, "ServersProfilerFrame"); - for (int i = 0; i < func_size / 4; i++) { - ScriptFunctionInfo fi; - fi.sig_id = p_arr[idx]; - fi.call_count = p_arr[idx + 1]; - fi.self_time = p_arr[idx + 2]; - fi.total_time = p_arr[idx + 3]; - script_functions.push_back(fi); - idx += 4; - } - CHECK_END(p_arr, idx, "ServersProfilerFrame"); - return true; -} - Array DebuggerMarshalls::ScriptStackDump::serialize() { Array arr; arr.push_back(frames.size() * 3); @@ -298,33 +145,3 @@ bool DebuggerMarshalls::OutputError::deserialize(const Array &p_arr) { CHECK_END(p_arr, idx, "OutputError"); return true; } - -Array DebuggerMarshalls::VisualProfilerFrame::serialize() { - Array arr; - arr.push_back(frame_number); - arr.push_back(areas.size() * 3); - for (int i = 0; i < areas.size(); i++) { - arr.push_back(areas[i].name); - arr.push_back(areas[i].cpu_msec); - arr.push_back(areas[i].gpu_msec); - } - return arr; -} - -bool DebuggerMarshalls::VisualProfilerFrame::deserialize(const Array &p_arr) { - CHECK_SIZE(p_arr, 2, "VisualProfilerFrame"); - frame_number = p_arr[0]; - int size = p_arr[1]; - CHECK_SIZE(p_arr, size, "VisualProfilerFrame"); - int idx = 2; - areas.resize(size / 3); - RS::FrameProfileArea *w = areas.ptrw(); - for (int i = 0; i < size / 3; i++) { - w[i].name = p_arr[idx]; - w[i].cpu_msec = p_arr[idx + 1]; - w[i].gpu_msec = p_arr[idx + 2]; - idx += 3; - } - CHECK_END(p_arr, idx, "VisualProfilerFrame"); - return true; -} diff --git a/core/debugger/debugger_marshalls.h b/core/debugger/debugger_marshalls.h index fae766812a..378c3af8aa 100644 --- a/core/debugger/debugger_marshalls.h +++ b/core/debugger/debugger_marshalls.h @@ -32,86 +32,8 @@ #define DEBUGGER_MARSHARLLS_H #include "core/object/script_language.h" -#include "servers/rendering_server.h" struct DebuggerMarshalls { - // Memory usage - struct ResourceInfo { - String path; - String format; - String type; - RID id; - int vram = 0; - bool operator<(const ResourceInfo &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; } - }; - - struct ResourceUsage { - List<ResourceInfo> infos; - - Array serialize(); - bool deserialize(const Array &p_arr); - }; - - // Network profiler - struct MultiplayerNodeInfo { - ObjectID node; - String node_path; - int incoming_rpc = 0; - int incoming_rset = 0; - int outgoing_rpc = 0; - int outgoing_rset = 0; - }; - - struct NetworkProfilerFrame { - Vector<MultiplayerNodeInfo> infos; - - Array serialize(); - bool deserialize(const Array &p_arr); - }; - - // Script Profiler - class ScriptFunctionSignature { - public: - StringName name; - int id = -1; - - Array serialize(); - bool deserialize(const Array &p_arr); - }; - - struct ScriptFunctionInfo { - StringName name; - int sig_id = -1; - int call_count = 0; - double self_time = 0; - double total_time = 0; - }; - - // Servers profiler - struct ServerFunctionInfo { - StringName name; - double time = 0; - }; - - struct ServerInfo { - StringName name; - List<ServerFunctionInfo> functions; - }; - - struct ServersProfilerFrame { - int frame_number = 0; - double frame_time = 0; - double idle_time = 0; - double physics_time = 0; - double physics_frame_time = 0; - double script_time = 0; - List<ServerInfo> servers; - Vector<ScriptFunctionInfo> script_functions; - - Array serialize(); - bool deserialize(const Array &p_arr); - }; - struct ScriptStackVariable { String name; Variant value; @@ -145,15 +67,6 @@ struct DebuggerMarshalls { Array serialize(); bool deserialize(const Array &p_arr); }; - - // Visual Profiler - struct VisualProfilerFrame { - uint64_t frame_number = 0; - Vector<RS::FrameProfileArea> areas; - - Array serialize(); - bool deserialize(const Array &p_arr); - }; }; #endif // DEBUGGER_MARSHARLLS_H diff --git a/core/debugger/engine_profiler.cpp b/core/debugger/engine_profiler.cpp new file mode 100644 index 0000000000..c858b1febd --- /dev/null +++ b/core/debugger/engine_profiler.cpp @@ -0,0 +1,82 @@ +/*************************************************************************/ +/* engine_profiler.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 "engine_profiler.h" + +#include "core/debugger/engine_debugger.h" + +void EngineProfiler::_bind_methods() { + GDVIRTUAL_BIND(_toggle, "enable", "options"); + GDVIRTUAL_BIND(_add_frame, "data"); + GDVIRTUAL_BIND(_tick, "frame_time", "idle_time", "physics_time", "physics_frame_time"); +} + +void EngineProfiler::toggle(bool p_enable, const Array &p_array) { + GDVIRTUAL_CALL(_toggle, p_enable, p_array); +} + +void EngineProfiler::add(const Array &p_data) { + GDVIRTUAL_CALL(_add_frame, p_data); +} + +void EngineProfiler::tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { + GDVIRTUAL_CALL(_tick, p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time); +} + +Error EngineProfiler::bind(const String &p_name) { + ERR_FAIL_COND_V(is_bound(), ERR_ALREADY_IN_USE); + EngineDebugger::Profiler prof( + this, + [](void *p_user, bool p_enable, const Array &p_opts) { + ((EngineProfiler *)p_user)->toggle(p_enable, p_opts); + }, + [](void *p_user, const Array &p_data) { + ((EngineProfiler *)p_user)->add(p_data); + }, + [](void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { + ((EngineProfiler *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time); + }); + registration = p_name; + EngineDebugger::register_profiler(p_name, prof); + return OK; +} + +Error EngineProfiler::unbind() { + ERR_FAIL_COND_V(!is_bound(), ERR_UNCONFIGURED); + EngineDebugger::unregister_profiler(registration); + registration.clear(); + return OK; +} + +EngineProfiler::~EngineProfiler() { + if (is_bound()) { + unbind(); + } +} diff --git a/core/debugger/engine_profiler.h b/core/debugger/engine_profiler.h new file mode 100644 index 0000000000..ade280a7bb --- /dev/null +++ b/core/debugger/engine_profiler.h @@ -0,0 +1,65 @@ +/*************************************************************************/ +/* engine_profiler.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 ENGINE_PROFILER_H +#define ENGINE_PROFILER_H + +#include "core/object/ref_counted.h" + +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" + +class EngineProfiler : public RefCounted { + GDCLASS(EngineProfiler, RefCounted); + +private: + String registration; + +protected: + static void _bind_methods(); + +public: + virtual void toggle(bool p_enable, const Array &p_opts); + virtual void add(const Array &p_data); + virtual void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time); + + Error bind(const String &p_name); + Error unbind(); + bool is_bound() const { return registration.length() > 0; } + + GDVIRTUAL2(_toggle, bool, Array); + GDVIRTUAL1(_add_frame, Array); + GDVIRTUAL4(_tick, double, double, double, double); + + EngineProfiler() {} + virtual ~EngineProfiler(); +}; + +#endif // ENGINE_PROFILER_H diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index 339aa9b61f..2fce23d003 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -33,32 +33,13 @@ #include "core/config/project_settings.h" #include "core/debugger/debugger_marshalls.h" #include "core/debugger/engine_debugger.h" +#include "core/debugger/engine_profiler.h" #include "core/debugger/script_debugger.h" #include "core/input/input.h" #include "core/object/script_language.h" #include "core/os/os.h" -#include "scene/main/node.h" -#include "servers/display_server.h" - -template <typename T> -void RemoteDebugger::_bind_profiler(const String &p_name, T *p_prof) { - EngineDebugger::Profiler prof( - p_prof, - [](void *p_user, bool p_enable, const Array &p_opts) { - ((T *)p_user)->toggle(p_enable, p_opts); - }, - [](void *p_user, const Array &p_data) { - ((T *)p_user)->add(p_data); - }, - [](void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { - ((T *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time); - }); - EngineDebugger::register_profiler(p_name, prof); -} -struct RemoteDebugger::NetworkProfiler { -public: - typedef DebuggerMarshalls::MultiplayerNodeInfo NodeInfo; +class RemoteDebugger::MultiplayerProfiler : public EngineProfiler { struct BandwidthFrame { uint32_t timestamp; int packet_size; @@ -70,11 +51,6 @@ public: Vector<BandwidthFrame> bandwidth_out; uint64_t last_bandwidth_time = 0; - Map<ObjectID, NodeInfo> multiplayer_node_data; - uint64_t last_profile_time = 0; - - NetworkProfiler() {} - int bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) { ERR_FAIL_COND_V(p_buffer.size() == 0, 0); int total_bandwidth = 0; @@ -96,22 +72,8 @@ public: return total_bandwidth; } - void init_node(const ObjectID p_node) { - if (multiplayer_node_data.has(p_node)) { - return; - } - multiplayer_node_data.insert(p_node, DebuggerMarshalls::MultiplayerNodeInfo()); - multiplayer_node_data[p_node].node = p_node; - multiplayer_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path(); - multiplayer_node_data[p_node].incoming_rpc = 0; - multiplayer_node_data[p_node].incoming_rset = 0; - multiplayer_node_data[p_node].outgoing_rpc = 0; - multiplayer_node_data[p_node].outgoing_rset = 0; - } - +public: void toggle(bool p_enable, const Array &p_opts) { - multiplayer_node_data.clear(); - if (!p_enable) { bandwidth_in.clear(); bandwidth_out.clear(); @@ -130,37 +92,18 @@ public: } void add(const Array &p_data) { - ERR_FAIL_COND(p_data.size() < 1); - const String type = p_data[0]; - if (type == "node") { - ERR_FAIL_COND(p_data.size() < 3); - const ObjectID id = p_data[1]; - const String what = p_data[2]; - init_node(id); - NodeInfo &info = multiplayer_node_data[id]; - if (what == "rpc_in") { - info.incoming_rpc++; - } else if (what == "rpc_out") { - info.outgoing_rpc++; - } else if (what == "rset_in") { - info.incoming_rset = 0; - } else if (what == "rset_out") { - info.outgoing_rset++; - } - } else if (type == "bandwidth") { - ERR_FAIL_COND(p_data.size() < 4); - const String inout = p_data[1]; - int time = p_data[2]; - int size = p_data[3]; - if (inout == "in") { - bandwidth_in.write[bandwidth_in_ptr].timestamp = time; - bandwidth_in.write[bandwidth_in_ptr].packet_size = size; - bandwidth_in_ptr = (bandwidth_in_ptr + 1) % bandwidth_in.size(); - } else if (inout == "out") { - bandwidth_out.write[bandwidth_out_ptr].timestamp = time; - bandwidth_out.write[bandwidth_out_ptr].packet_size = size; - bandwidth_out_ptr = (bandwidth_out_ptr + 1) % bandwidth_out.size(); - } + ERR_FAIL_COND(p_data.size() < 3); + const String inout = p_data[0]; + int time = p_data[1]; + int size = p_data[2]; + if (inout == "in") { + bandwidth_in.write[bandwidth_in_ptr].timestamp = time; + bandwidth_in.write[bandwidth_in_ptr].packet_size = size; + bandwidth_in_ptr = (bandwidth_in_ptr + 1) % bandwidth_in.size(); + } else if (inout == "out") { + bandwidth_out.write[bandwidth_out_ptr].timestamp = time; + bandwidth_out.write[bandwidth_out_ptr].packet_size = size; + bandwidth_out_ptr = (bandwidth_out_ptr + 1) % bandwidth_out.size(); } } @@ -174,208 +117,17 @@ public: Array arr; arr.push_back(incoming_bandwidth); arr.push_back(outgoing_bandwidth); - EngineDebugger::get_singleton()->send_message("network:bandwidth", arr); - } - if (pt - last_profile_time > 100) { - last_profile_time = pt; - DebuggerMarshalls::NetworkProfilerFrame frame; - 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()); - } - } -}; - -struct RemoteDebugger::ScriptsProfiler { - typedef DebuggerMarshalls::ScriptFunctionSignature FunctionSignature; - typedef DebuggerMarshalls::ScriptFunctionInfo FunctionInfo; - struct ProfileInfoSort { - bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const { - return A->total_time < B->total_time; - } - }; - Vector<ScriptLanguage::ProfilingInfo> info; - Vector<ScriptLanguage::ProfilingInfo *> ptrs; - Map<StringName, int> sig_map; - int max_frame_functions = 16; - - void toggle(bool p_enable, const Array &p_opts) { - if (p_enable) { - sig_map.clear(); - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ScriptServer::get_language(i)->profiling_start(); - } - if (p_opts.size() == 1 && p_opts[0].get_type() == Variant::INT) { - max_frame_functions = MAX(0, int(p_opts[0])); - } - } else { - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ScriptServer::get_language(i)->profiling_stop(); - } + EngineDebugger::get_singleton()->send_message("multiplayer:bandwidth", arr); } } - - void write_frame_data(Vector<FunctionInfo> &r_funcs, uint64_t &r_total, bool p_accumulated) { - int ofs = 0; - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - if (p_accumulated) { - ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&info.write[ofs], info.size() - ofs); - } else { - ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&info.write[ofs], info.size() - ofs); - } - } - - for (int i = 0; i < ofs; i++) { - ptrs.write[i] = &info.write[i]; - } - - SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa; - sa.sort(ptrs.ptrw(), ofs); - - int to_send = MIN(ofs, max_frame_functions); - - // Check signatures first, and compute total time. - r_total = 0; - for (int i = 0; i < to_send; i++) { - if (!sig_map.has(ptrs[i]->signature)) { - int idx = sig_map.size(); - FunctionSignature sig; - sig.name = ptrs[i]->signature; - sig.id = idx; - EngineDebugger::get_singleton()->send_message("servers:function_signature", sig.serialize()); - sig_map[ptrs[i]->signature] = idx; - } - r_total += ptrs[i]->self_time; - } - - // Send frame, script time, functions information then - r_funcs.resize(to_send); - - FunctionInfo *w = r_funcs.ptrw(); - for (int i = 0; i < to_send; i++) { - if (sig_map.has(ptrs[i]->signature)) { - w[i].sig_id = sig_map[ptrs[i]->signature]; - } - w[i].call_count = ptrs[i]->call_count; - w[i].total_time = ptrs[i]->total_time / 1000000.0; - w[i].self_time = ptrs[i]->self_time / 1000000.0; - } - } - - ScriptsProfiler() { - info.resize(GLOBAL_GET("debug/settings/profiler/max_functions")); - ptrs.resize(info.size()); - } }; -struct RemoteDebugger::ServersProfiler { - bool skip_profile_frame = false; - typedef DebuggerMarshalls::ServerInfo ServerInfo; - typedef DebuggerMarshalls::ServerFunctionInfo ServerFunctionInfo; - - Map<StringName, ServerInfo> server_data; - ScriptsProfiler scripts_profiler; - - double frame_time = 0; - double idle_time = 0; - double physics_time = 0; - double physics_frame_time = 0; - - void toggle(bool p_enable, const Array &p_opts) { - skip_profile_frame = false; - if (p_enable) { - server_data.clear(); // Clear old profiling data. - } else { - _send_frame_data(true); // Send final frame. - } - scripts_profiler.toggle(p_enable, p_opts); - } - - void add(const Array &p_data) { - String name = p_data[0]; - if (!server_data.has(name)) { - ServerInfo info; - info.name = name; - server_data[name] = info; - } - ServerInfo &srv = server_data[name]; - - ServerFunctionInfo fi; - fi.name = p_data[1]; - fi.time = p_data[2]; - srv.functions.push_back(fi); - } - - void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { - frame_time = p_frame_time; - idle_time = p_idle_time; - physics_time = p_physics_time; - physics_frame_time = p_physics_frame_time; - _send_frame_data(false); - } - - void _send_frame_data(bool p_final) { - DebuggerMarshalls::ServersProfilerFrame frame; - frame.frame_number = Engine::get_singleton()->get_process_frames(); - frame.frame_time = frame_time; - frame.idle_time = idle_time; - frame.physics_time = physics_time; - frame.physics_frame_time = physics_frame_time; - Map<StringName, ServerInfo>::Element *E = server_data.front(); - while (E) { - if (!p_final) { - frame.servers.push_back(E->get()); - } - E->get().functions.clear(); - E = E->next(); - } - uint64_t time = 0; - scripts_profiler.write_frame_data(frame.script_functions, time, p_final); - frame.script_time = USEC_TO_SEC(time); - if (skip_profile_frame) { - skip_profile_frame = false; - return; - } - if (p_final) { - EngineDebugger::get_singleton()->send_message("servers:profile_total", frame.serialize()); - } else { - EngineDebugger::get_singleton()->send_message("servers:profile_frame", frame.serialize()); - } - } -}; - -struct RemoteDebugger::VisualProfiler { - typedef DebuggerMarshalls::ServerInfo ServerInfo; - typedef DebuggerMarshalls::ServerFunctionInfo ServerFunctionInfo; - - Map<StringName, ServerInfo> server_data; - - void toggle(bool p_enable, const Array &p_opts) { - RS::get_singleton()->set_frame_profiling_enabled(p_enable); - } - - void add(const Array &p_data) {} - - void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { - Vector<RS::FrameProfileArea> profile_areas = RS::get_singleton()->get_frame_profile(); - DebuggerMarshalls::VisualProfilerFrame frame; - if (!profile_areas.size()) { - return; - } - - frame.frame_number = RS::get_singleton()->get_frame_profile_frame(); - frame.areas.append_array(profile_areas); - EngineDebugger::get_singleton()->send_message("visual:profile_frame", frame.serialize()); - } -}; - -struct RemoteDebugger::PerformanceProfiler { +class RemoteDebugger::PerformanceProfiler : public EngineProfiler { Object *performance = nullptr; int last_perf_time = 0; uint64_t last_monitor_modification_time = 0; +public: void toggle(bool p_enable, const Array &p_opts) {} void add(const Array &p_data) {} void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { @@ -421,29 +173,6 @@ struct RemoteDebugger::PerformanceProfiler { } }; -void RemoteDebugger::_send_resource_usage() { - DebuggerMarshalls::ResourceUsage usage; - - List<RS::TextureInfo> tinfo; - RS::get_singleton()->texture_debug_usage(&tinfo); - - for (const RS::TextureInfo &E : tinfo) { - DebuggerMarshalls::ResourceInfo info; - info.path = E.path; - info.vram = E.bytes; - info.id = E.texture; - info.type = "Texture"; - if (E.depth == 0) { - info.format = itos(E.width) + "x" + itos(E.height) + " " + Image::get_format_name(E.format); - } else { - info.format = itos(E.width) + "x" + itos(E.height) + "x" + itos(E.depth) + " " + Image::get_format_name(E.format); - } - usage.infos.push_back(info); - } - - EngineDebugger::get_singleton()->send_message("memory:usage", usage.serialize()); -} - Error RemoteDebugger::_put_msg(String p_message, Array p_data) { Array msg; msg.push_back(p_message); @@ -710,18 +439,12 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { msg.push_back(script_lang->debug_get_stack_level_count() > 0); send_message("debug_enter", msg); - servers_profiler->skip_profile_frame = true; // Avoid frame time spike in debug. - Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode(); if (mouse_mode != Input::MOUSE_MODE_VISIBLE) { Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); } - uint64_t loop_begin_usec = 0; - uint64_t loop_time_sec = 0; while (is_peer_connected()) { - loop_begin_usec = OS::get_singleton()->get_ticks_usec(); - flush_output(); peer->poll(); @@ -748,7 +471,6 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } else if (command == "continue") { script_debugger->set_depth(-1); script_debugger->set_lines_left(-1); - DisplayServer::get_singleton()->window_move_to_foreground(); break; } else if (command == "break") { @@ -824,13 +546,6 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { OS::get_singleton()->delay_usec(10000); OS::get_singleton()->process_and_drop_events(); } - - // This is for the camera override to stay live even when the game is paused from the editor - loop_time_sec = (OS::get_singleton()->get_ticks_usec() - loop_begin_usec) / 1000000.0f; - RenderingServer::get_singleton()->sync(); - if (RenderingServer::get_singleton()->has_changed()) { - RenderingServer::get_singleton()->draw(true, loop_time_sec * Engine::get_singleton()->get_time_scale()); - } } send_message("debug_exit", Array()); @@ -897,8 +612,6 @@ Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bo } else if (p_cmd == "set_skip_breakpoints") { ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA); script_debugger->set_skip_breakpoints(p_data[0]); - } else if (p_cmd == "memory") { - _send_resource_usage(); } else if (p_cmd == "break") { script_debugger->debug(script_debugger->get_break_language()); } else { @@ -928,23 +641,15 @@ RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) { max_errors_per_second = GLOBAL_GET("network/limits/debugger/max_errors_per_second"); max_warnings_per_second = GLOBAL_GET("network/limits/debugger/max_warnings_per_second"); - // Network Profiler - network_profiler = memnew(NetworkProfiler); - _bind_profiler("network", network_profiler); - - // Servers Profiler (audio/physics/...) - servers_profiler = memnew(ServersProfiler); - _bind_profiler("servers", servers_profiler); - - // Visual Profiler (cpu/gpu times) - visual_profiler = memnew(VisualProfiler); - _bind_profiler("visual", visual_profiler); + // Multiplayer Profiler + multiplayer_profiler.instantiate(); + multiplayer_profiler->bind("multiplayer"); // Performance Profiler Object *perf = Engine::get_singleton()->get_singleton_object("Performance"); if (perf) { - performance_profiler = memnew(PerformanceProfiler(perf)); - _bind_profiler("performance", performance_profiler); + performance_profiler = Ref<PerformanceProfiler>(memnew(PerformanceProfiler(perf))); + performance_profiler->bind("performance"); profiler_enable("performance", true); } @@ -973,17 +678,4 @@ RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) { RemoteDebugger::~RemoteDebugger() { remove_print_handler(&phl); remove_error_handler(&eh); - - EngineDebugger::get_singleton()->unregister_profiler("servers"); - EngineDebugger::get_singleton()->unregister_profiler("network"); - EngineDebugger::get_singleton()->unregister_profiler("visual"); - if (EngineDebugger::has_profiler("performance")) { - EngineDebugger::get_singleton()->unregister_profiler("performance"); - } - memdelete(servers_profiler); - memdelete(network_profiler); - memdelete(visual_profiler); - if (performance_profiler) { - memdelete(performance_profiler); - } } diff --git a/core/debugger/remote_debugger.h b/core/debugger/remote_debugger.h index bd64955c89..aada92da60 100644 --- a/core/debugger/remote_debugger.h +++ b/core/debugger/remote_debugger.h @@ -49,16 +49,11 @@ public: private: typedef DebuggerMarshalls::OutputError ErrorMessage; - struct NetworkProfiler; - struct ServersProfiler; - struct ScriptsProfiler; - struct VisualProfiler; - struct PerformanceProfiler; + class MultiplayerProfiler; + class PerformanceProfiler; - NetworkProfiler *network_profiler = nullptr; - ServersProfiler *servers_profiler = nullptr; - VisualProfiler *visual_profiler = nullptr; - PerformanceProfiler *performance_profiler = nullptr; + Ref<MultiplayerProfiler> multiplayer_profiler; + Ref<PerformanceProfiler> performance_profiler; Ref<RemoteDebuggerPeer> peer; @@ -97,7 +92,6 @@ private: bool is_peer_connected() { return peer->is_peer_connected(); } void flush_output(); - void _send_resource_usage(); void _send_stack_vars(List<String> &p_names, List<Variant> &p_vals, int p_type); Error _profiler_capture(const String &p_cmd, const Array &p_data, bool &r_captured); diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp index a45430465f..7c7d38ab0a 100644 --- a/core/debugger/remote_debugger_peer.cpp +++ b/core/debugger/remote_debugger_peer.cpp @@ -68,8 +68,8 @@ void RemoteDebuggerPeerTCP::close() { running = false; thread.wait_to_finish(); tcp_client->disconnect_from_host(); - out_buf.resize(0); - in_buf.resize(0); + out_buf.clear(); + in_buf.clear(); } RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_tcp) { @@ -190,7 +190,8 @@ Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_po } void RemoteDebuggerPeerTCP::_thread_func(void *p_ud) { - const uint64_t min_tick = 100; + // Update in time for 144hz monitors + const uint64_t min_tick = 6900; RemoteDebuggerPeerTCP *peer = (RemoteDebuggerPeerTCP *)p_ud; while (peer->running && peer->is_peer_connected()) { uint64_t ticks_usec = OS::get_singleton()->get_ticks_usec(); @@ -225,7 +226,7 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) { String debug_host = p_uri.replace("tcp://", ""); uint16_t debug_port = 6007; - if (debug_host.find(":") != -1) { + if (debug_host.contains(":")) { int sep_pos = debug_host.rfind(":"); debug_port = debug_host.substr(sep_pos + 1).to_int(); debug_host = debug_host.substr(0, sep_pos); diff --git a/core/debugger/script_debugger.cpp b/core/debugger/script_debugger.cpp index 36723e5568..4dd93249ef 100644 --- a/core/debugger/script_debugger.cpp +++ b/core/debugger/script_debugger.cpp @@ -104,7 +104,7 @@ void ScriptDebugger::send_error(const String &p_func, const String &p_file, int // 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_editor_notify, p_type); - error_stack_info.resize(0); + error_stack_info.clear(); } Vector<ScriptLanguage::StackInfo> ScriptDebugger::get_error_stack_info() const { diff --git a/core/error/error_macros.cpp b/core/error/error_macros.cpp index 928ddd3397..ceccd43259 100644 --- a/core/error/error_macros.cpp +++ b/core/error/error_macros.cpp @@ -118,3 +118,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li 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); } + +void _err_flush_stdout() { + fflush(stdout); +} diff --git a/core/error/error_macros.h b/core/error/error_macros.h index 802d7f9ef4..7b032fb4cd 100644 --- a/core/error/error_macros.h +++ b/core/error/error_macros.h @@ -69,6 +69,7 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co 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); +void _err_flush_stdout(); #ifdef __GNUC__ //#define FUNCTION_STR __PRETTY_FUNCTION__ - too annoying @@ -789,6 +790,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li #define CRASH_NOW() \ if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed."); \ + _err_flush_stdout(); \ GENERATE_TRAP(); \ } else \ ((void)0) @@ -801,6 +803,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li #define CRASH_NOW_MSG(m_msg) \ if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", m_msg); \ + _err_flush_stdout(); \ GENERATE_TRAP(); \ } else \ ((void)0) diff --git a/core/input/input.cpp b/core/input/input.cpp index fa2f00bf8d..d36d0f4da0 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -90,6 +90,7 @@ Input::MouseMode Input::get_mouse_mode() const { } void Input::_bind_methods() { + ClassDB::bind_method(D_METHOD("is_anything_pressed"), &Input::is_anything_pressed); ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed); ClassDB::bind_method(D_METHOD("is_physical_key_pressed", "keycode"), &Input::is_physical_key_pressed); ClassDB::bind_method(D_METHOD("is_mouse_button_pressed", "button"), &Input::is_mouse_button_pressed); @@ -189,35 +190,53 @@ void Input::VelocityTrack::update(const Vector2 &p_delta_p) { float delta_t = tdiff / 1000000.0; last_tick = tick; + if (delta_t > max_ref_frame) { + // First movement in a long time, reset and start again. + velocity = Vector2(); + accum = p_delta_p; + accum_t = 0; + return; + } + accum += p_delta_p; accum_t += delta_t; - if (accum_t > max_ref_frame * 10) { - accum_t = max_ref_frame * 10; + if (accum_t < min_ref_frame) { + // Not enough time has passed to calculate speed precisely. + return; } - while (accum_t >= min_ref_frame) { - float slice_t = min_ref_frame / accum_t; - Vector2 slice = accum * slice_t; - accum = accum - slice; - accum_t -= min_ref_frame; - - velocity = (slice / min_ref_frame).lerp(velocity, min_ref_frame / max_ref_frame); - } + velocity = accum / accum_t; + accum = Vector2(); + accum_t = 0; } void Input::VelocityTrack::reset() { last_tick = OS::get_singleton()->get_ticks_usec(); velocity = Vector2(); + accum = Vector2(); accum_t = 0; } Input::VelocityTrack::VelocityTrack() { min_ref_frame = 0.1; - max_ref_frame = 0.3; + max_ref_frame = 3.0; reset(); } +bool Input::is_anything_pressed() const { + _THREAD_SAFE_METHOD_ + + for (Map<StringName, Input::Action>::Element *E = action_state.front(); E; E = E->next()) { + if (E->get().pressed) { + return true; + } + } + return !keys_pressed.is_empty() || + !joy_buttons_pressed.is_empty() || + mouse_button_mask > MouseButton::NONE; +} + bool Input::is_key_pressed(Key p_keycode) const { _THREAD_SAFE_METHOD_ return keys_pressed.has(p_keycode); @@ -504,18 +523,20 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - Point2 pos = mm->get_global_position(); - if (mouse_pos != pos) { - set_mouse_position(pos); + Point2 position = mm->get_global_position(); + if (mouse_pos != position) { + set_mouse_position(position); } + Vector2 relative = mm->get_relative(); + mouse_velocity_track.update(relative); if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && (mm->get_button_mask() & MouseButton::LEFT) != MouseButton::NONE) { Ref<InputEventScreenDrag> drag_event; drag_event.instantiate(); - drag_event->set_position(mm->get_position()); - drag_event->set_relative(mm->get_relative()); - drag_event->set_velocity(mm->get_velocity()); + drag_event->set_position(position); + drag_event->set_relative(relative); + drag_event->set_velocity(get_last_mouse_velocity()); event_dispatch_function(drag_event); } @@ -696,7 +717,6 @@ void Input::set_gyroscope(const Vector3 &p_gyroscope) { } void Input::set_mouse_position(const Point2 &p_posf) { - mouse_velocity_track.update(p_posf - mouse_pos); mouse_pos = p_posf; } @@ -704,7 +724,8 @@ Point2 Input::get_mouse_position() const { return mouse_pos; } -Point2 Input::get_last_mouse_velocity() const { +Point2 Input::get_last_mouse_velocity() { + mouse_velocity_track.update(Vector2()); return mouse_velocity_track.velocity; } diff --git a/core/input/input.h b/core/input/input.h index e5ef31ab4f..ab2cd377f4 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -242,6 +242,7 @@ public: static Input *get_singleton(); + bool is_anything_pressed() const; bool is_key_pressed(Key p_keycode) const; bool is_physical_key_pressed(Key p_keycode) const; bool is_mouse_button_pressed(MouseButton p_button) const; @@ -269,7 +270,7 @@ public: Vector3 get_gyroscope() const; Point2 get_mouse_position() const; - Vector2 get_last_mouse_velocity() const; + Vector2 get_last_mouse_velocity(); MouseButton get_mouse_button_mask() const; void warp_mouse_position(const Vector2 &p_to); diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index c608076a21..ab0f36132f 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -86,7 +86,7 @@ Ref<InputEvent> InputEvent::xformed_by(const Transform2D &p_xform, const Vector2 return Ref<InputEvent>((InputEvent *)this); } -bool InputEvent::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { +bool InputEvent::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const { return false; } @@ -412,35 +412,32 @@ Ref<InputEventKey> InputEventKey::create_reference(Key p_keycode) { return ie; } -bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { +bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const { Ref<InputEventKey> key = p_event; if (key.is_null()) { return false; } - bool match = false; - if (get_keycode() == Key::NONE) { - Key code = get_physical_keycode_with_modifiers(); - Key event_code = key->get_physical_keycode_with_modifiers(); - - match = get_physical_keycode() == key->get_physical_keycode() && (!key->is_pressed() || (code & event_code) == code); + bool match; + if (keycode != Key::NONE) { + match = keycode == key->keycode; } else { - Key code = get_keycode_with_modifiers(); - Key event_code = key->get_keycode_with_modifiers(); - - match = get_keycode() == key->get_keycode() && (!key->is_pressed() || (code & event_code) == code); + match = get_physical_keycode() == key->get_physical_keycode(); + } + if (p_exact_match) { + match &= get_modifiers_mask() == key->get_modifiers_mask(); } if (match) { bool pressed = key->is_pressed(); - if (p_pressed != nullptr) { - *p_pressed = pressed; + if (r_pressed != nullptr) { + *r_pressed = pressed; } float strength = pressed ? 1.0f : 0.0f; - if (p_strength != nullptr) { - *p_strength = strength; + if (r_strength != nullptr) { + *r_strength = strength; } - if (p_raw_strength != nullptr) { - *p_raw_strength = strength; + if (r_raw_strength != nullptr) { + *r_raw_strength = strength; } } return match; @@ -585,24 +582,27 @@ Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, co return mb; } -bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { +bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const { Ref<InputEventMouseButton> mb = p_event; if (mb.is_null()) { return false; } - bool match = mb->button_index == button_index; + bool match = button_index == mb->button_index; + if (p_exact_match) { + match &= get_modifiers_mask() == mb->get_modifiers_mask(); + } if (match) { bool pressed = mb->is_pressed(); - if (p_pressed != nullptr) { - *p_pressed = pressed; + if (r_pressed != nullptr) { + *r_pressed = pressed; } float strength = pressed ? 1.0f : 0.0f; - if (p_strength != nullptr) { - *p_strength = strength; + if (r_strength != nullptr) { + *r_strength = strength; } - if (p_raw_strength != nullptr) { - *p_raw_strength = strength; + if (r_raw_strength != nullptr) { + *r_raw_strength = strength; } } @@ -887,36 +887,40 @@ bool InputEventJoypadMotion::is_pressed() const { return Math::abs(axis_value) >= 0.5f; } -bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { +bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const { Ref<InputEventJoypadMotion> jm = p_event; if (jm.is_null()) { return false; } - bool match = (axis == jm->axis); // Matches even if not in the same direction, but returns a "not pressed" event. + // Matches even if not in the same direction, but returns a "not pressed" event. + bool match = axis == jm->axis; + if (p_exact_match) { + match &= (axis_value < 0) == (jm->axis_value < 0); + } if (match) { float jm_abs_axis_value = Math::abs(jm->get_axis_value()); bool same_direction = (((axis_value < 0) == (jm->axis_value < 0)) || jm->axis_value == 0); bool pressed = same_direction && jm_abs_axis_value >= p_deadzone; - if (p_pressed != nullptr) { - *p_pressed = pressed; + if (r_pressed != nullptr) { + *r_pressed = pressed; } - if (p_strength != nullptr) { + if (r_strength != nullptr) { if (pressed) { if (p_deadzone == 1.0f) { - *p_strength = 1.0f; + *r_strength = 1.0f; } else { - *p_strength = CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, jm_abs_axis_value), 0.0f, 1.0f); + *r_strength = CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, jm_abs_axis_value), 0.0f, 1.0f); } } else { - *p_strength = 0.0f; + *r_strength = 0.0f; } } - if (p_raw_strength != nullptr) { + if (r_raw_strength != nullptr) { if (same_direction) { // NOT pressed, because we want to ignore the deadzone. - *p_raw_strength = jm_abs_axis_value; + *r_raw_strength = jm_abs_axis_value; } else { - *p_raw_strength = 0.0f; + *r_raw_strength = 0.0f; } } } @@ -994,7 +998,7 @@ float InputEventJoypadButton::get_pressure() const { return pressure; } -bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { +bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const { Ref<InputEventJoypadButton> jb = p_event; if (jb.is_null()) { return false; @@ -1003,15 +1007,15 @@ bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool * bool match = button_index == jb->button_index; if (match) { bool pressed = jb->is_pressed(); - if (p_pressed != nullptr) { - *p_pressed = pressed; + if (r_pressed != nullptr) { + *r_pressed = pressed; } float strength = pressed ? 1.0f : 0.0f; - if (p_strength != nullptr) { - *p_strength = strength; + if (r_strength != nullptr) { + *r_strength = strength; } - if (p_raw_strength != nullptr) { - *p_raw_strength = strength; + if (r_raw_strength != nullptr) { + *r_raw_strength = strength; } } @@ -1217,8 +1221,9 @@ String InputEventScreenDrag::to_string() { bool InputEventScreenDrag::accumulate(const Ref<InputEvent> &p_event) { Ref<InputEventScreenDrag> drag = p_event; - if (drag.is_null()) + if (drag.is_null()) { return false; + } if (get_index() != drag->get_index()) { return false; @@ -1288,7 +1293,7 @@ bool InputEventAction::is_action(const StringName &p_action) const { return action == p_action; } -bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { +bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const { Ref<InputEventAction> act = p_event; if (act.is_null()) { return false; @@ -1297,15 +1302,15 @@ bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool *p_pres bool match = action == act->action; if (match) { bool pressed = act->pressed; - if (p_pressed != nullptr) { - *p_pressed = pressed; + if (r_pressed != nullptr) { + *r_pressed = pressed; } float strength = pressed ? 1.0f : 0.0f; - if (p_strength != nullptr) { - *p_strength = strength; + if (r_strength != nullptr) { + *r_strength = strength; } - if (p_raw_strength != nullptr) { - *p_raw_strength = strength; + if (r_raw_strength != nullptr) { + *r_raw_strength = strength; } } return match; diff --git a/core/input/input_event.h b/core/input/input_event.h index 29450dfc52..114db46623 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -79,7 +79,7 @@ public: virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const; + virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const; virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const; virtual bool is_action_type() const; @@ -192,7 +192,7 @@ public: Key get_keycode_with_modifiers() const; Key get_physical_keycode_with_modifiers() const; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; + virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; virtual bool is_action_type() const override { return true; } @@ -255,7 +255,7 @@ public: virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; + virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; virtual bool is_action_type() const override { return true; } @@ -315,7 +315,7 @@ public: virtual bool is_pressed() const override; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; + virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; virtual bool is_action_type() const override { return true; } @@ -344,7 +344,7 @@ public: void set_pressure(float p_pressure); float get_pressure() const; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; + virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; virtual bool is_action_type() const override { return true; } @@ -437,7 +437,7 @@ public: virtual bool is_action(const StringName &p_action) const; - virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override; + virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; virtual bool is_action_type() const override { return true; } diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index 753ac72ab6..41083b4c47 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -126,15 +126,13 @@ List<StringName> InputMap::get_actions() const { return actions; } -List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const { +List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const { ERR_FAIL_COND_V(!p_event.is_valid(), nullptr); for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) { int device = E->get()->get_device(); if (device == ALL_DEVICES || device == p_event->get_device()) { - if (p_exact_match && E->get()->is_match(p_event, true)) { - return E; - } else if (!p_exact_match && E->get()->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) { + if (E->get()->action_match(p_event, p_exact_match, p_action.deadzone, r_pressed, r_strength, r_raw_strength)) { return E; } } @@ -217,40 +215,28 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName return event_get_action_status(p_event, p_action, p_exact_match); } -bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const { +bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const { OrderedHashMap<StringName, Action>::Element E = input_map.find(p_action); ERR_FAIL_COND_V_MSG(!E, false, suggest_actions(p_action)); Ref<InputEventAction> input_event_action = p_event; if (input_event_action.is_valid()) { - bool pressed = input_event_action->is_pressed(); - if (p_pressed != nullptr) { - *p_pressed = pressed; + const bool pressed = input_event_action->is_pressed(); + if (r_pressed != nullptr) { + *r_pressed = pressed; + } + const float strength = pressed ? input_event_action->get_strength() : 0.0f; + if (r_strength != nullptr) { + *r_strength = strength; } - if (p_strength != nullptr) { - *p_strength = pressed ? input_event_action->get_strength() : 0.0f; + if (r_raw_strength != nullptr) { + *r_raw_strength = strength; } return input_event_action->get_action() == p_action; } - bool pressed; - float strength; - float raw_strength; - List<Ref<InputEvent>>::Element *event = _find_event(E.get(), p_event, p_exact_match, &pressed, &strength, &raw_strength); - if (event != nullptr) { - if (p_pressed != nullptr) { - *p_pressed = pressed; - } - if (p_strength != nullptr) { - *p_strength = strength; - } - if (p_raw_strength != nullptr) { - *p_raw_strength = raw_strength; - } - return true; - } else { - return false; - } + List<Ref<InputEvent>>::Element *event = _find_event(E.get(), p_event, p_exact_match, r_pressed, r_strength, r_raw_strength); + return event != nullptr; } const OrderedHashMap<StringName, InputMap::Action> &InputMap::get_action_map() const { diff --git a/core/input/input_map.h b/core/input/input_map.h index e896d1f679..79b4d1038f 100644 --- a/core/input/input_map.h +++ b/core/input/input_map.h @@ -58,7 +58,7 @@ private: 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; + List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const; Array _action_get_events(const StringName &p_action); Array _get_actions(); @@ -83,7 +83,7 @@ public: const List<Ref<InputEvent>> *action_get_events(const StringName &p_action); bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const; - bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; + bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const; const OrderedHashMap<StringName, Action> &get_action_map() const; void load_from_project_settings(); diff --git a/core/io/compression.cpp b/core/io/compression.cpp index d1f915f064..ae5ccf8354 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -212,7 +212,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s strm.avail_in = p_src_size; // Ensure the destination buffer is empty - p_dst_vect->resize(0); + p_dst_vect->clear(); // decompress until deflate stream ends or end of file do { @@ -244,7 +244,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s WARN_PRINT(strm.msg); } (void)inflateEnd(&strm); - p_dst_vect->resize(0); + p_dst_vect->clear(); return ret; } } while (strm.avail_out > 0 && strm.avail_in > 0); @@ -254,7 +254,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s // Enforce max output size if (p_max_dst_size > -1 && strm.total_out > (uint64_t)p_max_dst_size) { (void)inflateEnd(&strm); - p_dst_vect->resize(0); + p_dst_vect->clear(); return Z_BUF_ERROR; } } while (ret != Z_STREAM_END); diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp index 3da9288ffd..86d8dea3d9 100644 --- a/core/io/dir_access.cpp +++ b/core/io/dir_access.cpp @@ -145,17 +145,21 @@ Error DirAccess::make_dir_recursive(String p_dir) { full_dir = full_dir.replace("\\", "/"); - //int slices = full_dir.get_slice_count("/"); - String base; if (full_dir.begins_with("res://")) { base = "res://"; } else if (full_dir.begins_with("user://")) { base = "user://"; + } else if (full_dir.is_network_share_path()) { + int pos = full_dir.find("/", 2); + ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER); + pos = full_dir.find("/", pos + 1); + ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER); + base = full_dir.substr(0, pos + 1); } else if (full_dir.begins_with("/")) { base = "/"; - } else if (full_dir.find(":/") != -1) { + } else if (full_dir.contains(":/")) { base = full_dir.substr(0, full_dir.find(":/") + 2); } else { ERR_FAIL_V(ERR_INVALID_PARAMETER); diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index bb92648484..86836454c5 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -538,7 +538,7 @@ void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_ for (int i = 0; i < size; ++i) { String value = p_values[i]; - if (value.find("\"") != -1 || value.find(p_delim) != -1 || value.find("\n") != -1) { + if (value.contains("\"") || value.contains(p_delim) || value.contains("\n")) { value = "\"" + value.replace("\"", "\"\"") + "\""; } if (i < size - 1) { diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index 307004b1c2..cb38ac0928 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -487,7 +487,6 @@ FileAccessNetwork::~FileAccessNetwork() { FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; nc->lock_mutex(); - id = nc->last_id++; nc->accesses.erase(id); nc->unlock_mutex(); } diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 3882627103..7dbea96c3d 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -70,7 +70,7 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64 String p = p_path.replace_first("res://", ""); PackedDir *cd = root; - if (p.find("/") != -1) { //in a subdir + if (p.contains("/")) { //in a subdir Vector<String> ds = p.get_base_dir().split("/"); diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 4d0747c591..52b1120b2a 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -96,6 +96,17 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) { return query.substr(1); } +Error HTTPClient::verify_headers(const Vector<String> &p_headers) { + for (int i = 0; i < p_headers.size(); i++) { + String sanitized = p_headers[i].strip_edges(); + ERR_FAIL_COND_V_MSG(sanitized.is_empty(), ERR_INVALID_PARAMETER, "Invalid HTTP header at index " + itos(i) + ": empty."); + ERR_FAIL_COND_V_MSG(sanitized.find(":") < 1, ERR_INVALID_PARAMETER, + "Invalid HTTP header at index " + itos(i) + ": String must contain header-value pair, delimited by ':', but was: " + p_headers[i]); + } + + return OK; +} + Dictionary HTTPClient::_get_response_headers_as_dictionary() { List<String> rh; get_response_headers(&rh); diff --git a/core/io/http_client.h b/core/io/http_client.h index 8be6e6524c..de6045f647 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -165,6 +165,7 @@ public: static HTTPClient *create(); String query_string_from_dict(const Dictionary &p_dict); + Error verify_headers(const Vector<String> &p_headers); virtual Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) = 0; virtual Error connect_to_host(const String &p_host, int p_port = -1, bool p_ssl = false, bool p_verify_host = true) = 0; @@ -180,7 +181,7 @@ public: virtual bool is_response_chunked() const = 0; virtual int get_response_code() const = 0; virtual Error get_response_headers(List<String> *r_response) = 0; - virtual int get_response_body_length() const = 0; + virtual int64_t get_response_body_length() const = 0; virtual PackedByteArray read_response_body_chunk() = 0; // Can't get body as partial text because of most encodings UTF8, gzip, etc. diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp index 4c27cd1b10..f920799677 100644 --- a/core/io/http_client_tcp.cpp +++ b/core/io/http_client_tcp.cpp @@ -71,7 +71,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss connection = tcp_connection; if (ssl && https_proxy_port != -1) { - proxy_client.instantiate(); // Needs proxy negotiation + proxy_client.instantiate(); // Needs proxy negotiation. server_host = https_proxy_host; server_port = https_proxy_port; } else if (!ssl && http_proxy_port != -1) { @@ -83,7 +83,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss } if (server_host.is_valid_ip_address()) { - // Host contains valid IP + // Host contains valid IP. Error err = tcp_connection->connect_to_host(IPAddress(server_host), server_port); if (err) { status = STATUS_CANT_CONNECT; @@ -92,7 +92,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss status = STATUS_CONNECTING; } else { - // Host contains hostname and needs to be resolved to IP + // Host contains hostname and needs to be resolved to IP. resolving = IP::get_singleton()->resolve_hostname_queue_item(server_host); status = STATUS_RESOLVING; } @@ -124,7 +124,7 @@ Ref<StreamPeer> HTTPClientTCP::get_connection() const { static bool _check_request_url(HTTPClientTCP::Method p_method, const String &p_url) { switch (p_method) { case HTTPClientTCP::METHOD_CONNECT: { - // Authority in host:port format, as in RFC7231 + // Authority in host:port format, as in RFC7231. int pos = p_url.find_char(':'); return 0 < pos && pos < p_url.length() - 1; } @@ -135,7 +135,7 @@ static bool _check_request_url(HTTPClientTCP::Method p_method, const String &p_u [[fallthrough]]; } default: - // Absolute path or absolute URL + // Absolute path or absolute URL. return p_url.begins_with("/") || p_url.begins_with("http://") || p_url.begins_with("https://"); } } @@ -146,6 +146,11 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA); + Error err = verify_headers(p_headers); + if (err) { + return err; + } + String uri = p_url; if (!ssl && http_proxy_port != -1) { uri = vformat("http://%s:%d%s", conn_host, conn_port, p_url); @@ -173,7 +178,7 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< } if (add_host) { if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) { - // Don't append the standard ports + // Don't append the standard ports. request += "Host: " + conn_host + "\r\n"; } else { request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n"; @@ -192,21 +197,12 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< request += "\r\n"; CharString cs = request.utf8(); - Vector<uint8_t> data; - data.resize(cs.length() + p_body_size); - memcpy(data.ptrw(), cs.get_data(), cs.length()); + request_buffer->clear(); + request_buffer->put_data((const uint8_t *)cs.get_data(), cs.length()); if (p_body_size > 0) { - memcpy(data.ptrw() + cs.length(), p_body, p_body_size); - } - - // TODO Implement non-blocking requests. - Error err = connection->put_data(data.ptr(), data.size()); - - if (err) { - close(); - status = STATUS_CONNECTION_ERROR; - return err; + request_buffer->put_data(p_body, p_body_size); } + request_buffer->seek(0); status = STATUS_REQUESTING; head_request = p_method == METHOD_HEAD; @@ -257,6 +253,7 @@ void HTTPClientTCP::close() { ip_candidates.clear(); response_headers.clear(); response_str.clear(); + request_buffer->clear(); body_size = -1; body_left = 0; chunk_left = 0; @@ -274,7 +271,7 @@ Error HTTPClientTCP::poll() { IP::ResolverStatus rstatus = IP::get_singleton()->get_resolve_item_status(resolving); switch (rstatus) { case IP::RESOLVER_STATUS_WAITING: - return OK; // Still resolving + return OK; // Still resolving. case IP::RESOLVER_STATUS_DONE: { ip_candidates = IP::get_singleton()->get_resolve_item_addresses(resolving); @@ -356,7 +353,7 @@ Error HTTPClientTCP::poll() { } else if (ssl) { Ref<StreamPeerSSL> ssl; if (!handshaking) { - // Connect the StreamPeerSSL and start handshaking + // Connect the StreamPeerSSL and start handshaking. ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); ssl->set_blocking_handshake_enabled(false); Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host); @@ -368,7 +365,7 @@ Error HTTPClientTCP::poll() { connection = ssl; handshaking = true; } else { - // We are already handshaking, which means we can use your already active SSL connection + // We are already handshaking, which means we can use your already active SSL connection. ssl = static_cast<Ref<StreamPeerSSL>>(connection); if (ssl.is_null()) { close(); @@ -376,22 +373,22 @@ Error HTTPClientTCP::poll() { return ERR_CANT_CONNECT; } - ssl->poll(); // Try to finish the handshake + ssl->poll(); // Try to finish the handshake. } if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) { - // Handshake has been successful + // Handshake has been successful. handshaking = false; ip_candidates.clear(); status = STATUS_CONNECTED; return OK; } else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) { - // Handshake has failed + // Handshake has failed. close(); status = STATUS_SSL_HANDSHAKE_ERROR; return ERR_CANT_CONNECT; } - // ... we will need to poll more for handshake to finish + // ... we will need to poll more for handshake to finish. } else { ip_candidates.clear(); status = STATUS_CONNECTED; @@ -416,7 +413,7 @@ Error HTTPClientTCP::poll() { } break; case STATUS_BODY: case STATUS_CONNECTED: { - // Check if we are still connected + // Check if we are still connected. if (ssl) { Ref<StreamPeerSSL> tmp = connection; tmp->poll(); @@ -428,10 +425,34 @@ Error HTTPClientTCP::poll() { status = STATUS_CONNECTION_ERROR; return ERR_CONNECTION_ERROR; } - // Connection established, requests can now be made + // Connection established, requests can now be made. return OK; } break; case STATUS_REQUESTING: { + if (request_buffer->get_available_bytes()) { + int avail = request_buffer->get_available_bytes(); + int pos = request_buffer->get_position(); + const Vector<uint8_t> data = request_buffer->get_data_array(); + int wrote = 0; + Error err; + if (blocking) { + err = connection->put_data(data.ptr() + pos, avail); + wrote += avail; + } else { + err = connection->put_partial_data(data.ptr() + pos, avail, wrote); + } + if (err != OK) { + close(); + status = STATUS_CONNECTION_ERROR; + return ERR_CONNECTION_ERROR; + } + pos += wrote; + request_buffer->seek(pos); + if (avail - wrote > 0) { + return OK; + } + request_buffer->clear(); + } while (true) { uint8_t byte; int rec = 0; @@ -534,7 +555,7 @@ Error HTTPClientTCP::poll() { return OK; } -int HTTPClientTCP::get_response_body_length() const { +int64_t HTTPClientTCP::get_response_body_length() const { return body_size; } @@ -547,7 +568,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { if (chunked) { while (true) { if (chunk_trailer_part) { - // We need to consume the trailer part too or keep-alive will break + // We need to consume the trailer part too or keep-alive will break. uint8_t b; int rec = 0; err = _get_http_data(&b, 1, rec); @@ -560,18 +581,18 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { int cs = chunk.size(); if ((cs >= 2 && chunk[cs - 2] == '\r' && chunk[cs - 1] == '\n')) { if (cs == 2) { - // Finally over + // Finally over. chunk_trailer_part = false; status = STATUS_CONNECTED; chunk.clear(); break; } else { - // We do not process nor return the trailer data + // We do not process nor return the trailer data. chunk.clear(); } } } else if (chunk_left == 0) { - // Reading length + // Reading length. uint8_t b; int rec = 0; err = _get_http_data(&b, 1, rec); @@ -593,7 +614,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { for (int i = 0; i < chunk.size() - 2; i++) { char c = chunk[i]; int v = 0; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a' + 10; @@ -658,7 +679,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { uint8_t *w = ret.ptrw(); err = _get_http_data(w + _offset, to_read, rec); } - if (rec <= 0) { // Ended up reading less + if (rec <= 0) { // Ended up reading less. ret.resize(_offset); break; } else { @@ -679,7 +700,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { close(); if (err == ERR_FILE_EOF) { - status = STATUS_DISCONNECTED; // Server disconnected + status = STATUS_DISCONNECTED; // Server disconnected. } else { status = STATUS_CONNECTION_ERROR; } @@ -759,6 +780,7 @@ void HTTPClientTCP::set_https_proxy(const String &p_host, int p_port) { HTTPClientTCP::HTTPClientTCP() { tcp_connection.instantiate(); + request_buffer.instantiate(); } HTTPClient *(*HTTPClient::_create)() = HTTPClientTCP::_create_func; diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h index 886ad0ef48..c10e0b1eca 100644 --- a/core/io/http_client_tcp.h +++ b/core/io/http_client_tcp.h @@ -38,13 +38,13 @@ private: Status status = STATUS_DISCONNECTED; IP::ResolverID resolving = IP::RESOLVER_INVALID_ID; Array ip_candidates; - int conn_port = -1; // Server to make requests to + int conn_port = -1; // Server to make requests to. String conn_host; - int server_port = -1; // Server to connect to (might be a proxy server) + int server_port = -1; // Server to connect to (might be a proxy server). String server_host; - int http_proxy_port = -1; // Proxy server for http requests + int http_proxy_port = -1; // Proxy server for http requests. String http_proxy_host; - int https_proxy_port = -1; // Proxy server for https requests + int https_proxy_port = -1; // Proxy server for https requests. String https_proxy_host; bool ssl = false; bool ssl_verify_host = false; @@ -62,9 +62,10 @@ private: int64_t body_left = 0; bool read_until_eof = false; + Ref<StreamPeerBuffer> request_buffer; Ref<StreamPeerTCP> tcp_connection; Ref<StreamPeer> connection; - Ref<HTTPClientTCP> proxy_client; // Negotiate with proxy server + Ref<HTTPClientTCP> proxy_client; // Negotiate with proxy server. int response_num = 0; Vector<String> response_headers; @@ -87,7 +88,7 @@ public: bool is_response_chunked() const override; int get_response_code() const override; Error get_response_headers(List<String> *r_response) override; - int get_response_body_length() const override; + int64_t get_response_body_length() const override; PackedByteArray read_response_body_chunk() override; void set_blocking_mode(bool p_enable) override; bool is_blocking_mode_enabled() const override; diff --git a/core/io/image.cpp b/core/io/image.cpp index 7956d0bad7..577fc59807 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -30,14 +30,17 @@ #include "image.h" +#include "core/error/error_list.h" #include "core/error/error_macros.h" #include "core/io/image_loader.h" #include "core/io/resource_loader.h" #include "core/math/math_funcs.h" #include "core/string/print_string.h" #include "core/templates/hash_map.h" +#include "core/variant/dictionary.h" #include <stdio.h> +#include <cmath> const char *Image::format_names[Image::FORMAT_MAX] = { "Lum8", //luminance @@ -1434,12 +1437,11 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int & } // Set mipmap size. - // It might be necessary to put this after the minimum mipmap size check because of the possible occurrence of "1 >> 1". if (r_mm_width) { - *r_mm_width = bw >> 1; + *r_mm_width = w; } if (r_mm_height) { - *r_mm_height = bh >> 1; + *r_mm_height = h; } // Reach target mipmap. @@ -2057,7 +2059,7 @@ void Image::create(const char **p_xpm) { for (int i = 0; i < 6; i++) { char v = line_ptr[i]; - if (v >= '0' && v <= '9') { + if (is_digit(v)) { v -= '0'; } else if (v >= 'A' && v <= 'F') { v = (v - 'A') + 10; @@ -3136,6 +3138,8 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("rgbe_to_srgb"), &Image::rgbe_to_srgb); ClassDB::bind_method(D_METHOD("bump_map_to_normal_map", "bump_scale"), &Image::bump_map_to_normal_map, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("compute_image_metrics", "compared_image", "use_luma"), &Image::compute_image_metrics); + ClassDB::bind_method(D_METHOD("blit_rect", "src", "src_rect", "dst"), &Image::blit_rect); ClassDB::bind_method(D_METHOD("blit_rect_mask", "src", "mask", "src_rect", "dst"), &Image::blit_rect_mask); ClassDB::bind_method(D_METHOD("blend_rect", "src", "src_rect", "dst"), &Image::blend_rect); @@ -3621,3 +3625,128 @@ Ref<Resource> Image::duplicate(bool p_subresources) const { void Image::set_as_black() { memset(data.ptrw(), 0, data.size()); } + +Dictionary Image::compute_image_metrics(const Ref<Image> p_compared_image, bool p_luma_metric) { + // https://github.com/richgel999/bc7enc_rdo/blob/master/LICENSE + // + // This is free and unencumbered software released into the public domain. + // Anyone is free to copy, modify, publish, use, compile, sell, or distribute this + // software, either in source code form or as a compiled binary, for any purpose, + // commercial or non - commercial, and by any means. + // In jurisdictions that recognize copyright laws, the author or authors of this + // software dedicate any and all copyright interest in the software to the public + // domain. We make this dedication for the benefit of the public at large and to + // the detriment of our heirs and successors. We intend this dedication to be an + // overt act of relinquishment in perpetuity of all present and future rights to + // this software under copyright law. + // 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 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. + + Dictionary result; + result["max"] = INFINITY; + result["mean"] = INFINITY; + result["mean_squared"] = INFINITY; + result["root_mean_squared"] = INFINITY; + result["peak_snr"] = 0.0f; + + ERR_FAIL_NULL_V(p_compared_image, result); + Error err = OK; + Ref<Image> compared_image = duplicate(true); + if (compared_image->is_compressed()) { + err = compared_image->decompress(); + } + ERR_FAIL_COND_V(err != OK, result); + Ref<Image> source_image = p_compared_image->duplicate(true); + if (source_image->is_compressed()) { + err = source_image->decompress(); + } + ERR_FAIL_COND_V(err != OK, result); + + ERR_FAIL_COND_V(err != OK, result); + + ERR_FAIL_COND_V_MSG((compared_image->get_format() >= Image::FORMAT_RH) && (compared_image->get_format() <= Image::FORMAT_RGBE9995), result, "Metrics on HDR images are not supported."); + ERR_FAIL_COND_V_MSG((source_image->get_format() >= Image::FORMAT_RH) && (source_image->get_format() <= Image::FORMAT_RGBE9995), result, "Metrics on HDR images are not supported."); + + double image_metric_max, image_metric_mean, image_metric_mean_squared, image_metric_root_mean_squared, image_metric_peak_snr = 0.0; + const bool average_component_error = true; + + const uint32_t width = MIN(compared_image->get_width(), source_image->get_width()); + const uint32_t height = MIN(compared_image->get_height(), source_image->get_height()); + + // Histogram approach originally due to Charles Bloom. + double hist[256]; + memset(hist, 0, sizeof(hist)); + + for (uint32_t y = 0; y < height; y++) { + for (uint32_t x = 0; x < width; x++) { + const Color color_a = compared_image->get_pixel(x, y); + + const Color color_b = source_image->get_pixel(x, y); + + if (!p_luma_metric) { + ERR_FAIL_COND_V_MSG(color_a.r > 1.0f, Dictionary(), "Can't compare HDR colors."); + ERR_FAIL_COND_V_MSG(color_b.r > 1.0f, Dictionary(), "Can't compare HDR colors."); + hist[Math::abs(color_a.get_r8() - color_b.get_r8())]++; + ERR_FAIL_COND_V_MSG(color_a.g > 1.0f, Dictionary(), "Can't compare HDR colors."); + ERR_FAIL_COND_V_MSG(color_b.g > 1.0f, Dictionary(), "Can't compare HDR colors."); + hist[Math::abs(color_a.get_g8() - color_b.get_g8())]++; + ERR_FAIL_COND_V_MSG(color_a.b > 1.0f, Dictionary(), "Can't compare HDR colors."); + ERR_FAIL_COND_V_MSG(color_b.b > 1.0f, Dictionary(), "Can't compare HDR colors."); + hist[Math::abs(color_a.get_b8() - color_b.get_b8())]++; + ERR_FAIL_COND_V_MSG(color_a.a > 1.0f, Dictionary(), "Can't compare HDR colors."); + ERR_FAIL_COND_V_MSG(color_b.a > 1.0f, Dictionary(), "Can't compare HDR colors."); + hist[Math::abs(color_a.get_a8() - color_b.get_a8())]++; + } else { + ERR_FAIL_COND_V_MSG(color_a.r > 1.0f, Dictionary(), "Can't compare HDR colors."); + ERR_FAIL_COND_V_MSG(color_b.r > 1.0f, Dictionary(), "Can't compare HDR colors."); + // REC709 weightings + int luma_a = (13938U * color_a.get_r8() + 46869U * color_a.get_g8() + 4729U * color_a.get_b8() + 32768U) >> 16U; + int luma_b = (13938U * color_b.get_r8() + 46869U * color_b.get_g8() + 4729U * color_b.get_b8() + 32768U) >> 16U; + hist[Math::abs(luma_a - luma_b)]++; + } + } + } + + image_metric_max = 0; + double sum = 0.0f, sum2 = 0.0f; + for (uint32_t i = 0; i < 256; i++) { + if (!hist[i]) { + continue; + } + + image_metric_max = MAX(image_metric_max, i); + + double x = i * hist[i]; + + sum += x; + sum2 += i * x; + } + + // See http://richg42.blogspot.com/2016/09/how-to-compute-psnr-from-old-berkeley.html + double total_values = width * height; + + if (average_component_error) { + total_values *= 4; + } + + image_metric_mean = CLAMP(sum / total_values, 0.0f, 255.0f); + image_metric_mean_squared = CLAMP(sum2 / total_values, 0.0f, 255.0f * 255.0f); + + image_metric_root_mean_squared = sqrt(image_metric_mean_squared); + + if (!image_metric_root_mean_squared) { + image_metric_peak_snr = 1e+10f; + } else { + image_metric_peak_snr = CLAMP(log10(255.0f / image_metric_root_mean_squared) * 20.0f, 0.0f, 500.0f); + } + result["max"] = image_metric_max; + result["mean"] = image_metric_mean; + result["mean_squared"] = image_metric_mean_squared; + result["root_mean_squared"] = image_metric_root_mean_squared; + result["peak_snr"] = image_metric_peak_snr; + return result; +} diff --git a/core/io/image.h b/core/io/image.h index ddfb2bb01d..53bfa0881f 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -399,6 +399,8 @@ public: mipmaps = p_image->mipmaps; data = p_image->data; } + + Dictionary compute_image_metrics(const Ref<Image> p_compared_image, bool p_luma_metric = true); }; VARIANT_ENUM_CAST(Image::Format) diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 4970afc1d3..8e0d47e762 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -98,6 +98,11 @@ struct _IP_ResolverPrivate { if (queue[i].status.get() != IP::RESOLVER_STATUS_WAITING) { continue; } + // We might be overriding another result, but we don't care as long as the result is valid. + if (response.size()) { + String key = get_cache_key(hostname, type); + cache[key] = response; + } queue[i].response = response; queue[i].status.set(response.is_empty() ? IP::RESOLVER_STATUS_ERROR : IP::RESOLVER_STATUS_DONE); } @@ -120,30 +125,8 @@ struct _IP_ResolverPrivate { }; IPAddress IP::resolve_hostname(const String &p_hostname, IP::Type p_type) { - List<IPAddress> res; - String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); - - resolver->mutex.lock(); - if (resolver->cache.has(key)) { - res = resolver->cache[key]; - } else { - // This should be run unlocked so the resolver thread can keep - // resolving other requests. - resolver->mutex.unlock(); - _resolve_hostname(res, p_hostname, p_type); - resolver->mutex.lock(); - // We might be overriding another result, but we don't care (they are the - // same hostname). - resolver->cache[key] = res; - } - resolver->mutex.unlock(); - - for (int i = 0; i < res.size(); ++i) { - if (res[i].is_valid()) { - return res[i]; - } - } - return IPAddress(); + const Array addresses = resolve_hostname_addresses(p_hostname, p_type); + return addresses.size() ? addresses[0].operator IPAddress() : IPAddress(); } Array IP::resolve_hostname_addresses(const String &p_hostname, Type p_type) { @@ -159,17 +142,16 @@ Array IP::resolve_hostname_addresses(const String &p_hostname, Type p_type) { resolver->mutex.unlock(); _resolve_hostname(res, p_hostname, p_type); resolver->mutex.lock(); - // We might be overriding another result, but we don't care (they are the - // same hostname). - resolver->cache[key] = res; + // We might be overriding another result, but we don't care as long as the result is valid. + if (res.size()) { + resolver->cache[key] = res; + } } resolver->mutex.unlock(); Array result; for (int i = 0; i < res.size(); ++i) { - if (res[i].is_valid()) { - result.push_back(String(res[i])); - } + result.push_back(String(res[i])); } return result; } diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp index 38f99a08a4..d183c60798 100644 --- a/core/io/ip_address.cpp +++ b/core/io/ip_address.cpp @@ -71,7 +71,7 @@ static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) { int n = 0; char32_t c = p_string[i]; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { n = c - '0'; } else if (c >= 'a' && c <= 'f') { n = 10 + (c - 'a'); @@ -113,7 +113,7 @@ void IPAddress::_parse_ipv6(const String &p_string) { } else if (c == '.') { part_ipv4 = true; - } else if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + } else if (is_hex_digit(c)) { if (!part_found) { parts[parts_idx++] = i; part_found = true; diff --git a/core/io/json.cpp b/core/io/json.cpp index 7b642f6a59..4b745dff44 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -229,12 +229,12 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to r_err_str = "Unterminated String"; return ERR_PARSE_ERROR; } - if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + if (!is_hex_digit(c)) { r_err_str = "Malformed hex constant in string"; return ERR_PARSE_ERROR; } char32_t v; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a'; @@ -265,12 +265,12 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to r_err_str = "Unterminated String"; return ERR_PARSE_ERROR; } - if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + if (!is_hex_digit(c)) { r_err_str = "Malformed hex constant in string"; return ERR_PARSE_ERROR; } char32_t v; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a'; @@ -326,7 +326,7 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to break; } - if (p_str[index] == '-' || (p_str[index] >= '0' && p_str[index] <= '9')) { + if (p_str[index] == '-' || is_digit(p_str[index])) { //a number const char32_t *rptr; double number = String::to_float(&p_str[index], &rptr); @@ -335,10 +335,10 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to r_token.value = number; return OK; - } else if ((p_str[index] >= 'A' && p_str[index] <= 'Z') || (p_str[index] >= 'a' && p_str[index] <= 'z')) { + } else if (is_ascii_char(p_str[index])) { String id; - while ((p_str[index] >= 'A' && p_str[index] <= 'Z') || (p_str[index] >= 'a' && p_str[index] <= 'z')) { + while (is_ascii_char(p_str[index])) { id += p_str[index]; index++; } diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 6a0668f027..a363cc3694 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -52,9 +52,8 @@ ObjectID EncodedObjectAsID::get_object_id() const { return id; } -#define _S(a) ((int32_t)a) -#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(_S(b) < 0 || _S(a) < 0 || _S(a) > INT_MAX - _S(b), err) -#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(_S(a) < 0 || _S(b) <= 0 || _S(a) > INT_MAX / _S(b), err) +#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(b)) < 0 || ((int32_t)(a)) < 0 || ((int32_t)(a)) > INT_MAX - ((int32_t)(b)), err) +#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(a)) < 0 || ((int32_t)(b)) <= 0 || ((int32_t)(a)) > INT_MAX / ((int32_t)(b)), err) #define ENCODE_MASK 0xFF #define ENCODE_FLAG_64 1 << 16 @@ -1031,7 +1030,7 @@ static void _encode_string(const String &p_string, uint8_t *&buf, int &r_len) { } Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects, int p_depth) { - ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ERR_OUT_OF_MEMORY, "Potential inifite recursion detected. Bailing."); + ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ERR_OUT_OF_MEMORY, "Potential infinite recursion detected. Bailing."); uint8_t *buf = r_buffer; r_len = 0; diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index e90d1695e5..0af236f766 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -39,7 +39,7 @@ void PacketPeer::set_encode_buffer_max_size(int p_max_size) { ERR_FAIL_COND_MSG(p_max_size < 1024, "Max encode buffer must be at least 1024 bytes"); ERR_FAIL_COND_MSG(p_max_size > 256 * 1024 * 1024, "Max encode buffer cannot exceed 256 MiB"); encode_buffer_max_size = next_power_of_2(p_max_size); - encode_buffer.resize(0); + encode_buffer.clear(); } int PacketPeer::get_encode_buffer_max_size() const { diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 221a680130..272ace3438 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -62,7 +62,7 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String & int v = 0; if (i * 2 < _key.length()) { char32_t ct = _key[i * 2]; - if (ct >= '0' && ct <= '9') { + if (is_digit(ct)) { ct = ct - '0'; } else if (ct >= 'a' && ct <= 'f') { ct = 10 + ct - 'a'; @@ -72,7 +72,7 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String & if (i * 2 + 1 < _key.length()) { char32_t ct = _key[i * 2 + 1]; - if (ct >= '0' && ct <= '9') { + if (is_digit(ct)) { ct = ct - '0'; } else if (ct >= 'a' && ct <= 'f') { ct = 10 + ct - 'a'; diff --git a/core/io/resource.h b/core/io/resource.h index a0800c57cb..b1e1c15541 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -37,6 +37,8 @@ #include "core/templates/safe_refcount.h" #include "core/templates/self_list.h" +class Node; + #define RES_BASE_EXTENSION(m_ext) \ public: \ static void register_custom_data_to_otdb() { ClassDB::add_resource_base_extension(m_ext, get_class_static()); } \ @@ -103,7 +105,7 @@ public: virtual void set_path(const String &p_path, bool p_take_over = false); String get_path() const; - _FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.find("::") != -1 || path_cache.begins_with("local://"); } + _FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.contains("::") || path_cache.begins_with("local://"); } static String generate_scene_unique_id(); void set_scene_unique_id(const String &p_id); diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 200d5fafde..8588bab0be 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -335,7 +335,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { String exttype = get_unicode_string(); String path = get_unicode_string(); - if (path.find("://") == -1 && path.is_relative_path()) { + if (!path.contains("://") && path.is_relative_path()) { // path is relative to file being loaded, so convert to a resource path path = ProjectSettings::get_singleton()->localize_path(res_path.get_base_dir().plus_file(path)); } @@ -626,7 +626,7 @@ Error ResourceLoaderBinary::load() { path = remaps[path]; } - if (path.find("://") == -1 && path.is_relative_path()) { + if (!path.contains("://") && path.is_relative_path()) { // path is relative to file being loaded, so convert to a resource path path = ProjectSettings::get_singleton()->localize_path(path.get_base_dir().plus_file(external_resources[i].path)); } diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index 470fb2d42d..9b6440e2a2 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -463,3 +463,12 @@ void ResourceImporter::_bind_methods() { BIND_ENUM_CONSTANT(IMPORT_ORDER_DEFAULT); BIND_ENUM_CONSTANT(IMPORT_ORDER_SCENE); } + +void ResourceFormatImporter::add_importer(const Ref<ResourceImporter> &p_importer, bool p_first_priority) { + ERR_FAIL_COND(p_importer.is_null()); + if (p_first_priority) { + importers.insert(0, p_importer); + } else { + importers.push_back(p_importer); + } +} diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index 261afbab69..2fffc16ad8 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -80,9 +80,8 @@ public: String get_internal_resource_path(const String &p_path) const; void get_internal_resource_path_list(const String &p_path, List<String> *r_paths); - void add_importer(const Ref<ResourceImporter> &p_importer) { - importers.push_back(p_importer); - } + void add_importer(const Ref<ResourceImporter> &p_importer, bool p_first_priority = false); + void remove_importer(const Ref<ResourceImporter> &p_importer) { importers.erase(p_importer); } Ref<ResourceImporter> get_importer_by_name(const String &p_name) const; Ref<ResourceImporter> get_importer_by_extension(const String &p_extension) const; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 3e1c9d2e4a..21bf566b1b 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -820,7 +820,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem } String l = res_remaps[i].substr(split + 1).strip_edges(); int score = TranslationServer::get_singleton()->compare_locales(locale, l); - if (score > best_score) { + if (score > 0 && score >= best_score) { new_path = res_remaps[i].left(split); best_score = score; if (score == 10) { diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp index 1a16d5b47a..776756e64e 100644 --- a/core/io/resource_uid.cpp +++ b/core/io/resource_uid.cpp @@ -71,9 +71,9 @@ ResourceUID::ID ResourceUID::text_to_id(const String &p_text) const { for (uint32_t i = 6; i < l; i++) { uid *= base; uint32_t c = p_text[i]; - if (c >= 'a' && c <= 'z') { + if (is_ascii_lower_case(c)) { uid += c - 'a'; - } else if (c >= '0' && c <= '9') { + } else if (is_digit(c)) { uid += c - '0' + char_count; } else { return INVALID_ID; diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 28ebe811c9..c65968ef03 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -98,7 +98,7 @@ Array StreamPeer::_get_partial_data(int p_bytes) { Error err = get_partial_data(&w[0], p_bytes, received); if (err != OK) { - data.resize(0); + data.clear(); } else if (received != data.size()) { data.resize(received); } @@ -563,7 +563,7 @@ Vector<uint8_t> StreamPeerBuffer::get_data_array() const { } void StreamPeerBuffer::clear() { - data.resize(0); + data.clear(); pointer = 0; } diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 2f6742bd7a..8d3e58cad1 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -179,7 +179,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { } if (l.is_empty() || l.begins_with("#")) { - if (l.find("fuzzy") != -1) { + if (l.contains("fuzzy")) { skip_next = true; } line++; diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index ce2435216b..14057b96be 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -32,7 +32,6 @@ #include "core/math/geometry_3d.h" #include "core/object/script_language.h" -#include "scene/scene_string_names.h" int AStar::get_available_point_id() const { if (points.has(last_free_id)) { diff --git a/core/math/aabb.h b/core/math/aabb.h index 3d19410ddf..cb6f05e9ea 100644 --- a/core/math/aabb.h +++ b/core/math/aabb.h @@ -36,13 +36,13 @@ #include "core/math/vector3.h" /** - * AABB / AABB (Axis Aligned Bounding Box) - * This is implemented by a point (position) and the box size + * AABB (Axis Aligned Bounding Box) + * This is implemented by a point (position) and the box size. */ + class Variant; -class _NO_DISCARD_ AABB { -public: +struct _NO_DISCARD_ AABB { Vector3 position; Vector3 size; diff --git a/core/math/audio_frame.h b/core/math/audio_frame.h index 94fc3de2b1..8b244e9fe4 100644 --- a/core/math/audio_frame.h +++ b/core/math/audio_frame.h @@ -140,4 +140,16 @@ struct AudioFrame { _ALWAYS_INLINE_ AudioFrame() {} }; +_ALWAYS_INLINE_ AudioFrame operator*(float p_scalar, const AudioFrame &p_frame) { + return AudioFrame(p_frame.l * p_scalar, p_frame.r * p_scalar); +} + +_ALWAYS_INLINE_ AudioFrame operator*(int32_t p_scalar, const AudioFrame &p_frame) { + return AudioFrame(p_frame.l * p_scalar, p_frame.r * p_scalar); +} + +_ALWAYS_INLINE_ AudioFrame operator*(int64_t p_scalar, const AudioFrame &p_frame) { + return AudioFrame(p_frame.l * p_scalar, p_frame.r * p_scalar); +} + #endif // AUDIO_FRAME_H diff --git a/core/math/basis.h b/core/math/basis.h index 802da82089..683f05150c 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -34,11 +34,7 @@ #include "core/math/quaternion.h" #include "core/math/vector3.h" -class _NO_DISCARD_ Basis { -private: - void _set_diagonal(const Vector3 &p_diag); - -public: +struct _NO_DISCARD_ Basis { Vector3 elements[3] = { Vector3(1, 0, 0), Vector3(0, 1, 0), @@ -263,6 +259,10 @@ public: } _FORCE_INLINE_ Basis() {} + +private: + // Helper method. + void _set_diagonal(const Vector3 &p_diag); }; _FORCE_INLINE_ void Basis::operator*=(const Basis &p_matrix) { @@ -334,4 +334,5 @@ real_t Basis::determinant() const { elements[1][0] * (elements[0][1] * elements[2][2] - elements[2][1] * elements[0][2]) + elements[2][0] * (elements[0][1] * elements[1][2] - elements[1][1] * elements[0][2]); } + #endif // BASIS_H diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp index 2902ca59b9..f5d746ef0f 100644 --- a/core/math/camera_matrix.cpp +++ b/core/math/camera_matrix.cpp @@ -30,7 +30,11 @@ #include "camera_matrix.h" +#include "core/math/aabb.h" #include "core/math/math_funcs.h" +#include "core/math/plane.h" +#include "core/math/rect2.h" +#include "core/math/transform_3d.h" #include "core/string/print_string.h" float CameraMatrix::determinant() const { diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h index da1aba7562..285d2ae384 100644 --- a/core/math/camera_matrix.h +++ b/core/math/camera_matrix.h @@ -31,8 +31,14 @@ #ifndef CAMERA_MATRIX_H #define CAMERA_MATRIX_H -#include "core/math/rect2.h" -#include "core/math/transform_3d.h" +#include "core/math/math_defs.h" +#include "core/math/vector3.h" + +struct AABB; +struct Plane; +struct Rect2; +struct Transform3D; +struct Vector2; struct CameraMatrix { enum Planes { diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp index 912ffb8b16..bd292f4c2a 100644 --- a/core/math/convex_hull.cpp +++ b/core/math/convex_hull.cpp @@ -2129,7 +2129,7 @@ bool ConvexHullInternal::shift_face(Face *p_face, real_t p_amount, LocalVector<V printf("Needed %d iterations to remove part\n", n); #endif - p_stack.resize(0); + p_stack.clear(); p_face->origin = shifted_origin; return true; @@ -2167,9 +2167,9 @@ real_t ConvexHullComputer::compute(const Vector3 *p_coords, int32_t p_count, rea return shift; } - vertices.resize(0); - edges.resize(0); - faces.resize(0); + vertices.clear(); + edges.clear(); + faces.clear(); LocalVector<ConvexHullInternal::Vertex *> old_vertices; get_vertex_copy(hull.vertex_list, old_vertices); diff --git a/core/math/delaunay_2d.h b/core/math/delaunay_2d.h index 08f5df8472..c39997d6a9 100644 --- a/core/math/delaunay_2d.h +++ b/core/math/delaunay_2d.h @@ -32,6 +32,7 @@ #define DELAUNAY_2D_H #include "core/math/rect2.h" +#include "core/templates/vector.h" class Delaunay2D { public: diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h index 3041cdf268..50ec2c2b30 100644 --- a/core/math/dynamic_bvh.h +++ b/core/math/dynamic_bvh.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef DYNAMICBVH_H -#define DYNAMICBVH_H +#ifndef DYNAMIC_BVH_H +#define DYNAMIC_BVH_H #include "core/math/aabb.h" #include "core/templates/list.h" @@ -474,4 +474,4 @@ void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResu } while (depth > 0); } -#endif // DYNAMICBVH_H +#endif // DYNAMIC_BVH_H diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 4f8e79038f..0ddac9744e 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -37,10 +37,6 @@ #include "core/os/os.h" #include "core/variant/variant_parser.h" -static bool _is_number(char32_t c) { - return (c >= '0' && c <= '9'); -} - Error Expression::_get_token(Token &r_token) { while (true) { #define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++]) @@ -88,7 +84,7 @@ Error Expression::_get_token(Token &r_token) { r_token.type = TK_INPUT; int index = 0; do { - if (!_is_number(expression[str_ofs])) { + if (!is_digit(expression[str_ofs])) { _set_error("Expected number after '$'"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; @@ -97,7 +93,7 @@ Error Expression::_get_token(Token &r_token) { index += expression[str_ofs] - '0'; str_ofs++; - } while (_is_number(expression[str_ofs])); + } while (is_digit(expression[str_ofs])); r_token.value = index; return OK; @@ -197,6 +193,7 @@ Error Expression::_get_token(Token &r_token) { case '\'': case '"': { String str; + char32_t prev = 0; while (true) { char32_t ch = GET_CHAR(); @@ -234,9 +231,11 @@ Error Expression::_get_token(Token &r_token) { case 'r': res = 13; break; + case 'U': case 'u': { - // hex number - for (int j = 0; j < 4; j++) { + // Hexadecimal sequence. + int hex_len = (next == 'U') ? 6 : 4; + for (int j = 0; j < hex_len; j++) { char32_t c = GET_CHAR(); if (c == 0) { @@ -244,13 +243,13 @@ Error Expression::_get_token(Token &r_token) { r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - if (!(_is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + if (!is_hex_digit(c)) { _set_error("Malformed hex constant in string"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } char32_t v; - if (_is_number(c)) { + if (is_digit(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a'; @@ -273,12 +272,46 @@ Error Expression::_get_token(Token &r_token) { } break; } + // Parse UTF-16 pair. + if ((res & 0xfffffc00) == 0xd800) { + if (prev == 0) { + prev = res; + continue; + } else { + _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + } else if ((res & 0xfffffc00) == 0xdc00) { + if (prev == 0) { + _set_error("Invalid UTF-16 sequence in string, unpaired trail surrogate"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } else { + res = (prev << 10UL) + res - ((0xd800 << 10UL) + 0xdc00 - 0x10000); + prev = 0; + } + } + if (prev != 0) { + _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } str += res; - } else { + if (prev != 0) { + _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } str += ch; } } + if (prev != 0) { + _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } r_token.type = TK_CONSTANT; r_token.value = str; @@ -291,39 +324,67 @@ Error Expression::_get_token(Token &r_token) { } char32_t next_char = (str_ofs >= expression.length()) ? 0 : expression[str_ofs]; - if (_is_number(cchar) || (cchar == '.' && _is_number(next_char))) { + if (is_digit(cchar) || (cchar == '.' && is_digit(next_char))) { //a number String num; #define READING_SIGN 0 #define READING_INT 1 -#define READING_DEC 2 -#define READING_EXP 3 -#define READING_DONE 4 +#define READING_HEX 2 +#define READING_BIN 3 +#define READING_DEC 4 +#define READING_EXP 5 +#define READING_DONE 6 int reading = READING_INT; char32_t c = cchar; bool exp_sign = false; bool exp_beg = false; + bool bin_beg = false; + bool hex_beg = false; bool is_float = false; + bool is_first_char = true; while (true) { switch (reading) { case READING_INT: { - if (_is_number(c)) { - //pass + if (is_digit(c)) { + if (is_first_char && c == '0') { + if (next_char == 'b') { + reading = READING_BIN; + } else if (next_char == 'x') { + reading = READING_HEX; + } + } } else if (c == '.') { reading = READING_DEC; is_float = true; } else if (c == 'e') { reading = READING_EXP; + is_float = true; } else { reading = READING_DONE; } } break; + case READING_BIN: { + if (bin_beg && !is_binary_digit(c)) { + reading = READING_DONE; + } else if (c == 'b') { + bin_beg = true; + } + + } break; + case READING_HEX: { + if (hex_beg && !is_hex_digit(c)) { + reading = READING_DONE; + } else if (c == 'x') { + hex_beg = true; + } + + } break; case READING_DEC: { - if (_is_number(c)) { + if (is_digit(c)) { } else if (c == 'e') { reading = READING_EXP; @@ -333,13 +394,10 @@ Error Expression::_get_token(Token &r_token) { } break; case READING_EXP: { - if (_is_number(c)) { + if (is_digit(c)) { exp_beg = true; } else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) { - if (c == '-') { - is_float = true; - } exp_sign = true; } else { @@ -353,6 +411,7 @@ Error Expression::_get_token(Token &r_token) { } num += String::chr(c); c = GET_CHAR(); + is_first_char = false; } str_ofs--; @@ -361,16 +420,20 @@ Error Expression::_get_token(Token &r_token) { if (is_float) { r_token.value = num.to_float(); + } else if (bin_beg) { + r_token.value = num.bin_to_int(); + } else if (hex_beg) { + r_token.value = num.hex_to_int(); } else { r_token.value = num.to_int(); } return OK; - } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') { + } else if (is_ascii_char(cchar) || is_underscore(cchar)) { String id; bool first = true; - while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && _is_number(cchar))) { + while (is_ascii_char(cchar) || is_underscore(cchar) || (!first && is_digit(cchar))) { id += String::chr(cchar); cchar = GET_CHAR(); first = false; diff --git a/core/math/face3.cpp b/core/math/face3.cpp index ba10b50465..d588f34e5d 100644 --- a/core/math/face3.cpp +++ b/core/math/face3.cpp @@ -260,8 +260,8 @@ void Face3::project_range(const Vector3 &p_normal, const Transform3D &p_transfor } void Face3::get_support(const Vector3 &p_normal, const Transform3D &p_transform, Vector3 *p_vertices, int *p_count, int p_max) const { -#define _FACE_IS_VALID_SUPPORT_THRESHOLD 0.98 -#define _EDGE_IS_VALID_SUPPORT_THRESHOLD 0.05 + constexpr double face_support_threshold = 0.98; + constexpr double edge_support_threshold = 0.05; if (p_max <= 0) { return; @@ -270,7 +270,7 @@ void Face3::get_support(const Vector3 &p_normal, const Transform3D &p_transform, Vector3 n = p_transform.basis.xform_inv(p_normal); /** TEST FACE AS SUPPORT **/ - if (get_plane().normal.dot(n) > _FACE_IS_VALID_SUPPORT_THRESHOLD) { + if (get_plane().normal.dot(n) > face_support_threshold) { *p_count = MIN(3, p_max); for (int i = 0; i < *p_count; i++) { @@ -304,7 +304,7 @@ void Face3::get_support(const Vector3 &p_normal, const Transform3D &p_transform, // check if edge is valid as a support real_t dot = (vertex[i] - vertex[(i + 1) % 3]).normalized().dot(n); dot = ABS(dot); - if (dot < _EDGE_IS_VALID_SUPPORT_THRESHOLD) { + if (dot < edge_support_threshold) { *p_count = MIN(2, p_max); for (int j = 0; j < *p_count; j++) { diff --git a/core/math/face3.h b/core/math/face3.h index 3dbbca09e0..8b123f078c 100644 --- a/core/math/face3.h +++ b/core/math/face3.h @@ -36,8 +36,7 @@ #include "core/math/transform_3d.h" #include "core/math/vector3.h" -class _NO_DISCARD_ Face3 { -public: +struct _NO_DISCARD_ Face3 { enum Side { SIDE_OVER, SIDE_UNDER, @@ -48,14 +47,11 @@ public: Vector3 vertex[3]; /** - * * @param p_plane plane used to split the face * @param p_res array of at least 3 faces, amount used in function return * @param p_is_point_over array of at least 3 booleans, determining which face is over the plane, amount used in function return - * @param _epsilon constant used for numerical error rounding, to add "thickness" to the plane (so coplanar points can happen) * @return amount of faces generated by the split, either 0 (means no split possible), 2 or 3 */ - int split_by_plane(const Plane &p_plane, Face3 *p_res, bool *p_is_point_over) const; Plane get_plane(ClockDirection p_dir = CLOCKWISE) const; diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h index 7385dba438..a2881d5f60 100644 --- a/core/math/geometry_2d.h +++ b/core/math/geometry_2d.h @@ -32,7 +32,11 @@ #define GEOMETRY_2D_H #include "core/math/delaunay_2d.h" +#include "core/math/math_funcs.h" #include "core/math/triangulate.h" +#include "core/math/vector2.h" +#include "core/math/vector2i.h" +#include "core/math/vector3.h" #include "core/math/vector3i.h" #include "core/templates/vector.h" diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp index 98a2c27d93..a9ff46410e 100644 --- a/core/math/geometry_3d.cpp +++ b/core/math/geometry_3d.cpp @@ -281,16 +281,16 @@ static inline void _plot_face(uint8_t ***p_cell_status, int x, int y, int z, int int div_y = len_y > 1 ? 2 : 1; int div_z = len_z > 1 ? 2 : 1; -#define _SPLIT(m_i, m_div, m_v, m_len_v, m_new_v, m_new_len_v) \ - if (m_div == 1) { \ - m_new_v = m_v; \ - m_new_len_v = 1; \ - } else if (m_i == 0) { \ - m_new_v = m_v; \ - m_new_len_v = m_len_v / 2; \ - } else { \ - m_new_v = m_v + m_len_v / 2; \ - m_new_len_v = m_len_v - m_len_v / 2; \ +#define SPLIT_DIV(m_i, m_div, m_v, m_len_v, m_new_v, m_new_len_v) \ + if (m_div == 1) { \ + m_new_v = m_v; \ + m_new_len_v = 1; \ + } else if (m_i == 0) { \ + m_new_v = m_v; \ + m_new_len_v = m_len_v / 2; \ + } else { \ + m_new_v = m_v + m_len_v / 2; \ + m_new_len_v = m_len_v - m_len_v / 2; \ } int new_x; @@ -301,18 +301,20 @@ static inline void _plot_face(uint8_t ***p_cell_status, int x, int y, int z, int int new_len_z; for (int i = 0; i < div_x; i++) { - _SPLIT(i, div_x, x, len_x, new_x, new_len_x); + SPLIT_DIV(i, div_x, x, len_x, new_x, new_len_x); for (int j = 0; j < div_y; j++) { - _SPLIT(j, div_y, y, len_y, new_y, new_len_y); + SPLIT_DIV(j, div_y, y, len_y, new_y, new_len_y); for (int k = 0; k < div_z; k++) { - _SPLIT(k, div_z, z, len_z, new_z, new_len_z); + SPLIT_DIV(k, div_z, z, len_z, new_z, new_len_z); _plot_face(p_cell_status, new_x, new_y, new_z, new_len_x, new_len_y, new_len_z, voxelsize, p_face); } } } + +#undef SPLIT_DIV } static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z, int len_x, int len_y, int len_z) { @@ -491,11 +493,10 @@ static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, i } Vector<Face3> Geometry3D::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { -#define _MIN_SIZE 1.0 -#define _MAX_LENGTH 20 - int face_count = p_array.size(); const Face3 *faces = p_array.ptr(); + constexpr double min_size = 1.0; + constexpr int max_length = 20; AABB global_aabb; @@ -512,22 +513,22 @@ Vector<Face3> Geometry3D::wrap_geometry(Vector<Face3> p_array, real_t *p_error) // Determine amount of cells in grid axis. int div_x, div_y, div_z; - if (global_aabb.size.x / _MIN_SIZE < _MAX_LENGTH) { - div_x = (int)(global_aabb.size.x / _MIN_SIZE) + 1; + if (global_aabb.size.x / min_size < max_length) { + div_x = (int)(global_aabb.size.x / min_size) + 1; } else { - div_x = _MAX_LENGTH; + div_x = max_length; } - if (global_aabb.size.y / _MIN_SIZE < _MAX_LENGTH) { - div_y = (int)(global_aabb.size.y / _MIN_SIZE) + 1; + if (global_aabb.size.y / min_size < max_length) { + div_y = (int)(global_aabb.size.y / min_size) + 1; } else { - div_y = _MAX_LENGTH; + div_y = max_length; } - if (global_aabb.size.z / _MIN_SIZE < _MAX_LENGTH) { - div_z = (int)(global_aabb.size.z / _MIN_SIZE) + 1; + if (global_aabb.size.z / min_size < max_length) { + div_z = (int)(global_aabb.size.z / min_size) + 1; } else { - div_z = _MAX_LENGTH; + div_z = max_length; } Vector3 voxelsize = global_aabb.size; diff --git a/core/math/plane.h b/core/math/plane.h index 8cb6f62b3b..66c1741662 100644 --- a/core/math/plane.h +++ b/core/math/plane.h @@ -35,13 +35,12 @@ class Variant; -class _NO_DISCARD_ Plane { -public: +struct _NO_DISCARD_ Plane { Vector3 normal; real_t d = 0; void set_normal(const Vector3 &p_normal); - _FORCE_INLINE_ Vector3 get_normal() const { return normal; }; ///Point is coplanar, CMP_EPSILON for precision + _FORCE_INLINE_ Vector3 get_normal() const { return normal; }; void normalize(); Plane normalized() const; diff --git a/core/math/quaternion.h b/core/math/quaternion.h index 2575d7d229..7874e4f428 100644 --- a/core/math/quaternion.h +++ b/core/math/quaternion.h @@ -36,8 +36,7 @@ #include "core/math/vector3.h" #include "core/string/ustring.h" -class _NO_DISCARD_ Quaternion { -public: +struct _NO_DISCARD_ Quaternion { union { struct { real_t x; diff --git a/core/math/rect2.cpp b/core/math/rect2.cpp index 9047c19434..d6e20bdc3c 100644 --- a/core/math/rect2.cpp +++ b/core/math/rect2.cpp @@ -28,7 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/math/transform_2d.h" // Includes rect2.h but Rect2 needs Transform2D +#include "rect2.h" + +#include "core/math/rect2i.h" +#include "core/math/transform_2d.h" +#include "core/string/ustring.h" bool Rect2::is_equal_approx(const Rect2 &p_rect) const { return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size); @@ -278,6 +282,6 @@ Rect2::operator String() const { return "[P: " + position.operator String() + ", S: " + size + "]"; } -Rect2i::operator String() const { - return "[P: " + position.operator String() + ", S: " + size + "]"; +Rect2::operator Rect2i() const { + return Rect2i(position, size); } diff --git a/core/math/rect2.h b/core/math/rect2.h index 4ea24e8f88..6ecc02336c 100644 --- a/core/math/rect2.h +++ b/core/math/rect2.h @@ -31,8 +31,11 @@ #ifndef RECT2_H #define RECT2_H -#include "core/math/vector2.h" // also includes math_funcs and ustring +#include "core/error/error_macros.h" +#include "core/math/vector2.h" +class String; +struct Rect2i; struct Transform2D; struct _NO_DISCARD_ Rect2 { @@ -179,6 +182,7 @@ struct _NO_DISCARD_ Rect2 { return new_rect; } + inline bool has_point(const Point2 &p_point) const { #ifdef MATH_CHECKS if (unlikely(size.x < 0 || size.y < 0)) { @@ -201,6 +205,7 @@ struct _NO_DISCARD_ Rect2 { return true; } + bool is_equal_approx(const Rect2 &p_rect) const; bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; } @@ -351,6 +356,7 @@ struct _NO_DISCARD_ Rect2 { } operator String() const; + operator Rect2i() const; Rect2() {} Rect2(real_t p_x, real_t p_y, real_t p_width, real_t p_height) : @@ -363,214 +369,4 @@ struct _NO_DISCARD_ Rect2 { } }; -struct _NO_DISCARD_ Rect2i { - Point2i position; - Size2i size; - - const Point2i &get_position() const { return position; } - void set_position(const Point2i &p_position) { position = p_position; } - const Size2i &get_size() const { return size; } - void set_size(const Size2i &p_size) { size = p_size; } - - 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 { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { - ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); - } -#endif - if (position.x > (p_rect.position.x + p_rect.size.width)) { - return false; - } - if ((position.x + size.width) < p_rect.position.x) { - return false; - } - if (position.y > (p_rect.position.y + p_rect.size.height)) { - return false; - } - if ((position.y + size.height) < p_rect.position.y) { - return false; - } - - return true; - } - - inline bool encloses(const Rect2i &p_rect) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { - ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); - } -#endif - return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && - ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && - ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); - } - - _FORCE_INLINE_ bool has_no_area() const { - return (size.x <= 0 || size.y <= 0); - } - - // Returns the instersection between two Rect2is or an empty Rect2i if there is no intersection - inline Rect2i intersection(const Rect2i &p_rect) const { - Rect2i new_rect = p_rect; - - if (!intersects(new_rect)) { - return Rect2i(); - } - - new_rect.position.x = MAX(p_rect.position.x, position.x); - new_rect.position.y = MAX(p_rect.position.y, position.y); - - Point2i p_rect_end = p_rect.position + p_rect.size; - Point2i end = position + size; - - new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x; - new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y; - - return new_rect; - } - - inline Rect2i merge(const Rect2i &p_rect) const { ///< return a merged rect -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { - ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); - } -#endif - Rect2i new_rect; - - new_rect.position.x = MIN(p_rect.position.x, position.x); - new_rect.position.y = MIN(p_rect.position.y, position.y); - - new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); - new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); - - new_rect.size = new_rect.size - new_rect.position; //make relative again - - return new_rect; - } - bool has_point(const Point2i &p_point) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0)) { - ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); - } -#endif - if (p_point.x < position.x) { - return false; - } - if (p_point.y < position.y) { - return false; - } - - if (p_point.x >= (position.x + size.x)) { - return false; - } - if (p_point.y >= (position.y + size.y)) { - return false; - } - - return true; - } - - bool operator==(const Rect2i &p_rect) const { return position == p_rect.position && size == p_rect.size; } - bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; } - - Rect2i grow(int p_amount) const { - Rect2i g = *this; - g.position.x -= p_amount; - g.position.y -= p_amount; - g.size.width += p_amount * 2; - g.size.height += p_amount * 2; - return g; - } - - inline Rect2i grow_side(Side p_side, int p_amount) const { - Rect2i g = *this; - g = g.grow_individual((SIDE_LEFT == p_side) ? p_amount : 0, - (SIDE_TOP == p_side) ? p_amount : 0, - (SIDE_RIGHT == p_side) ? p_amount : 0, - (SIDE_BOTTOM == p_side) ? p_amount : 0); - return g; - } - - inline Rect2i grow_side_bind(uint32_t p_side, int p_amount) const { - return grow_side(Side(p_side), p_amount); - } - - inline Rect2i grow_individual(int p_left, int p_top, int p_right, int p_bottom) const { - Rect2i g = *this; - g.position.x -= p_left; - g.position.y -= p_top; - g.size.width += p_left + p_right; - g.size.height += p_top + p_bottom; - - return g; - } - - _FORCE_INLINE_ Rect2i expand(const Vector2i &p_vector) const { - Rect2i r = *this; - r.expand_to(p_vector); - return r; - } - - inline void expand_to(const Point2i &p_vector) { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0)) { - ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); - } -#endif - Point2i begin = position; - Point2i end = position + size; - - if (p_vector.x < begin.x) { - begin.x = p_vector.x; - } - if (p_vector.y < begin.y) { - begin.y = p_vector.y; - } - - if (p_vector.x > end.x) { - end.x = p_vector.x; - } - if (p_vector.y > end.y) { - end.y = p_vector.y; - } - - position = begin; - size = end - begin; - } - - _FORCE_INLINE_ Rect2i abs() const { - return Rect2i(Point2i(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs()); - } - - _FORCE_INLINE_ void set_end(const Vector2i &p_end) { - size = p_end - position; - } - - _FORCE_INLINE_ Vector2i get_end() const { - return position + size; - } - - operator String() const; - - operator Rect2() const { return Rect2(position, size); } - - Rect2i() {} - Rect2i(const Rect2 &p_r2) : - position(p_r2.position), - size(p_r2.size) { - } - Rect2i(int p_x, int p_y, int p_width, int p_height) : - position(Point2i(p_x, p_y)), - size(Size2i(p_width, p_height)) { - } - Rect2i(const Point2i &p_pos, const Size2i &p_size) : - position(p_pos), - size(p_size) { - } -}; - #endif // RECT2_H diff --git a/core/math/rect2i.cpp b/core/math/rect2i.cpp new file mode 100644 index 0000000000..0782c450d0 --- /dev/null +++ b/core/math/rect2i.cpp @@ -0,0 +1,42 @@ +/*************************************************************************/ +/* rect2i.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 "rect2i.h" + +#include "core/math/rect2.h" +#include "core/string/ustring.h" + +Rect2i::operator String() const { + return "[P: " + position.operator String() + ", S: " + size + "]"; +} + +Rect2i::operator Rect2() const { + return Rect2(position, size); +} diff --git a/core/math/rect2i.h b/core/math/rect2i.h new file mode 100644 index 0000000000..db1459a3e6 --- /dev/null +++ b/core/math/rect2i.h @@ -0,0 +1,245 @@ +/*************************************************************************/ +/* rect2i.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 RECT2I_H +#define RECT2I_H + +#include "core/error/error_macros.h" +#include "core/math/vector2i.h" + +class String; +struct Rect2; + +struct _NO_DISCARD_ Rect2i { + Point2i position; + Size2i size; + + const Point2i &get_position() const { return position; } + void set_position(const Point2i &p_position) { position = p_position; } + const Size2i &get_size() const { return size; } + void set_size(const Size2i &p_size) { size = p_size; } + + 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 { +#ifdef MATH_CHECKS + if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { + ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); + } +#endif + if (position.x >= (p_rect.position.x + p_rect.size.width)) { + return false; + } + if ((position.x + size.width) <= p_rect.position.x) { + return false; + } + if (position.y >= (p_rect.position.y + p_rect.size.height)) { + return false; + } + if ((position.y + size.height) <= p_rect.position.y) { + return false; + } + + return true; + } + + inline bool encloses(const Rect2i &p_rect) const { +#ifdef MATH_CHECKS + if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { + ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); + } +#endif + return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && + ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) && + ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y)); + } + + _FORCE_INLINE_ bool has_no_area() const { + return (size.x <= 0 || size.y <= 0); + } + + // Returns the instersection between two Rect2is or an empty Rect2i if there is no intersection + inline Rect2i intersection(const Rect2i &p_rect) const { + Rect2i new_rect = p_rect; + + if (!intersects(new_rect)) { + return Rect2i(); + } + + new_rect.position.x = MAX(p_rect.position.x, position.x); + new_rect.position.y = MAX(p_rect.position.y, position.y); + + Point2i p_rect_end = p_rect.position + p_rect.size; + Point2i end = position + size; + + new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x; + new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y; + + return new_rect; + } + + inline Rect2i merge(const Rect2i &p_rect) const { ///< return a merged rect +#ifdef MATH_CHECKS + if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { + ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); + } +#endif + Rect2i new_rect; + + new_rect.position.x = MIN(p_rect.position.x, position.x); + new_rect.position.y = MIN(p_rect.position.y, position.y); + + new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); + new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); + + new_rect.size = new_rect.size - new_rect.position; //make relative again + + return new_rect; + } + bool has_point(const Point2i &p_point) const { +#ifdef MATH_CHECKS + if (unlikely(size.x < 0 || size.y < 0)) { + ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); + } +#endif + if (p_point.x < position.x) { + return false; + } + if (p_point.y < position.y) { + return false; + } + + if (p_point.x >= (position.x + size.x)) { + return false; + } + if (p_point.y >= (position.y + size.y)) { + return false; + } + + return true; + } + + bool operator==(const Rect2i &p_rect) const { return position == p_rect.position && size == p_rect.size; } + bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; } + + Rect2i grow(int p_amount) const { + Rect2i g = *this; + g.position.x -= p_amount; + g.position.y -= p_amount; + g.size.width += p_amount * 2; + g.size.height += p_amount * 2; + return g; + } + + inline Rect2i grow_side(Side p_side, int p_amount) const { + Rect2i g = *this; + g = g.grow_individual((SIDE_LEFT == p_side) ? p_amount : 0, + (SIDE_TOP == p_side) ? p_amount : 0, + (SIDE_RIGHT == p_side) ? p_amount : 0, + (SIDE_BOTTOM == p_side) ? p_amount : 0); + return g; + } + + inline Rect2i grow_side_bind(uint32_t p_side, int p_amount) const { + return grow_side(Side(p_side), p_amount); + } + + inline Rect2i grow_individual(int p_left, int p_top, int p_right, int p_bottom) const { + Rect2i g = *this; + g.position.x -= p_left; + g.position.y -= p_top; + g.size.width += p_left + p_right; + g.size.height += p_top + p_bottom; + + return g; + } + + _FORCE_INLINE_ Rect2i expand(const Vector2i &p_vector) const { + Rect2i r = *this; + r.expand_to(p_vector); + return r; + } + + inline void expand_to(const Point2i &p_vector) { +#ifdef MATH_CHECKS + if (unlikely(size.x < 0 || size.y < 0)) { + ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); + } +#endif + Point2i begin = position; + Point2i end = position + size; + + if (p_vector.x < begin.x) { + begin.x = p_vector.x; + } + if (p_vector.y < begin.y) { + begin.y = p_vector.y; + } + + if (p_vector.x > end.x) { + end.x = p_vector.x; + } + if (p_vector.y > end.y) { + end.y = p_vector.y; + } + + position = begin; + size = end - begin; + } + + _FORCE_INLINE_ Rect2i abs() const { + return Rect2i(Point2i(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs()); + } + + _FORCE_INLINE_ void set_end(const Vector2i &p_end) { + size = p_end - position; + } + + _FORCE_INLINE_ Vector2i get_end() const { + return position + size; + } + + operator String() const; + operator Rect2() const; + + Rect2i() {} + Rect2i(int p_x, int p_y, int p_width, int p_height) : + position(Point2i(p_x, p_y)), + size(Size2i(p_width, p_height)) { + } + Rect2i(const Point2i &p_pos, const Size2i &p_size) : + position(p_pos), + size(p_size) { + } +}; + +#endif // RECT2I_H diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index 0201cf575c..e6e24e9b32 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -30,6 +30,8 @@ #include "transform_2d.h" +#include "core/string/ustring.h" + void Transform2D::invert() { // FIXME: this function assumes the basis is a rotation matrix, with no scaling. // Transform2D::affine_inverse can handle matrices with scaling, so GDScript should eventually use that. diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index 6c2d51bd9b..f4546c13c8 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -31,7 +31,12 @@ #ifndef TRANSFORM_2D_H #define TRANSFORM_2D_H -#include "core/math/rect2.h" // also includes vector2, math_funcs, and ustring +#include "core/math/math_funcs.h" +#include "core/math/rect2.h" +#include "core/math/vector2.h" +#include "core/templates/vector.h" + +class String; struct _NO_DISCARD_ Transform2D { // Warning #1: basis of Transform2D is stored differently from Basis. In terms of elements array, the basis matrix looks like "on paper": diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h index c16c278e74..3b4762e221 100644 --- a/core/math/transform_3d.h +++ b/core/math/transform_3d.h @@ -28,15 +28,14 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef TRANSFORM_H -#define TRANSFORM_H +#ifndef TRANSFORM_3D_H +#define TRANSFORM_3D_H #include "core/math/aabb.h" #include "core/math/basis.h" #include "core/math/plane.h" -class _NO_DISCARD_ Transform3D { -public: +struct _NO_DISCARD_ Transform3D { Basis basis; Vector3 origin; @@ -265,4 +264,4 @@ _FORCE_INLINE_ Plane Transform3D::xform_inv_fast(const Plane &p_plane, const Tra return Plane(normal, d); } -#endif // TRANSFORM_H +#endif // TRANSFORM_3D_H diff --git a/core/math/triangulate.h b/core/math/triangulate.h index d96bdb8cab..0bfcfcb978 100644 --- a/core/math/triangulate.h +++ b/core/math/triangulate.h @@ -32,6 +32,7 @@ #define TRIANGULATE_H #include "core/math/vector2.h" +#include "core/templates/vector.h" /* https://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index 676a0004ea..40149e8cc1 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -30,6 +30,9 @@ #include "vector2.h" +#include "core/math/vector2i.h" +#include "core/string/ustring.h" + real_t Vector2::angle() const { return Math::atan2(y, x); } @@ -202,91 +205,6 @@ Vector2::operator String() const { return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")"; } -/* Vector2i */ - -Vector2i Vector2i::clamp(const Vector2i &p_min, const Vector2i &p_max) const { - return Vector2i( - CLAMP(x, p_min.x, p_max.x), - CLAMP(y, p_min.y, p_max.y)); -} - -int64_t Vector2i::length_squared() const { - return x * (int64_t)x + y * (int64_t)y; -} - -double Vector2i::length() const { - return Math::sqrt((double)length_squared()); -} - -Vector2i Vector2i::operator+(const Vector2i &p_v) const { - return Vector2i(x + p_v.x, y + p_v.y); -} - -void Vector2i::operator+=(const Vector2i &p_v) { - x += p_v.x; - y += p_v.y; -} - -Vector2i Vector2i::operator-(const Vector2i &p_v) const { - return Vector2i(x - p_v.x, y - p_v.y); -} - -void Vector2i::operator-=(const Vector2i &p_v) { - x -= p_v.x; - y -= p_v.y; -} - -Vector2i Vector2i::operator*(const Vector2i &p_v1) const { - return Vector2i(x * p_v1.x, y * p_v1.y); -} - -Vector2i Vector2i::operator*(const int32_t &rvalue) const { - return Vector2i(x * rvalue, y * rvalue); -} - -void Vector2i::operator*=(const int32_t &rvalue) { - x *= rvalue; - y *= rvalue; -} - -Vector2i Vector2i::operator/(const Vector2i &p_v1) const { - return Vector2i(x / p_v1.x, y / p_v1.y); -} - -Vector2i Vector2i::operator/(const int32_t &rvalue) const { - return Vector2i(x / rvalue, y / rvalue); -} - -void Vector2i::operator/=(const int32_t &rvalue) { - x /= rvalue; - y /= rvalue; -} - -Vector2i Vector2i::operator%(const Vector2i &p_v1) const { - return Vector2i(x % p_v1.x, y % p_v1.y); -} - -Vector2i Vector2i::operator%(const int32_t &rvalue) const { - return Vector2i(x % rvalue, y % rvalue); -} - -void Vector2i::operator%=(const int32_t &rvalue) { - x %= rvalue; - y %= rvalue; -} - -Vector2i Vector2i::operator-() const { - return Vector2i(-x, -y); -} - -bool Vector2i::operator==(const Vector2i &p_vec2) const { - return x == p_vec2.x && y == p_vec2.y; -} - -bool Vector2i::operator!=(const Vector2i &p_vec2) const { - return x != p_vec2.x || y != p_vec2.y; -} - -Vector2i::operator String() const { - return "(" + itos(x) + ", " + itos(y) + ")"; +Vector2::operator Vector2i() const { + return Vector2i(x, y); } diff --git a/core/math/vector2.h b/core/math/vector2.h index af40b9e68d..92ac5257b0 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -32,8 +32,8 @@ #define VECTOR2_H #include "core/math/math_funcs.h" -#include "core/string/ustring.h" +class String; struct Vector2i; struct _NO_DISCARD_ Vector2 { @@ -60,10 +60,10 @@ struct _NO_DISCARD_ Vector2 { }; _FORCE_INLINE_ real_t &operator[](int p_idx) { - return p_idx ? y : x; + return coord[p_idx]; } _FORCE_INLINE_ const real_t &operator[](int p_idx) const { - return p_idx ? y : x; + return coord[p_idx]; } _FORCE_INLINE_ void set_all(const real_t p_value) { @@ -167,6 +167,7 @@ struct _NO_DISCARD_ Vector2 { real_t aspect() const { return width / height; } operator String() const; + operator Vector2i() const; _FORCE_INLINE_ Vector2() {} _FORCE_INLINE_ Vector2(const real_t p_x, const real_t p_y) { @@ -179,22 +180,6 @@ _FORCE_INLINE_ Vector2 Vector2::plane_project(const real_t p_d, const Vector2 &p return p_vec - *this * (dot(p_vec) - p_d); } -_FORCE_INLINE_ Vector2 operator*(const float p_scalar, const Vector2 &p_vec) { - return p_vec * p_scalar; -} - -_FORCE_INLINE_ Vector2 operator*(const double p_scalar, const Vector2 &p_vec) { - return p_vec * p_scalar; -} - -_FORCE_INLINE_ Vector2 operator*(const int32_t p_scalar, const Vector2 &p_vec) { - return p_vec * p_scalar; -} - -_FORCE_INLINE_ Vector2 operator*(const int64_t p_scalar, const Vector2 &p_vec) { - return p_vec * p_scalar; -} - _FORCE_INLINE_ Vector2 Vector2::operator+(const Vector2 &p_v) const { return Vector2(x + p_v.x, y + p_v.y); } @@ -279,116 +264,26 @@ Vector2 Vector2::direction_to(const Vector2 &p_to) const { return ret; } -typedef Vector2 Size2; -typedef Vector2 Point2; - -/* INTEGER STUFF */ - -struct _NO_DISCARD_ Vector2i { - enum Axis { - AXIS_X, - AXIS_Y, - }; - - union { - int32_t x = 0; - int32_t width; - }; - union { - int32_t y = 0; - int32_t height; - }; - - _FORCE_INLINE_ int32_t &operator[](int p_idx) { - return p_idx ? y : x; - } - _FORCE_INLINE_ const int32_t &operator[](int p_idx) const { - return p_idx ? y : x; - } - - _FORCE_INLINE_ Vector2i::Axis min_axis_index() const { - return x < y ? Vector2i::AXIS_X : Vector2i::AXIS_Y; - } - - _FORCE_INLINE_ Vector2i::Axis max_axis_index() const { - return x < y ? Vector2i::AXIS_Y : Vector2i::AXIS_X; - } - - Vector2i min(const Vector2i &p_vector2i) const { - return Vector2(MIN(x, p_vector2i.x), MIN(y, p_vector2i.y)); - } - - Vector2i max(const Vector2i &p_vector2i) const { - return Vector2(MAX(x, p_vector2i.x), MAX(y, p_vector2i.y)); - } - - Vector2i operator+(const Vector2i &p_v) const; - void operator+=(const Vector2i &p_v); - Vector2i operator-(const Vector2i &p_v) const; - void operator-=(const Vector2i &p_v); - Vector2i operator*(const Vector2i &p_v1) const; - - Vector2i operator*(const int32_t &rvalue) const; - void operator*=(const int32_t &rvalue); - - Vector2i operator/(const Vector2i &p_v1) const; - Vector2i operator/(const int32_t &rvalue) const; - void operator/=(const int32_t &rvalue); - - Vector2i operator%(const Vector2i &p_v1) const; - Vector2i operator%(const int32_t &rvalue) const; - void operator%=(const int32_t &rvalue); - - Vector2i operator-() const; - bool operator<(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } - bool operator>(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); } - - bool operator<=(const Vector2i &p_vec2) const { return x == p_vec2.x ? (y <= p_vec2.y) : (x < p_vec2.x); } - bool operator>=(const Vector2i &p_vec2) const { return x == p_vec2.x ? (y >= p_vec2.y) : (x > p_vec2.x); } - - bool operator==(const Vector2i &p_vec2) const; - bool operator!=(const Vector2i &p_vec2) const; - - int64_t length_squared() const; - double length() const; - - real_t aspect() const { return width / (real_t)height; } - Vector2i sign() const { return Vector2i(SIGN(x), SIGN(y)); } - Vector2i abs() const { return Vector2i(ABS(x), ABS(y)); } - Vector2i clamp(const Vector2i &p_min, const Vector2i &p_max) const; - - operator String() const; - - operator Vector2() const { return Vector2(x, y); } +// Multiplication operators required to workaround issues with LLVM using implicit conversion +// to Vector2i instead for integers where it should not. - inline Vector2i() {} - inline Vector2i(const Vector2 &p_vec2) { - x = (int32_t)p_vec2.x; - y = (int32_t)p_vec2.y; - } - inline Vector2i(const int32_t p_x, const int32_t p_y) { - x = p_x; - y = p_y; - } -}; - -_FORCE_INLINE_ Vector2i operator*(const int32_t &p_scalar, const Vector2i &p_vector) { - return p_vector * p_scalar; +_FORCE_INLINE_ Vector2 operator*(const float p_scalar, const Vector2 &p_vec) { + return p_vec * p_scalar; } -_FORCE_INLINE_ Vector2i operator*(const int64_t &p_scalar, const Vector2i &p_vector) { - return p_vector * p_scalar; +_FORCE_INLINE_ Vector2 operator*(const double p_scalar, const Vector2 &p_vec) { + return p_vec * p_scalar; } -_FORCE_INLINE_ Vector2i operator*(const float &p_scalar, const Vector2i &p_vector) { - return p_vector * p_scalar; +_FORCE_INLINE_ Vector2 operator*(const int32_t p_scalar, const Vector2 &p_vec) { + return p_vec * p_scalar; } -_FORCE_INLINE_ Vector2i operator*(const double &p_scalar, const Vector2i &p_vector) { - return p_vector * p_scalar; +_FORCE_INLINE_ Vector2 operator*(const int64_t p_scalar, const Vector2 &p_vec) { + return p_vec * p_scalar; } -typedef Vector2i Size2i; -typedef Vector2i Point2i; +typedef Vector2 Size2; +typedef Vector2 Point2; #endif // VECTOR2_H diff --git a/core/math/vector2i.cpp b/core/math/vector2i.cpp new file mode 100644 index 0000000000..dfed42e4d6 --- /dev/null +++ b/core/math/vector2i.cpp @@ -0,0 +1,125 @@ +/*************************************************************************/ +/* vector2i.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 "vector2i.h" + +#include "core/math/vector2.h" +#include "core/string/ustring.h" + +Vector2i Vector2i::clamp(const Vector2i &p_min, const Vector2i &p_max) const { + return Vector2i( + CLAMP(x, p_min.x, p_max.x), + CLAMP(y, p_min.y, p_max.y)); +} + +int64_t Vector2i::length_squared() const { + return x * (int64_t)x + y * (int64_t)y; +} + +double Vector2i::length() const { + return Math::sqrt((double)length_squared()); +} + +Vector2i Vector2i::operator+(const Vector2i &p_v) const { + return Vector2i(x + p_v.x, y + p_v.y); +} + +void Vector2i::operator+=(const Vector2i &p_v) { + x += p_v.x; + y += p_v.y; +} + +Vector2i Vector2i::operator-(const Vector2i &p_v) const { + return Vector2i(x - p_v.x, y - p_v.y); +} + +void Vector2i::operator-=(const Vector2i &p_v) { + x -= p_v.x; + y -= p_v.y; +} + +Vector2i Vector2i::operator*(const Vector2i &p_v1) const { + return Vector2i(x * p_v1.x, y * p_v1.y); +} + +Vector2i Vector2i::operator*(const int32_t &rvalue) const { + return Vector2i(x * rvalue, y * rvalue); +} + +void Vector2i::operator*=(const int32_t &rvalue) { + x *= rvalue; + y *= rvalue; +} + +Vector2i Vector2i::operator/(const Vector2i &p_v1) const { + return Vector2i(x / p_v1.x, y / p_v1.y); +} + +Vector2i Vector2i::operator/(const int32_t &rvalue) const { + return Vector2i(x / rvalue, y / rvalue); +} + +void Vector2i::operator/=(const int32_t &rvalue) { + x /= rvalue; + y /= rvalue; +} + +Vector2i Vector2i::operator%(const Vector2i &p_v1) const { + return Vector2i(x % p_v1.x, y % p_v1.y); +} + +Vector2i Vector2i::operator%(const int32_t &rvalue) const { + return Vector2i(x % rvalue, y % rvalue); +} + +void Vector2i::operator%=(const int32_t &rvalue) { + x %= rvalue; + y %= rvalue; +} + +Vector2i Vector2i::operator-() const { + return Vector2i(-x, -y); +} + +bool Vector2i::operator==(const Vector2i &p_vec2) const { + return x == p_vec2.x && y == p_vec2.y; +} + +bool Vector2i::operator!=(const Vector2i &p_vec2) const { + return x != p_vec2.x || y != p_vec2.y; +} + +Vector2i::operator String() const { + return "(" + itos(x) + ", " + itos(y) + ")"; +} + +Vector2i::operator Vector2() const { + return Vector2((int32_t)x, (int32_t)y); +} diff --git a/core/math/vector2i.h b/core/math/vector2i.h new file mode 100644 index 0000000000..3f5f12d4dd --- /dev/null +++ b/core/math/vector2i.h @@ -0,0 +1,149 @@ +/*************************************************************************/ +/* vector2i.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 VECTOR2I_H +#define VECTOR2I_H + +#include "core/math/math_funcs.h" + +class String; +struct Vector2; + +struct _NO_DISCARD_ Vector2i { + enum Axis { + AXIS_X, + AXIS_Y, + }; + + union { + struct { + union { + int32_t x; + int32_t width; + }; + union { + int32_t y; + int32_t height; + }; + }; + + int32_t coord[2] = { 0 }; + }; + + _FORCE_INLINE_ int32_t &operator[](int p_idx) { + return coord[p_idx]; + } + _FORCE_INLINE_ const int32_t &operator[](int p_idx) const { + return coord[p_idx]; + } + + _FORCE_INLINE_ Vector2i::Axis min_axis_index() const { + return x < y ? Vector2i::AXIS_X : Vector2i::AXIS_Y; + } + + _FORCE_INLINE_ Vector2i::Axis max_axis_index() const { + return x < y ? Vector2i::AXIS_Y : Vector2i::AXIS_X; + } + + Vector2i min(const Vector2i &p_vector2i) const { + return Vector2i(MIN(x, p_vector2i.x), MIN(y, p_vector2i.y)); + } + + Vector2i max(const Vector2i &p_vector2i) const { + return Vector2i(MAX(x, p_vector2i.x), MAX(y, p_vector2i.y)); + } + + Vector2i operator+(const Vector2i &p_v) const; + void operator+=(const Vector2i &p_v); + Vector2i operator-(const Vector2i &p_v) const; + void operator-=(const Vector2i &p_v); + Vector2i operator*(const Vector2i &p_v1) const; + + Vector2i operator*(const int32_t &rvalue) const; + void operator*=(const int32_t &rvalue); + + Vector2i operator/(const Vector2i &p_v1) const; + Vector2i operator/(const int32_t &rvalue) const; + void operator/=(const int32_t &rvalue); + + Vector2i operator%(const Vector2i &p_v1) const; + Vector2i operator%(const int32_t &rvalue) const; + void operator%=(const int32_t &rvalue); + + Vector2i operator-() const; + bool operator<(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } + bool operator>(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); } + + bool operator<=(const Vector2i &p_vec2) const { return x == p_vec2.x ? (y <= p_vec2.y) : (x < p_vec2.x); } + bool operator>=(const Vector2i &p_vec2) const { return x == p_vec2.x ? (y >= p_vec2.y) : (x > p_vec2.x); } + + bool operator==(const Vector2i &p_vec2) const; + bool operator!=(const Vector2i &p_vec2) const; + + int64_t length_squared() const; + double length() const; + + real_t aspect() const { return width / (real_t)height; } + Vector2i sign() const { return Vector2i(SIGN(x), SIGN(y)); } + Vector2i abs() const { return Vector2i(ABS(x), ABS(y)); } + Vector2i clamp(const Vector2i &p_min, const Vector2i &p_max) const; + + operator String() const; + operator Vector2() const; + + inline Vector2i() {} + inline Vector2i(const int32_t p_x, const int32_t p_y) { + x = p_x; + y = p_y; + } +}; + +// Multiplication operators required to workaround issues with LLVM using implicit conversion. + +_FORCE_INLINE_ Vector2i operator*(const int32_t p_scalar, const Vector2i &p_vector) { + return p_vector * p_scalar; +} + +_FORCE_INLINE_ Vector2i operator*(const int64_t p_scalar, const Vector2i &p_vector) { + return p_vector * p_scalar; +} + +_FORCE_INLINE_ Vector2i operator*(const float p_scalar, const Vector2i &p_vector) { + return p_vector * p_scalar; +} + +_FORCE_INLINE_ Vector2i operator*(const double p_scalar, const Vector2i &p_vector) { + return p_vector * p_scalar; +} + +typedef Vector2i Size2i; +typedef Vector2i Point2i; + +#endif // VECTOR2I_H diff --git a/core/math/vector3.h b/core/math/vector3.h index b62edef40f..345329f7f3 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -35,7 +35,8 @@ #include "core/math/vector2.h" #include "core/math/vector3i.h" #include "core/string/ustring.h" -class Basis; + +struct Basis; struct _NO_DISCARD_ Vector3 { static const int AXIS_COUNT = 3; @@ -342,6 +343,9 @@ Vector3 &Vector3::operator*=(const real_t p_scalar) { return *this; } +// Multiplication operators required to workaround issues with LLVM using implicit conversion +// to Vector2i instead for integers where it should not. + _FORCE_INLINE_ Vector3 operator*(const float p_scalar, const Vector3 &p_vec) { return p_vec * p_scalar; } diff --git a/core/math/vector3i.h b/core/math/vector3i.h index 1564ee9173..d166de80aa 100644 --- a/core/math/vector3i.h +++ b/core/math/vector3i.h @@ -194,6 +194,12 @@ Vector3i &Vector3i::operator*=(const int32_t p_scalar) { return *this; } +Vector3i Vector3i::operator*(const int32_t p_scalar) const { + return Vector3i(x * p_scalar, y * p_scalar, z * p_scalar); +} + +// Multiplication operators required to workaround issues with LLVM using implicit conversion. + _FORCE_INLINE_ Vector3i operator*(const int32_t p_scalar, const Vector3i &p_vector) { return p_vector * p_scalar; } @@ -210,10 +216,6 @@ _FORCE_INLINE_ Vector3i operator*(const double p_scalar, const Vector3i &p_vecto return p_vector * p_scalar; } -Vector3i Vector3i::operator*(const int32_t p_scalar) const { - return Vector3i(x * p_scalar, y * p_scalar, z * p_scalar); -} - Vector3i &Vector3i::operator/=(const int32_t p_scalar) { x /= p_scalar; y /= p_scalar; diff --git a/core/multiplayer/multiplayer_api.cpp b/core/multiplayer/multiplayer_api.cpp index 627825246a..3533acd103 100644 --- a/core/multiplayer/multiplayer_api.cpp +++ b/core/multiplayer/multiplayer_api.cpp @@ -32,9 +32,6 @@ #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> @@ -42,11 +39,14 @@ #include "core/os/os.h" #endif +MultiplayerReplicationInterface *(*MultiplayerAPI::create_default_replication_interface)(MultiplayerAPI *p_multiplayer) = nullptr; +MultiplayerRPCInterface *(*MultiplayerAPI::create_default_rpc_interface)(MultiplayerAPI *p_multiplayer) = nullptr; +MultiplayerCacheInterface *(*MultiplayerAPI::create_default_cache_interface)(MultiplayerAPI *p_multiplayer) = nullptr; + #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); @@ -74,7 +74,7 @@ void MultiplayerAPI::poll() { Error err = multiplayer_peer->get_packet(&packet, len); if (err != OK) { ERR_PRINT("Error getting packet!"); - break; // Something is wrong! + return; // Something is wrong! } remote_sender_id = sender; @@ -82,29 +82,25 @@ void MultiplayerAPI::poll() { 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. + return; // 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(); - } + replicator->on_network_process(); } void MultiplayerAPI::clear() { - replicator->clear(); connected_peers.clear(); - path_get_cache.clear(); - path_send_cache.clear(); packet_cache.clear(); - last_send_cache_id = 1; + cache->clear(); } -void MultiplayerAPI::set_root_node(Node *p_node) { - root_node = p_node; +void MultiplayerAPI::set_root_path(const NodePath &p_path) { + ERR_FAIL_COND_MSG(!p_path.is_absolute() && !p_path.is_empty(), "MultiplayerAPI root path must be absolute."); + root_path = p_path; } -Node *MultiplayerAPI::get_root_node() { - return root_node; +NodePath MultiplayerAPI::get_root_path() const { + return root_path; } void MultiplayerAPI::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) { @@ -133,6 +129,7 @@ void MultiplayerAPI::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) { multiplayer_peer->connect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); multiplayer_peer->connect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); } + replicator->on_reset(); } Ref<MultiplayerPeer> MultiplayerAPI::get_multiplayer_peer() const { @@ -140,7 +137,7 @@ Ref<MultiplayerPeer> MultiplayerAPI::get_multiplayer_peer() const { } 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(root_path.is_empty(), "Multiplayer root was not initialized. If you are using custom multiplayer, remember to set the root path via MultiplayerAPI.set_root_path before using it."); ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small."); #ifdef DEBUG_ENABLED @@ -152,166 +149,32 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ switch (packet_type) { case NETWORK_COMMAND_SIMPLIFY_PATH: { - _process_simplify_path(p_from, p_packet, p_packet_len); + cache->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); + cache->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); + rpc->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); + replicator->on_spawn_receive(p_from, p_packet, p_packet_len); } break; case NETWORK_COMMAND_DESPAWN: { - replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, false); + replicator->on_despawn_receive(p_from, p_packet, p_packet_len); } break; case NETWORK_COMMAND_SYNC: { - replicator->process_sync(p_from, p_packet, p_packet_len); + replicator->on_sync_receive(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. @@ -324,7 +187,7 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC #define ENCODE_16 1 << 5 #define ENCODE_32 2 << 5 #define ENCODE_64 3 << 5 -Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) { +Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_allow_object_decoding) { // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31 CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK); @@ -385,7 +248,7 @@ Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint } break; default: // Any other case is not yet compressed. - Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding); + Error err = encode_variant(p_variant, r_buffer, r_len, p_allow_object_decoding); if (err != OK) { return err; } @@ -399,7 +262,7 @@ Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint return OK; } -Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) { +Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding) { const uint8_t *buf = p_buffer; int len = p_len; @@ -458,7 +321,7 @@ Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const ui } } break; default: - Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding); + Error err = decode_variant(r_variant, p_buffer, p_len, r_len, p_allow_object_decoding); if (err != OK) { return err; } @@ -467,27 +330,86 @@ Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const ui return OK; } +Error MultiplayerAPI::encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw, bool p_allow_object_decoding) { + r_len = 0; + int size = 0; + + if (p_count == 0) { + if (r_raw) { + *r_raw = true; + } + return OK; + } + + // Try raw encoding optimization. + if (r_raw && p_count == 1) { + *r_raw = false; + const Variant &v = *(p_variants[0]); + if (v.get_type() == Variant::PACKED_BYTE_ARRAY) { + *r_raw = true; + const PackedByteArray pba = v; + if (p_buffer) { + memcpy(p_buffer, pba.ptr(), pba.size()); + } + r_len += pba.size(); + } else { + encode_and_compress_variant(v, p_buffer, size, p_allow_object_decoding); + r_len += size; + } + return OK; + } + + // Regular encoding. + for (int i = 0; i < p_count; i++) { + const Variant &v = *(p_variants[i]); + encode_and_compress_variant(v, p_buffer ? p_buffer + r_len : nullptr, size, p_allow_object_decoding); + r_len += size; + } + return OK; +} + +Error MultiplayerAPI::decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw, bool p_allow_object_decoding) { + r_len = 0; + int argc = r_variants.size(); + if (argc == 0 && p_raw) { + return OK; + } + ERR_FAIL_COND_V(p_raw && argc != 1, ERR_INVALID_DATA); + if (p_raw) { + r_len = p_len; + PackedByteArray pba; + pba.resize(p_len); + memcpy(pba.ptrw(), p_buffer, p_len); + r_variants.write[0] = pba; + return OK; + } + + Vector<Variant> args; + Vector<const Variant *> argp; + args.resize(argc); + + for (int i = 0; i < argc; i++) { + ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small."); + + int vlen; + Error err = MultiplayerAPI::decode_and_decompress_variant(r_variants.write[i], &p_buffer[r_len], p_len - r_len, &vlen, p_allow_object_decoding); + ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid packet received. Unable to decode state variable."); + r_len += vlen; + } + 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); - } + cache->on_peer_change(p_id, true); + replicator->on_peer_change(p_id, true); emit_signal(SNAME("peer_connected"), p_id); } void MultiplayerAPI::_del_peer(int p_id) { + replicator->on_peer_change(p_id, false); + cache->on_peer_change(p_id, false); 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); } @@ -500,6 +422,7 @@ void MultiplayerAPI::_connection_failed() { } void MultiplayerAPI::_server_disconnected() { + replicator->on_reset(); emit_signal(SNAME("server_disconnected")); } @@ -537,41 +460,15 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac } 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); + return cache->is_cache_confirmed(p_path, p_peer); } -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)); +bool MultiplayerAPI::send_object_cache(Object *p_obj, NodePath p_path, int p_peer_id, int &r_id) { + return cache->send_object_cache(p_obj, p_path, p_peer_id, r_id); +} - 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; +Object *MultiplayerAPI::get_cached_object(int p_from, uint32_t p_cache_id) { + return cache->get_cached_object(p_from, p_cache_id); } int MultiplayerAPI::get_unique_id() const { @@ -612,17 +509,33 @@ 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); +String MultiplayerAPI::get_rpc_md5(const Object *p_obj) const { + return rpc->get_rpc_md5(p_obj); } -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::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { + rpc->rpcp(p_obj, p_peer_id, p_method, p_arg, p_argcount); +} + +Error MultiplayerAPI::spawn(Object *p_object, Variant p_config) { + return replicator->on_spawn(p_object, p_config); +} + +Error MultiplayerAPI::despawn(Object *p_object, Variant p_config) { + return replicator->on_despawn(p_object, p_config); +} + +Error MultiplayerAPI::replication_start(Object *p_object, Variant p_config) { + return replicator->on_replication_start(p_object, p_config); +} + +Error MultiplayerAPI::replication_stop(Object *p_object, Variant p_config) { + return replicator->on_replication_stop(p_object, p_config); } 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("set_root_path", "path"), &MultiplayerAPI::set_root_path); + ClassDB::bind_method(D_METHOD("get_root_path"), &MultiplayerAPI::get_root_path); 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); @@ -638,14 +551,12 @@ void MultiplayerAPI::_bind_methods() { 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(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path"); 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"))); @@ -656,13 +567,23 @@ void MultiplayerAPI::_bind_methods() { } MultiplayerAPI::MultiplayerAPI() { - replicator = memnew(MultiplayerReplicator(this)); - rpc_manager = memnew(RPCManager(this)); - clear(); + if (create_default_replication_interface) { + replicator = Ref<MultiplayerReplicationInterface>(create_default_replication_interface(this)); + } else { + replicator.instantiate(); + } + if (create_default_rpc_interface) { + rpc = Ref<MultiplayerRPCInterface>(create_default_rpc_interface(this)); + } else { + rpc.instantiate(); + } + if (create_default_cache_interface) { + cache = Ref<MultiplayerCacheInterface>(create_default_cache_interface(this)); + } else { + cache.instantiate(); + } } MultiplayerAPI::~MultiplayerAPI() { clear(); - memdelete(replicator); - memdelete(rpc_manager); } diff --git a/core/multiplayer/multiplayer_api.h b/core/multiplayer/multiplayer_api.h index 713035428d..9fe67615e3 100644 --- a/core/multiplayer/multiplayer_api.h +++ b/core/multiplayer/multiplayer_api.h @@ -35,8 +35,54 @@ #include "core/multiplayer/multiplayer_peer.h" #include "core/object/ref_counted.h" -class MultiplayerReplicator; -class RPCManager; +class MultiplayerAPI; + +class MultiplayerReplicationInterface : public RefCounted { + GDCLASS(MultiplayerReplicationInterface, RefCounted); + +public: + virtual void on_peer_change(int p_id, bool p_connected) {} + virtual void on_reset() {} + virtual Error on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { return ERR_UNAVAILABLE; } + virtual Error on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { return ERR_UNAVAILABLE; } + virtual Error on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { return ERR_UNAVAILABLE; } + virtual Error on_spawn(Object *p_obj, Variant p_config) { return ERR_UNAVAILABLE; } + virtual Error on_despawn(Object *p_obj, Variant p_config) { return ERR_UNAVAILABLE; } + virtual Error on_replication_start(Object *p_obj, Variant p_config) { return ERR_UNAVAILABLE; } + virtual Error on_replication_stop(Object *p_obj, Variant p_config) { return ERR_UNAVAILABLE; } + virtual void on_network_process() {} + + MultiplayerReplicationInterface() {} +}; + +class MultiplayerRPCInterface : public RefCounted { + GDCLASS(MultiplayerRPCInterface, RefCounted); + +public: + // Called by Node.rpc + virtual void rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {} + virtual void process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) {} + virtual String get_rpc_md5(const Object *p_obj) const { return String(); } + + MultiplayerRPCInterface() {} +}; + +class MultiplayerCacheInterface : public RefCounted { + GDCLASS(MultiplayerCacheInterface, RefCounted); + +public: + virtual void clear() {} + virtual void on_peer_change(int p_id, bool p_connected) {} + virtual void process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {} + virtual void process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {} + + // Returns true if all peers have cached path. + virtual bool send_object_cache(Object *p_obj, NodePath p_path, int p_target, int &p_id) { return false; } + virtual Object *get_cached_object(int p_from, uint32_t p_cache_id) { return nullptr; } + virtual bool is_cache_confirmed(NodePath p_path, int p_peer) { return false; } + + MultiplayerCacheInterface() {} +}; class MultiplayerAPI : public RefCounted { GDCLASS(MultiplayerAPI, RefCounted); @@ -66,67 +112,56 @@ public: }; private: - //path sent caches - struct PathSentCache { - Map<int, bool> confirmed_peers; - int id; - }; - - //path get caches - struct PathGetCache { - struct NodeInfo { - NodePath path; - ObjectID instance; - }; - - Map<int, NodeInfo> nodes; - }; - 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; + NodePath root_path; bool allow_object_decoding = false; - MultiplayerReplicator *replicator = nullptr; - RPCManager *rpc_manager = nullptr; + Ref<MultiplayerCacheInterface> cache; + Ref<MultiplayerReplicationInterface> replicator; + Ref<MultiplayerRPCInterface> rpc; 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); void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len); public: + static MultiplayerReplicationInterface *(*create_default_replication_interface)(MultiplayerAPI *p_multiplayer); + static MultiplayerRPCInterface *(*create_default_rpc_interface)(MultiplayerAPI *p_multiplayer); + static MultiplayerCacheInterface *(*create_default_cache_interface)(MultiplayerAPI *p_multiplayer); + + static Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len, bool p_allow_object_decoding); + static Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding); + static Error encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw = nullptr, bool p_allow_object_decoding = false); + static Error decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw = false, bool p_allow_object_decoding = false); + void poll(); void clear(); - void set_root_node(Node *p_node); - Node *get_root_node(); + void set_root_path(const NodePath &p_path); + NodePath get_root_path() const; 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, 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); + // RPC API + void rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount); + String get_rpc_md5(const Object *p_obj) const; + // Replication API + Error spawn(Object *p_object, Variant p_config); + Error despawn(Object *p_object, Variant p_config); + Error replication_start(Object *p_object, Variant p_config); + Error replication_stop(Object *p_object, Variant p_config); + // Cache API + bool send_object_cache(Object *p_obj, NodePath p_path, int p_target, int &p_id); + Object *get_cached_object(int p_from, uint32_t p_cache_id); bool is_cache_confirmed(NodePath p_path, int p_peer); void _add_peer(int p_id); @@ -148,9 +183,6 @@ public: void set_allow_object_decoding(bool p_enable); bool is_object_decoding_allowed() const; - MultiplayerReplicator *get_replicator() const { return replicator; } - RPCManager *get_rpc_manager() const { return rpc_manager; } - #ifdef DEBUG_ENABLED void profile_bandwidth(const String &p_inout, int p_size); #endif diff --git a/core/multiplayer/multiplayer_replicator.cpp b/core/multiplayer/multiplayer_replicator.cpp deleted file mode 100644 index e7de8219c7..0000000000 --- a/core/multiplayer/multiplayer_replicator.cpp +++ /dev/null @@ -1,791 +0,0 @@ -/*************************************************************************/ -/* multiplayer_replicator.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "core/multiplayer/multiplayer_replicator.h" - -#include "core/io/marshalls.h" -#include "scene/main/node.h" -#include "scene/resources/packed_scene.h" - -#define MAKE_ROOM(m_amount) \ - if (packet_cache.size() < m_amount) \ - packet_cache.resize(m_amount); - -Error MultiplayerReplicator::_sync_all_default(const ResourceUID::ID &p_scene_id, int p_peer) { - ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER); - SceneConfig &cfg = replications[p_scene_id]; - int full_size = 0; - bool same_size = true; - int last_size = 0; - bool all_raw = true; - struct EncodeInfo { - int size = 0; - bool raw = false; - List<Variant> state; - }; - Map<ObjectID, struct EncodeInfo> state; - if (tracked_objects.has(p_scene_id)) { - for (const ObjectID &obj_id : tracked_objects[p_scene_id]) { - Object *obj = ObjectDB::get_instance(obj_id); - if (obj) { - struct EncodeInfo info; - Error err = _get_state(cfg.sync_properties, obj, info.state); - ERR_CONTINUE(err); - err = _encode_state(info.state, nullptr, info.size, &info.raw); - ERR_CONTINUE(err); - state[obj_id] = info; - full_size += info.size; - if (last_size && info.size != last_size) { - same_size = false; - } - all_raw = all_raw && info.raw; - last_size = info.size; - } - } - } - // Default implementation do not send empty updates. - if (!full_size) { - return OK; - } -#ifdef DEBUG_ENABLED - if (full_size > 4096 && cfg.sync_interval) { - WARN_PRINT_ONCE(vformat("The timed state update for scene %d is big (%d bytes) consider optimizing it", p_scene_id)); - } -#endif - if (same_size) { - // This is fast and small. Should we allow more than 256 objects per type? - // This costs us 1 byte. - MAKE_ROOM(SYNC_CMD_OFFSET + 1 + 2 + 2 + full_size); - } else { - MAKE_ROOM(SYNC_CMD_OFFSET + 1 + 2 + state.size() * 2 + full_size); - } - int ofs = 0; - uint8_t *ptr = packet_cache.ptrw(); - ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC | (same_size ? BYTE_OR_ZERO_FLAG : 0); - ofs = 1; - ofs += encode_uint64(p_scene_id, &ptr[ofs]); - ptr[ofs] = cfg.sync_recv++; - ofs += 1; - ofs += encode_uint16(state.size(), &ptr[ofs]); - if (same_size) { - ofs += encode_uint16(last_size + (all_raw ? 1 << 15 : 0), &ptr[ofs]); - } - for (const ObjectID &obj_id : tracked_objects[p_scene_id]) { - if (!state.has(obj_id)) { - continue; - } - struct EncodeInfo &info = state[obj_id]; - Object *obj = ObjectDB::get_instance(obj_id); - ERR_CONTINUE(!obj); - int size = 0; - if (!same_size) { - // We need to encode the size of every object. - ofs += encode_uint16(info.size + (info.raw ? 1 << 15 : 0), &ptr[ofs]); - } - Error err = _encode_state(info.state, &ptr[ofs], size, &info.raw); - ERR_CONTINUE(err); - ofs += size; - } - Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); - peer->set_target_peer(p_peer); - peer->set_transfer_channel(0); - peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_UNRELIABLE); - return peer->put_packet(ptr, ofs); -} - -void MultiplayerReplicator::_process_default_sync(const ResourceUID::ID &p_id, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < SYNC_CMD_OFFSET + 5, "Invalid spawn packet received"); - ERR_FAIL_COND_MSG(!replications.has(p_id), "Invalid spawn ID received " + itos(p_id)); - SceneConfig &cfg = replications[p_id]; - ERR_FAIL_COND_MSG(cfg.mode != REPLICATION_MODE_SERVER || multiplayer->is_server(), "The default implementation only allows sync packets from the server"); - const bool same_size = p_packet[0] & BYTE_OR_ZERO_FLAG; - int ofs = SYNC_CMD_OFFSET; - int time = p_packet[ofs]; - // Skip old update. - if (time < cfg.sync_recv && cfg.sync_recv - time < 127) { - return; - } - cfg.sync_recv = time; - ofs += 1; - int count = decode_uint16(&p_packet[ofs]); - ofs += 2; -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(!tracked_objects.has(p_id) || tracked_objects[p_id].size() != count); -#else - if (!tracked_objects.has(p_id) || tracked_objects[p_id].size() != count) { - return; - } -#endif - int data_size = 0; - bool raw = false; - if (same_size) { - // This is fast and optimized. - data_size = decode_uint16(&p_packet[ofs]); - raw = (data_size & (1 << 15)) != 0; - data_size = data_size & ~(1 << 15); - ofs += 2; - ERR_FAIL_COND(p_packet_len - ofs < data_size * count); - } - for (const ObjectID &obj_id : tracked_objects[p_id]) { - Object *obj = ObjectDB::get_instance(obj_id); - ERR_CONTINUE(!obj); - if (!same_size) { - // This is slow and wasteful. - data_size = decode_uint16(&p_packet[ofs]); - raw = (data_size & (1 << 15)) != 0; - data_size = data_size & ~(1 << 15); - ofs += 2; - ERR_FAIL_COND(p_packet_len - ofs < data_size); - } - int size = 0; - Error err = _decode_state(cfg.sync_properties, obj, &p_packet[ofs], data_size, size, raw); - ofs += data_size; - ERR_CONTINUE(err); - ERR_CONTINUE(size != data_size); - } -} - -Error MultiplayerReplicator::_send_default_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, Object *p_obj, const NodePath &p_path, bool p_spawn) { - ERR_FAIL_COND_V(p_spawn && !p_obj, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER); - Error err; - // Prepare state - List<Variant> state_variants; - int state_len = 0; - const SceneConfig &cfg = replications[p_scene_id]; - if (p_spawn) { - if ((err = _get_state(cfg.properties, p_obj, state_variants)) != OK) { - return err; - } - } - - bool is_raw = false; - if (state_variants.size() == 1 && state_variants[0].get_type() == Variant::PACKED_BYTE_ARRAY) { - is_raw = true; - const PackedByteArray pba = state_variants[0]; - state_len = pba.size(); - } else if (state_variants.size()) { - err = _encode_state(state_variants, nullptr, state_len); - ERR_FAIL_COND_V(err, err); - } else { - is_raw = true; - } - - int ofs = 0; - - // Prepare simplified path - const Node *root_node = multiplayer->get_root_node(); - ERR_FAIL_COND_V(!root_node, ERR_UNCONFIGURED); - NodePath rel_path = (root_node->get_path()).rel_path_to(p_path); - const Vector<StringName> names = rel_path.get_names(); - ERR_FAIL_COND_V(names.size() < 2, ERR_INVALID_PARAMETER); - - NodePath parent = NodePath(names.slice(0, names.size() - 1), false); - ERR_FAIL_COND_V_MSG(!root_node->has_node(parent), ERR_INVALID_PARAMETER, "Path not found: " + parent); - - int path_id = 0; - multiplayer->send_confirm_path(root_node->get_node(parent), parent, p_peer_id, path_id); - - // Encode name and parent ID. - CharString cname = String(names[names.size() - 1]).utf8(); - int nlen = encode_cstring(cname.get_data(), nullptr); - MAKE_ROOM(SPAWN_CMD_OFFSET + 4 + 4 + nlen + state_len); - uint8_t *ptr = packet_cache.ptrw(); - ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) | (is_raw ? BYTE_OR_ZERO_FLAG : 0); - ofs = 1; - ofs += encode_uint64(p_scene_id, &ptr[ofs]); - ofs += encode_uint32(path_id, &ptr[ofs]); - ofs += encode_uint32(nlen, &ptr[ofs]); - ofs += encode_cstring(cname.get_data(), &ptr[ofs]); - - // Encode state. - if (!is_raw) { - _encode_state(state_variants, &ptr[ofs], state_len); - } else if (state_len) { - PackedByteArray pba = state_variants[0]; - memcpy(&ptr[ofs], pba.ptr(), state_len); - } - - Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); - peer->set_target_peer(p_peer_id); - peer->set_transfer_channel(0); - peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); - return peer->put_packet(ptr, ofs + state_len); -} - -void MultiplayerReplicator::_process_default_spawn_despawn(int p_from, const ResourceUID::ID &p_scene_id, const uint8_t *p_packet, int p_packet_len, bool p_spawn) { - ERR_FAIL_COND_MSG(p_packet_len < SPAWN_CMD_OFFSET + 9, "Invalid spawn packet received"); - int ofs = SPAWN_CMD_OFFSET; - uint32_t node_target = decode_uint32(&p_packet[ofs]); - Node *parent = multiplayer->get_cached_node(p_from, node_target); - ofs += 4; - ERR_FAIL_COND_MSG(parent == nullptr, "Invalid packet received. Requested node was not found."); - - uint32_t name_len = decode_uint32(&p_packet[ofs]); - ofs += 4; - ERR_FAIL_COND_MSG(name_len > uint32_t(p_packet_len - ofs), vformat("Invalid spawn packet size: %d, wants: %d", p_packet_len, ofs + name_len)); - ERR_FAIL_COND_MSG(name_len < 1, "Zero spawn name size."); - - const String name = String::utf8((const char *)&p_packet[ofs], name_len); - // We need to make sure no trickery happens here (e.g. despawning a subpath), but we want to allow autogenerated ("@") node names. - ERR_FAIL_COND_MSG(name.validate_node_name() != name.replace("@", ""), vformat("Invalid node name received: '%s'", name)); - ofs += name_len; - - const SceneConfig &cfg = replications[p_scene_id]; - if (cfg.mode == REPLICATION_MODE_SERVER && p_from == 1) { - String scene_path = ResourceUID::get_singleton()->get_id_path(p_scene_id); - if (p_spawn) { - const bool is_raw = ((p_packet[0] & BYTE_OR_ZERO_FLAG) >> BYTE_OR_ZERO_SHIFT) == 1; - - ERR_FAIL_COND_MSG(parent->has_node(name), vformat("Unable to spawn node. Node already exists: %s/%s", parent->get_path(), name)); - RES res = ResourceLoader::load(scene_path); - ERR_FAIL_COND_MSG(!res.is_valid(), "Unable to load scene to spawn at path: " + scene_path); - PackedScene *scene = Object::cast_to<PackedScene>(res.ptr()); - ERR_FAIL_COND(!scene); - Node *node = scene->instantiate(); - ERR_FAIL_COND(!node); - replicated_nodes[node->get_instance_id()] = p_scene_id; - _track(p_scene_id, node); - int size; - _decode_state(cfg.properties, node, &p_packet[ofs], p_packet_len - ofs, size, is_raw); - parent->_add_child_nocheck(node, name); - emit_signal(SNAME("spawned"), p_scene_id, node); - } else { - ERR_FAIL_COND_MSG(!parent->has_node(name), vformat("Path not found: %s/%s", parent->get_path(), name)); - Node *node = parent->get_node(name); - ERR_FAIL_COND_MSG(!replicated_nodes.has(node->get_instance_id()), vformat("Trying to despawn a Node that was not replicated: %s/%s", parent->get_path(), name)); - emit_signal(SNAME("despawned"), p_scene_id, node); - _untrack(p_scene_id, node); - replicated_nodes.erase(node->get_instance_id()); - node->queue_delete(); - } - } else { - PackedByteArray data; - if (p_packet_len > ofs) { - data.resize(p_packet_len - ofs); - memcpy(data.ptrw(), &p_packet[ofs], data.size()); - } - if (p_spawn) { - emit_signal(SNAME("spawn_requested"), p_from, p_scene_id, parent, name, data); - } else { - emit_signal(SNAME("despawn_requested"), p_from, p_scene_id, parent, name, data); - } - } -} - -void MultiplayerReplicator::process_spawn_despawn(int p_from, const uint8_t *p_packet, int p_packet_len, bool p_spawn) { - ERR_FAIL_COND_MSG(p_packet_len < SPAWN_CMD_OFFSET, "Invalid spawn packet received"); - ResourceUID::ID id = decode_uint64(&p_packet[1]); - ERR_FAIL_COND_MSG(!replications.has(id), "Invalid spawn ID received " + itos(id)); - - const SceneConfig &cfg = replications[id]; - if (cfg.on_spawn_despawn_receive.is_valid()) { - int ofs = SPAWN_CMD_OFFSET; - bool is_raw = ((p_packet[0] & BYTE_OR_ZERO_FLAG) >> BYTE_OR_ZERO_SHIFT) == 1; - Variant data; - int left = p_packet_len - ofs; - if (is_raw && left) { - PackedByteArray pba; - pba.resize(left); - memcpy(pba.ptrw(), &p_packet[ofs], pba.size()); - data = pba; - } else if (left) { - ERR_FAIL_COND(decode_variant(data, &p_packet[ofs], left) != OK); - } - - Variant args[4]; - args[0] = p_from; - args[1] = id; - args[2] = data; - args[3] = p_spawn; - const Variant *argp[] = { &args[0], &args[1], &args[2], &args[3] }; - Callable::CallError ce; - Variant ret; - cfg.on_spawn_despawn_receive.call(argp, 4, ret, ce); - ERR_FAIL_COND_MSG(ce.error != Callable::CallError::CALL_OK, "Custom receive function failed"); - } else { - _process_default_spawn_despawn(p_from, id, p_packet, p_packet_len, p_spawn); - } -} - -void MultiplayerReplicator::process_sync(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < SPAWN_CMD_OFFSET, "Invalid spawn packet received"); - ResourceUID::ID id = decode_uint64(&p_packet[1]); - ERR_FAIL_COND_MSG(!replications.has(id), "Invalid spawn ID received " + itos(id)); - const SceneConfig &cfg = replications[id]; - if (cfg.on_sync_receive.is_valid()) { - Array objs; - if (tracked_objects.has(id)) { - objs.resize(tracked_objects[id].size()); - int idx = 0; - for (const ObjectID &obj_id : tracked_objects[id]) { - objs[idx++] = ObjectDB::get_instance(obj_id); - } - } - PackedByteArray pba; - pba.resize(p_packet_len - SYNC_CMD_OFFSET); - if (pba.size()) { - memcpy(pba.ptrw(), p_packet + SYNC_CMD_OFFSET, p_packet_len - SYNC_CMD_OFFSET); - } - Variant args[4] = { p_from, id, objs, pba }; - Variant *argp[4] = { args, &args[1], &args[2], &args[3] }; - Callable::CallError ce; - Variant ret; - cfg.on_sync_receive.call((const Variant **)argp, 4, ret, ce); - ERR_FAIL_COND_MSG(ce.error != Callable::CallError::CALL_OK, "Custom sync function failed"); - } else { - ERR_FAIL_COND_MSG(p_from != 1, "Default sync implementation only allow syncing from server to client"); - _process_default_sync(id, p_packet, p_packet_len); - } -} - -Error MultiplayerReplicator::_get_state(const List<StringName> &p_properties, const Object *p_obj, List<Variant> &r_variant) { - ERR_FAIL_COND_V_MSG(!p_obj, ERR_INVALID_PARAMETER, "Cannot encode null object"); - for (const StringName &prop : p_properties) { - bool valid = false; - const Variant v = p_obj->get(prop, &valid); - ERR_FAIL_COND_V_MSG(!valid, ERR_INVALID_DATA, vformat("Property '%s' not found.", prop)); - r_variant.push_back(v); - } - return OK; -} - -Error MultiplayerReplicator::_encode_state(const List<Variant> &p_variants, uint8_t *p_buffer, int &r_len, bool *r_raw) { - r_len = 0; - int size = 0; - - // Try raw encoding optimization. - if (r_raw && p_variants.size() == 1) { - *r_raw = false; - const Variant v = p_variants[0]; - if (v.get_type() == Variant::PACKED_BYTE_ARRAY) { - *r_raw = true; - const PackedByteArray pba = v; - if (p_buffer) { - memcpy(p_buffer, pba.ptr(), pba.size()); - } - r_len += pba.size(); - } else { - multiplayer->encode_and_compress_variant(v, p_buffer, size); - r_len += size; - } - return OK; - } - - // Regular encoding. - for (const Variant &v : p_variants) { - multiplayer->encode_and_compress_variant(v, p_buffer ? p_buffer + r_len : nullptr, size); - r_len += size; - } - return OK; -} - -Error MultiplayerReplicator::_decode_state(const List<StringName> &p_properties, Object *p_obj, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw) { - r_len = 0; - int argc = p_properties.size(); - if (argc == 0 && p_raw) { - ERR_FAIL_COND_V_MSG(p_len != 0, ERR_INVALID_DATA, "Buffer has trailing bytes."); - return OK; - } - ERR_FAIL_COND_V(p_raw && argc != 1, ERR_INVALID_DATA); - if (p_raw) { - r_len = p_len; - PackedByteArray pba; - pba.resize(p_len); - memcpy(pba.ptrw(), p_buffer, p_len); - p_obj->set(p_properties[0], pba); - return OK; - } - - Vector<Variant> args; - Vector<const Variant *> argp; - args.resize(argc); - - for (int i = 0; i < argc; i++) { - ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small."); - - int vlen; - Error err = multiplayer->decode_and_decompress_variant(args.write[i], &p_buffer[r_len], p_len - r_len, &vlen); - ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid packet received. Unable to decode state variable."); - r_len += vlen; - } - ERR_FAIL_COND_V_MSG(p_len - r_len != 0, ERR_INVALID_DATA, "Buffer has trailing bytes."); - - int i = 0; - for (const StringName &prop : p_properties) { - p_obj->set(prop, args[i]); - i += 1; - } - return OK; -} - -Error MultiplayerReplicator::spawn_config(const ResourceUID::ID &p_id, ReplicationMode p_mode, const TypedArray<StringName> &p_props, const Callable &p_on_send, const Callable &p_on_recv) { - ERR_FAIL_COND_V(p_mode < REPLICATION_MODE_NONE || p_mode > REPLICATION_MODE_CUSTOM, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!ResourceUID::get_singleton()->has_id(p_id), ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V_MSG(p_on_send.is_valid() != p_on_recv.is_valid(), ERR_INVALID_PARAMETER, "Send and receive custom callables must be both valid or both empty"); -#ifdef TOOLS_ENABLED - if (!p_on_send.is_valid()) { - // We allow non scene spawning with custom callables. - String path = ResourceUID::get_singleton()->get_id_path(p_id); - RES res = ResourceLoader::load(path); - ERR_FAIL_COND_V(!res->is_class("PackedScene"), ERR_INVALID_PARAMETER); - } -#endif - if (p_mode == REPLICATION_MODE_NONE) { - if (replications.has(p_id)) { - replications.erase(p_id); - } - } else { - SceneConfig cfg; - cfg.mode = p_mode; - for (int i = 0; i < p_props.size(); i++) { - cfg.properties.push_back(p_props[i]); - } - cfg.on_spawn_despawn_send = p_on_send; - cfg.on_spawn_despawn_receive = p_on_recv; - replications[p_id] = cfg; - } - return OK; -} - -Error MultiplayerReplicator::sync_config(const ResourceUID::ID &p_id, uint64_t p_interval, const TypedArray<StringName> &p_props, const Callable &p_on_send, const Callable &p_on_recv) { - ERR_FAIL_COND_V(!ResourceUID::get_singleton()->has_id(p_id), ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V_MSG(p_on_send.is_valid() != p_on_recv.is_valid(), ERR_INVALID_PARAMETER, "Send and receive custom callables must be both valid or both empty"); - ERR_FAIL_COND_V(!replications.has(p_id), ERR_UNCONFIGURED); - SceneConfig &cfg = replications[p_id]; - ERR_FAIL_COND_V_MSG(p_interval && cfg.mode != REPLICATION_MODE_SERVER && !p_on_send.is_valid(), ERR_INVALID_PARAMETER, "Timed updates in custom mode are only allowed if custom callbacks are also specified"); - for (int i = 0; i < p_props.size(); i++) { - cfg.sync_properties.push_back(p_props[i]); - } - cfg.on_sync_send = p_on_send; - cfg.on_sync_receive = p_on_recv; - cfg.sync_interval = p_interval * 1000; - return OK; -} - -Error MultiplayerReplicator::_send_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, bool p_spawn) { - int data_size = 0; - int is_raw = false; - if (p_data.get_type() == Variant::PACKED_BYTE_ARRAY) { - const PackedByteArray pba = p_data; - is_raw = true; - data_size = p_data.operator PackedByteArray().size(); - } else if (p_data.get_type() == Variant::NIL) { - is_raw = true; - } else { - Error err = encode_variant(p_data, nullptr, data_size); - ERR_FAIL_COND_V(err, err); - } - MAKE_ROOM(SPAWN_CMD_OFFSET + data_size); - uint8_t *ptr = packet_cache.ptrw(); - ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) + ((is_raw ? 1 : 0) << BYTE_OR_ZERO_SHIFT); - encode_uint64(p_scene_id, &ptr[1]); - if (p_data.get_type() == Variant::PACKED_BYTE_ARRAY) { - const PackedByteArray pba = p_data; - memcpy(&ptr[SPAWN_CMD_OFFSET], pba.ptr(), pba.size()); - } else if (data_size) { - encode_variant(p_data, &ptr[SPAWN_CMD_OFFSET], data_size); - } - Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); - peer->set_target_peer(p_peer_id); - peer->set_transfer_channel(0); - peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); - return peer->put_packet(ptr, SPAWN_CMD_OFFSET + data_size); -} - -Error MultiplayerReplicator::send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, const NodePath &p_path) { - ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED); - ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id)); - const SceneConfig &cfg = replications[p_scene_id]; - if (cfg.on_spawn_despawn_send.is_valid()) { - return _send_spawn_despawn(p_peer_id, p_scene_id, p_data, true); - } else { - ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server(), ERR_UNAVAILABLE, "Manual despawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests."); - NodePath path = p_path; - Object *obj = p_data.get_type() == Variant::OBJECT ? p_data.get_validated_object() : nullptr; - if (path.is_empty() && obj) { - Node *node = Object::cast_to<Node>(obj); - if (node && node->is_inside_tree()) { - path = node->get_path(); - } - } - ERR_FAIL_COND_V_MSG(path.is_empty(), ERR_INVALID_PARAMETER, "Despawn default implementation requires a despawn path, or the data to be a node inside the SceneTree"); - return _send_default_spawn_despawn(p_peer_id, p_scene_id, obj, path, false); - } -} - -Error MultiplayerReplicator::send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, const NodePath &p_path) { - ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED); - ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id)); - const SceneConfig &cfg = replications[p_scene_id]; - if (cfg.on_spawn_despawn_send.is_valid()) { - return _send_spawn_despawn(p_peer_id, p_scene_id, p_data, false); - } else { - ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server(), ERR_UNAVAILABLE, "Manual spawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests."); - NodePath path = p_path; - Object *obj = p_data.get_type() == Variant::OBJECT ? p_data.get_validated_object() : nullptr; - ERR_FAIL_COND_V_MSG(!obj, ERR_INVALID_PARAMETER, "Spawn default implementation requires the data to be an object."); - if (path.is_empty()) { - Node *node = Object::cast_to<Node>(obj); - if (node && node->is_inside_tree()) { - path = node->get_path(); - } - } - ERR_FAIL_COND_V_MSG(path.is_empty(), ERR_INVALID_PARAMETER, "Spawn default implementation requires a spawn path, or the data to be a node inside the SceneTree"); - return _send_default_spawn_despawn(p_peer_id, p_scene_id, obj, path, true); - } -} - -Error MultiplayerReplicator::_spawn_despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer, bool p_spawn) { - ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id)); - - const SceneConfig &cfg = replications[p_scene_id]; - if (cfg.on_spawn_despawn_send.is_valid()) { - Variant args[4]; - args[0] = p_peer; - args[1] = p_scene_id; - args[2] = p_obj; - args[3] = p_spawn; - const Variant *argp[] = { &args[0], &args[1], &args[2], &args[3] }; - Callable::CallError ce; - Variant ret; - cfg.on_spawn_despawn_send.call(argp, 4, ret, ce); - ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, FAILED, "Custom send function failed"); - return OK; - } else { - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V_MSG(!p_obj, ERR_INVALID_PARAMETER, "Only nodes can be replicated by the default implementation"); - return _send_default_spawn_despawn(p_peer, p_scene_id, node, node->get_path(), p_spawn); - } -} - -Error MultiplayerReplicator::spawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer) { - return _spawn_despawn(p_scene_id, p_obj, p_peer, true); -} - -Error MultiplayerReplicator::despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer) { - return _spawn_despawn(p_scene_id, p_obj, p_peer, false); -} - -PackedByteArray MultiplayerReplicator::encode_state(const ResourceUID::ID &p_scene_id, const Object *p_obj, bool p_initial) { - PackedByteArray state; - ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), state, vformat("Spawnable not found: %d", p_scene_id)); - const SceneConfig &cfg = replications[p_scene_id]; - int len = 0; - List<Variant> state_vars; - const List<StringName> props = p_initial ? cfg.properties : cfg.sync_properties; - Error err = _get_state(props, p_obj, state_vars); - ERR_FAIL_COND_V_MSG(err != OK, state, "Unable to retrieve object state."); - err = _encode_state(state_vars, nullptr, len); - ERR_FAIL_COND_V_MSG(err != OK, state, "Unable to encode object state."); - state.resize(len); - _encode_state(state_vars, state.ptrw(), len); - return state; -} - -Error MultiplayerReplicator::decode_state(const ResourceUID::ID &p_scene_id, Object *p_obj, const PackedByteArray p_data, bool p_initial) { - ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id)); - const SceneConfig &cfg = replications[p_scene_id]; - const List<StringName> props = p_initial ? cfg.properties : cfg.sync_properties; - int size; - return _decode_state(props, p_obj, p_data.ptr(), p_data.size(), size); -} - -void MultiplayerReplicator::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) { - if (!multiplayer->has_multiplayer_peer()) { - return; - } - Node *root_node = multiplayer->get_root_node(); - ERR_FAIL_COND(!p_node || !p_node->get_parent() || !root_node); - NodePath path = (root_node->get_path()).rel_path_to(p_node->get_parent()->get_path()); - if (path.is_empty()) { - return; - } - ResourceUID::ID id = ResourceLoader::get_resource_uid(p_scene); - if (!replications.has(id)) { - return; - } - const SceneConfig &cfg = replications[id]; - if (p_enter) { - if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server()) { - replicated_nodes[p_node->get_instance_id()] = id; - _track(id, p_node); - spawn(id, p_node, 0); - } - emit_signal(SNAME("replicated_instance_added"), id, p_node); - } else { - if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server() && replicated_nodes.has(p_node->get_instance_id())) { - replicated_nodes.erase(p_node->get_instance_id()); - _untrack(id, p_node); - despawn(id, p_node, 0); - } - emit_signal(SNAME("replicated_instance_removed"), id, p_node); - } -} - -void MultiplayerReplicator::spawn_all(int p_peer) { - for (const KeyValue<ObjectID, ResourceUID::ID> &E : replicated_nodes) { - // Only server mode adds to replicated_nodes, no need to check it. - Object *obj = ObjectDB::get_instance(E.key); - ERR_CONTINUE(!obj); - Node *node = Object::cast_to<Node>(obj); - ERR_CONTINUE(!node); - spawn(E.value, node, p_peer); - } -} - -void MultiplayerReplicator::poll() { - for (KeyValue<ResourceUID::ID, SceneConfig> &E : replications) { - if (!E.value.sync_interval) { - continue; - } - if (E.value.mode == REPLICATION_MODE_SERVER && !multiplayer->is_server()) { - continue; - } - uint64_t time = OS::get_singleton()->get_ticks_usec(); - if (E.value.sync_last + E.value.sync_interval <= time) { - sync_all(E.key, 0); - E.value.sync_last = time; - } - // Handle wrapping. - if (E.value.sync_last > time) { - E.value.sync_last = time; - } - } -} - -void MultiplayerReplicator::track(const ResourceUID::ID &p_scene_id, Object *p_obj) { - ERR_FAIL_COND(!replications.has(p_scene_id)); - const SceneConfig &cfg = replications[p_scene_id]; - ERR_FAIL_COND_MSG(cfg.mode == REPLICATION_MODE_SERVER, "Manual object tracking is not allowed in server mode."); - _track(p_scene_id, p_obj); -} - -void MultiplayerReplicator::_track(const ResourceUID::ID &p_scene_id, Object *p_obj) { - ERR_FAIL_COND(!p_obj); - ERR_FAIL_COND(!replications.has(p_scene_id)); - if (!tracked_objects.has(p_scene_id)) { - tracked_objects[p_scene_id] = List<ObjectID>(); - } - tracked_objects[p_scene_id].push_back(p_obj->get_instance_id()); -} - -void MultiplayerReplicator::untrack(const ResourceUID::ID &p_scene_id, Object *p_obj) { - ERR_FAIL_COND(!replications.has(p_scene_id)); - const SceneConfig &cfg = replications[p_scene_id]; - ERR_FAIL_COND_MSG(cfg.mode == REPLICATION_MODE_SERVER, "Manual object tracking is not allowed in server mode."); - _untrack(p_scene_id, p_obj); -} - -void MultiplayerReplicator::_untrack(const ResourceUID::ID &p_scene_id, Object *p_obj) { - ERR_FAIL_COND(!p_obj); - ERR_FAIL_COND(!replications.has(p_scene_id)); - if (tracked_objects.has(p_scene_id)) { - tracked_objects[p_scene_id].erase(p_obj->get_instance_id()); - } -} - -Error MultiplayerReplicator::sync_all(const ResourceUID::ID &p_scene_id, int p_peer) { - ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER); - if (!tracked_objects.has(p_scene_id)) { - return OK; - } - const SceneConfig &cfg = replications[p_scene_id]; - if (cfg.on_sync_send.is_valid()) { - Array objs; - if (tracked_objects.has(p_scene_id)) { - objs.resize(tracked_objects[p_scene_id].size()); - int idx = 0; - for (const ObjectID &obj_id : tracked_objects[p_scene_id]) { - objs[idx++] = ObjectDB::get_instance(obj_id); - } - } - Variant args[3] = { p_scene_id, objs, p_peer }; - Variant *argp[3] = { args, &args[1], &args[2] }; - Callable::CallError ce; - Variant ret; - cfg.on_sync_send.call((const Variant **)argp, 3, ret, ce); - ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, FAILED, "Custom sync function failed"); - return OK; - } else if (cfg.sync_properties.size()) { - return _sync_all_default(p_scene_id, p_peer); - } - return OK; -} - -Error MultiplayerReplicator::send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_transfer_mode, int p_channel) { - ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED); - ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER); - const SceneConfig &cfg = replications[p_scene_id]; - ERR_FAIL_COND_V_MSG(!cfg.on_sync_send.is_valid(), ERR_UNCONFIGURED, "Sending raw sync messages is only available with custom functions"); - MAKE_ROOM(SYNC_CMD_OFFSET + p_data.size()); - uint8_t *ptr = packet_cache.ptrw(); - ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC; - encode_uint64(p_scene_id, &ptr[1]); - if (p_data.size()) { - memcpy(&ptr[SYNC_CMD_OFFSET], p_data.ptr(), p_data.size()); - } - Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); - peer->set_target_peer(p_peer_id); - peer->set_transfer_channel(p_channel); - peer->set_transfer_mode(p_transfer_mode); - return peer->put_packet(ptr, SYNC_CMD_OFFSET + p_data.size()); -} - -void MultiplayerReplicator::clear() { - tracked_objects.clear(); - replicated_nodes.clear(); -} - -void MultiplayerReplicator::_bind_methods() { - ClassDB::bind_method(D_METHOD("spawn_config", "scene_id", "spawn_mode", "properties", "custom_send", "custom_receive"), &MultiplayerReplicator::spawn_config, DEFVAL(TypedArray<StringName>()), DEFVAL(Callable()), DEFVAL(Callable())); - ClassDB::bind_method(D_METHOD("sync_config", "scene_id", "interval", "properties", "custom_send", "custom_receive"), &MultiplayerReplicator::sync_config, DEFVAL(TypedArray<StringName>()), DEFVAL(Callable()), DEFVAL(Callable())); - ClassDB::bind_method(D_METHOD("despawn", "scene_id", "object", "peer_id"), &MultiplayerReplicator::despawn, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("spawn", "scene_id", "object", "peer_id"), &MultiplayerReplicator::spawn, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("send_despawn", "peer_id", "scene_id", "data", "path"), &MultiplayerReplicator::send_despawn, DEFVAL(Variant()), DEFVAL(NodePath())); - ClassDB::bind_method(D_METHOD("send_spawn", "peer_id", "scene_id", "data", "path"), &MultiplayerReplicator::send_spawn, DEFVAL(Variant()), DEFVAL(NodePath())); - ClassDB::bind_method(D_METHOD("send_sync", "peer_id", "scene_id", "data", "transfer_mode", "channel"), &MultiplayerReplicator::send_sync, DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("sync_all", "scene_id", "peer_id"), &MultiplayerReplicator::sync_all, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("track", "scene_id", "object"), &MultiplayerReplicator::track); - ClassDB::bind_method(D_METHOD("untrack", "scene_id", "object"), &MultiplayerReplicator::untrack); - ClassDB::bind_method(D_METHOD("encode_state", "scene_id", "object", "initial"), &MultiplayerReplicator::encode_state, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("decode_state", "scene_id", "object", "data", "initial"), &MultiplayerReplicator::decode_state, DEFVAL(true)); - - ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("despawn_requested", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "parent", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data"))); - ADD_SIGNAL(MethodInfo("spawn_requested", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "parent", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data"))); - ADD_SIGNAL(MethodInfo("replicated_instance_added", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("replicated_instance_removed", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - - BIND_ENUM_CONSTANT(REPLICATION_MODE_NONE); - BIND_ENUM_CONSTANT(REPLICATION_MODE_SERVER); - BIND_ENUM_CONSTANT(REPLICATION_MODE_CUSTOM); -} diff --git a/core/multiplayer/multiplayer_replicator.h b/core/multiplayer/multiplayer_replicator.h deleted file mode 100644 index a9cd6e211e..0000000000 --- a/core/multiplayer/multiplayer_replicator.h +++ /dev/null @@ -1,138 +0,0 @@ -/*************************************************************************/ -/* multiplayer_replicator.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef MULTIPLAYER_REPLICATOR_H -#define MULTIPLAYER_REPLICATOR_H - -#include "core/multiplayer/multiplayer_api.h" - -#include "core/io/resource_uid.h" -#include "core/templates/hash_map.h" -#include "core/variant/typed_array.h" - -class MultiplayerReplicator : public Object { - GDCLASS(MultiplayerReplicator, Object); - -public: - enum { - SPAWN_CMD_OFFSET = 9, - SYNC_CMD_OFFSET = 9, - }; - - enum ReplicationMode { - REPLICATION_MODE_NONE, - REPLICATION_MODE_SERVER, - REPLICATION_MODE_CUSTOM, - }; - - struct SceneConfig { - ReplicationMode mode; - uint64_t sync_interval = 0; - uint64_t sync_last = 0; - uint8_t sync_recv = 0; - List<StringName> properties; - List<StringName> sync_properties; - Callable on_spawn_despawn_send; - Callable on_spawn_despawn_receive; - Callable on_sync_send; - Callable on_sync_receive; - }; - -protected: - static void _bind_methods(); - -private: - enum { - BYTE_OR_ZERO_SHIFT = MultiplayerAPI::CMD_FLAG_0_SHIFT, - }; - - enum { - BYTE_OR_ZERO_FLAG = 1 << BYTE_OR_ZERO_SHIFT, - }; - - MultiplayerAPI *multiplayer = nullptr; - Vector<uint8_t> packet_cache; - Map<ResourceUID::ID, SceneConfig> replications; - Map<ObjectID, ResourceUID::ID> replicated_nodes; - HashMap<ResourceUID::ID, List<ObjectID>> tracked_objects; - - // Encoding - Error _get_state(const List<StringName> &p_properties, const Object *p_obj, List<Variant> &r_variant); - Error _encode_state(const List<Variant> &p_variants, uint8_t *p_buffer, int &r_len, bool *r_raw = nullptr); - Error _decode_state(const List<StringName> &p_cfg, Object *p_obj, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw = false); - - // Spawn - Error _spawn_despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer, bool p_spawn); - Error _send_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, bool p_spawn); - void _process_default_spawn_despawn(int p_from, const ResourceUID::ID &p_scene_id, const uint8_t *p_packet, int p_packet_len, bool p_spawn); - Error _send_default_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, Object *p_obj, const NodePath &p_path, bool p_spawn); - - // Sync - void _process_default_sync(const ResourceUID::ID &p_id, const uint8_t *p_packet, int p_packet_len); - Error _sync_all_default(const ResourceUID::ID &p_scene_id, int p_peer); - void _track(const ResourceUID::ID &p_scene_id, Object *p_object); - void _untrack(const ResourceUID::ID &p_scene_id, Object *p_object); - -public: - void clear(); - - // Encoding - PackedByteArray encode_state(const ResourceUID::ID &p_scene_id, const Object *p_node, bool p_initial); - Error decode_state(const ResourceUID::ID &p_scene_id, Object *p_node, PackedByteArray p_data, bool p_initial); - - // Spawn - Error spawn_config(const ResourceUID::ID &p_id, ReplicationMode p_mode, const TypedArray<StringName> &p_props = TypedArray<StringName>(), const Callable &p_on_send = Callable(), const Callable &p_on_recv = Callable()); - Error spawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer = 0); - Error despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer = 0); - Error send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data = Variant(), const NodePath &p_path = NodePath()); - Error send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data = Variant(), const NodePath &p_path = NodePath()); - - // Sync - Error sync_config(const ResourceUID::ID &p_id, uint64_t p_interval, const TypedArray<StringName> &p_props = TypedArray<StringName>(), const Callable &p_on_send = Callable(), const Callable &p_on_recv = Callable()); - Error sync_all(const ResourceUID::ID &p_scene_id, int p_peer); - Error send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_mode, int p_channel); - void track(const ResourceUID::ID &p_scene_id, Object *p_object); - void untrack(const ResourceUID::ID &p_scene_id, Object *p_object); - - // Used by MultiplayerAPI - void spawn_all(int p_peer); - void process_spawn_despawn(int p_from, const uint8_t *p_packet, int p_packet_len, bool p_spawn); - void process_sync(int p_from, const uint8_t *p_packet, int p_packet_len); - void scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter); - void poll(); - - MultiplayerReplicator(MultiplayerAPI *p_multiplayer) { - multiplayer = p_multiplayer; - } -}; - -VARIANT_ENUM_CAST(MultiplayerReplicator::ReplicationMode); - -#endif // MULTIPLAYER_REPLICATOR_H diff --git a/core/multiplayer/rpc_manager.cpp b/core/multiplayer/rpc_manager.cpp deleted file mode 100644 index 7736637349..0000000000 --- a/core/multiplayer/rpc_manager.cpp +++ /dev/null @@ -1,525 +0,0 @@ -/*************************************************************************/ -/* rpc_manager.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 deleted file mode 100644 index 00bd1f9cb0..0000000000 --- a/core/multiplayer/rpc_manager.h +++ /dev/null @@ -1,89 +0,0 @@ -/*************************************************************************/ -/* rpc_manager.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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/callable_method_pointer.h b/core/object/callable_method_pointer.h index 53410a9acf..3cd9ad3819 100644 --- a/core/object/callable_method_pointer.h +++ b/core/object/callable_method_pointer.h @@ -51,6 +51,14 @@ protected: void _setup(uint32_t *p_base_ptr, uint32_t p_ptr_size); public: + virtual StringName get_method() const { +#ifdef DEBUG_METHODS_ENABLED + return StringName(text); +#else + return StringName(); +#endif + } + #ifdef DEBUG_METHODS_ENABLED void set_text(const char *p_text) { text = p_text; diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 03774bc36a..c29316c089 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -732,7 +732,7 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName String enum_name = p_enum; if (!enum_name.is_empty()) { - if (enum_name.find(".") != -1) { + if (enum_name.contains(".")) { enum_name = enum_name.get_slicec('.', 1); } @@ -1007,20 +1007,30 @@ bool ClassDB::get_signal(const StringName &p_class, const StringName &p_signal, return false; } -void ClassDB::add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix) { +void ClassDB::add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix, int p_indent_depth) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ERR_FAIL_COND(!type); - type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_GROUP)); + String prefix = p_prefix; + if (p_indent_depth > 0) { + prefix = vformat("%s,%d", p_prefix, p_indent_depth); + } + + type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, prefix, PROPERTY_USAGE_GROUP)); } -void ClassDB::add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix) { +void ClassDB::add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix, int p_indent_depth) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ERR_FAIL_COND(!type); - type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_SUBGROUP)); + String prefix = p_prefix; + if (p_indent_depth > 0) { + prefix = vformat("%s,%d", p_prefix, p_indent_depth); + } + + type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, 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) { @@ -1632,7 +1642,8 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con // Some properties may have an instantiated Object as default value, // (like Path2D's `curve` used to have), but that's not a good practice. // Instead, those properties should use PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT - // to be auto-instantiated when created in the editor. + // to be auto-instantiated when created in the editor with the following method: + // EditorNode::get_editor_data().instantiate_object_properties(obj); if (var.get_type() == Variant::OBJECT) { Object *obj = var.get_validated_object(); if (obj) { diff --git a/core/object/class_db.h b/core/object/class_db.h index 5adf1a59a4..5d258a29bf 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -328,8 +328,8 @@ public: static bool get_signal(const StringName &p_class, const StringName &p_signal, MethodInfo *r_signal); static void get_signal_list(const StringName &p_class, List<MethodInfo> *p_signals, bool p_no_inheritance = false); - static void add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix = ""); - static void add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix = ""); + static void add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix = "", int p_indent_depth = 0); + static void add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix = "", int p_indent_depth = 0); 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_DEFAULT); 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); diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py index e961745d96..5de1b49026 100644 --- a/core/object/make_virtuals.py +++ b/core/object/make_virtuals.py @@ -123,9 +123,9 @@ def generate_version(argcount, const=False, returns=False): callargtext += "," callargtext += " m_ret& r_ret" s = s.replace("$CALLSIBEGIN", "Variant ret = ") - s = s.replace("$CALLSIRET", "r_ret = ret;") + s = s.replace("$CALLSIRET", "r_ret = VariantCaster<m_ret>::cast(ret);") s = s.replace("$CALLPTRRETPASS", "&ret") - s = s.replace("$CALLPTRRET", "r_ret = ret;") + s = s.replace("$CALLPTRRET", "r_ret = (m_ret)ret;") else: s = s.replace("$CALLSIBEGIN", "") s = s.replace("$CALLSIRET", "") diff --git a/core/object/object.h b/core/object/object.h index 63130a1aef..be360703bc 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -142,7 +142,9 @@ enum PropertyUsageFlags { #define ADD_PROPERTYI(m_property, m_setter, m_getter, m_index) ::ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter), m_index) #define ADD_PROPERTY_DEFAULT(m_property, m_default) ::ClassDB::set_property_default_value(get_class_static(), m_property, m_default) #define ADD_GROUP(m_name, m_prefix) ::ClassDB::add_property_group(get_class_static(), m_name, m_prefix) +#define ADD_GROUP_INDENT(m_name, m_prefix, m_depth) ::ClassDB::add_property_group(get_class_static(), m_name, m_prefix, m_depth) #define ADD_SUBGROUP(m_name, m_prefix) ::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix) +#define ADD_SUBGROUP_INDENT(m_name, m_prefix, m_depth) ::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix, m_depth) #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) @@ -700,8 +702,9 @@ public: static String get_category_static() { return String(); } virtual String get_class() const { - if (_extension) + if (_extension) { return _extension->class_name.operator String(); + } return "Object"; } virtual String get_save_class() const { return get_class(); } //class stored when saving diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index a18ec4d6ad..a4b6a589f3 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -37,6 +37,7 @@ #include "core/crypto/aes_context.h" #include "core/crypto/crypto.h" #include "core/crypto/hashing_context.h" +#include "core/debugger/engine_profiler.h" #include "core/extension/native_extension.h" #include "core/extension/native_extension_manager.h" #include "core/input/input.h" @@ -69,7 +70,6 @@ #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" @@ -200,7 +200,6 @@ void register_core_types() { GDREGISTER_VIRTUAL_CLASS(MultiplayerPeer); GDREGISTER_CLASS(MultiplayerPeerExtension); - GDREGISTER_VIRTUAL_CLASS(MultiplayerReplicator); GDREGISTER_CLASS(MultiplayerAPI); GDREGISTER_CLASS(MainLoop); GDREGISTER_CLASS(Translation); @@ -239,6 +238,8 @@ void register_core_types() { GDREGISTER_VIRTUAL_CLASS(ResourceUID); + GDREGISTER_CLASS(EngineProfiler); + resource_uid = memnew(ResourceUID); native_extension_manager = memnew(NativeExtensionManager); diff --git a/core/string/char_utils.h b/core/string/char_utils.h new file mode 100644 index 0000000000..0afd058f01 --- /dev/null +++ b/core/string/char_utils.h @@ -0,0 +1,92 @@ +/*************************************************************************/ +/* char_utils.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 CHAR_UTILS_H +#define CHAR_UTILS_H + +#include "core/typedefs.h" + +static _FORCE_INLINE_ bool is_ascii_upper_case(char32_t c) { + return (c >= 'A' && c <= 'Z'); +} + +static _FORCE_INLINE_ bool is_ascii_lower_case(char32_t c) { + return (c >= 'a' && c <= 'z'); +} + +static _FORCE_INLINE_ bool is_digit(char32_t c) { + return (c >= '0' && c <= '9'); +} + +static _FORCE_INLINE_ bool is_hex_digit(char32_t c) { + return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + +static _FORCE_INLINE_ bool is_binary_digit(char32_t c) { + return (c == '0' || c == '1'); +} + +static _FORCE_INLINE_ bool is_ascii_char(char32_t c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static _FORCE_INLINE_ bool is_ascii_alphanumeric_char(char32_t c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); +} + +static _FORCE_INLINE_ bool is_ascii_identifier_char(char32_t c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; +} + +static _FORCE_INLINE_ bool is_symbol(char32_t c) { + return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' '); +} + +static _FORCE_INLINE_ bool is_control(char32_t p_char) { + return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009f); +} + +static _FORCE_INLINE_ bool is_whitespace(char32_t p_char) { + return (p_char == ' ') || (p_char == 0x00a0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085); +} + +static _FORCE_INLINE_ bool is_linebreak(char32_t p_char) { + return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029); +} + +static _FORCE_INLINE_ bool is_punct(char32_t p_char) { + return (p_char >= ' ' && p_char <= '/') || (p_char >= ':' && p_char <= '@') || (p_char >= '[' && p_char <= '^') || (p_char == '`') || (p_char >= '{' && p_char <= '~') || (p_char >= 0x2000 && p_char <= 0x206f) || (p_char >= 0x3000 && p_char <= 0x303f); +} + +static _FORCE_INLINE_ bool is_underscore(char32_t p_char) { + return (p_char == '_'); +} + +#endif // CHAR_UTILS_H diff --git a/core/string/locales.h b/core/string/locales.h index 9ef6dee5ac..32d6608ec2 100644 --- a/core/string/locales.h +++ b/core/string/locales.h @@ -42,6 +42,7 @@ static const char *locale_renames[][2] = { { "in", "id" }, // Indonesian { "iw", "he" }, // Hebrew { "no", "nb" }, // Norwegian Bokmål + { "C", "en" }, // Locale is not set, fallback to English. { nullptr, nullptr } }; diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp index 61742ac582..11674629fc 100644 --- a/core/string/string_name.cpp +++ b/core/string/string_name.cpp @@ -84,12 +84,15 @@ void StringName::cleanup() { for (int i = 0; i < STRING_TABLE_LEN; i++) { while (_table[i]) { _Data *d = _table[i]; - lost_strings++; - if (d->static_count.get() != d->refcount.get() && OS::get_singleton()->is_stdout_verbose()) { - if (d->cname) { - print_line("Orphan StringName: " + String(d->cname)); - } else { - print_line("Orphan StringName: " + String(d->name)); + if (d->static_count.get() != d->refcount.get()) { + lost_strings++; + + if (OS::get_singleton()->is_stdout_verbose()) { + if (d->cname) { + print_line("Orphan StringName: " + String(d->cname)); + } else { + print_line("Orphan StringName: " + String(d->name)); + } } } diff --git a/core/string/string_name.h b/core/string/string_name.h index 9653d2b4cf..6f08d32981 100644 --- a/core/string/string_name.h +++ b/core/string/string_name.h @@ -181,6 +181,18 @@ bool operator!=(const char *p_name, const StringName &p_string_name); StringName _scs_create(const char *p_chr, bool p_static = false); +/* + * The SNAME macro is used to speed up StringName creation, as it allows caching it after the first usage in a very efficient way. + * It should NOT be used everywhere, but instead in places where high performance is required and the creation of a StringName + * can be costly. Places where it should be used are: + * - Control::get_theme_*(<name> and Window::get_theme_*(<name> functions. + * - emit_signal(<name>,..) function + * - call_deferred(<name>,..) function + * - Comparisons to a StringName in overridden _set and _get methods. + * + * Use in places that can be called hundreds of times per frame (or more) is recommended, but this situation is very rare. If in doubt, do not use. + */ + #define SNAME(m_arg) ([]() -> const StringName & { static StringName sname = _scs_create(m_arg, true); return sname; })() #endif // STRING_NAME_H diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 674098b06c..eeac8b0acf 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -213,14 +213,6 @@ static _character_accent_pair _character_to_accented[] = { { 'z', U"ź" }, }; -static _FORCE_INLINE_ bool is_upper_case(char32_t c) { - return (c >= 'A' && c <= 'Z'); -} - -static _FORCE_INLINE_ bool is_lower_case(char32_t c) { - return (c >= 'a' && c <= 'z'); -} - Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info; Map<String, String> TranslationServer::language_map; @@ -309,15 +301,15 @@ String TranslationServer::standardize_locale(const String &p_locale) const { Vector<String> locale_elements = univ_locale.get_slice("@", 0).split("_"); lang = locale_elements[0]; if (locale_elements.size() >= 2) { - if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) { + if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) { script = locale_elements[1]; } - if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) { + if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) { country = locale_elements[1]; } } if (locale_elements.size() >= 3) { - if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) { + if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) { country = locale_elements[2]; } else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang) { variant = locale_elements[2].to_lower(); @@ -434,15 +426,15 @@ String TranslationServer::get_locale_name(const String &p_locale) const { Vector<String> locale_elements = locale.split("_"); lang = locale_elements[0]; if (locale_elements.size() >= 2) { - if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) { + if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) { script = locale_elements[1]; } - if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) { + if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) { country = locale_elements[1]; } } if (locale_elements.size() >= 3) { - if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) { + if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) { country = locale_elements[2]; } } @@ -544,7 +536,7 @@ Ref<Translation> TranslationServer::get_translation_object(const String &p_local String l = t->get_locale(); int score = compare_locales(p_locale, l); - if (score > best_score) { + if (score > 0 && score >= best_score) { res = t; best_score = score; if (score == 10) { @@ -566,8 +558,6 @@ StringName TranslationServer::translate(const StringName &p_message, const Strin return p_message; } - ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid."); - StringName res = _get_message_from_translations(p_message, p_context, locale, false); if (!res && fallback.length() >= 2) { @@ -589,8 +579,6 @@ StringName TranslationServer::translate_plural(const StringName &p_message, cons return p_message_plural; } - ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid."); - StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n); if (!res && fallback.length() >= 2) { @@ -617,13 +605,17 @@ StringName TranslationServer::_get_message_from_translations(const StringName &p String l = t->get_locale(); int score = compare_locales(p_locale, l); - if (score > best_score) { + if (score > 0 && score >= best_score) { StringName r; if (!plural) { - res = t->get_message(p_message, p_context); + r = t->get_message(p_message, p_context); } else { - res = t->get_plural_message(p_message, p_message_plural, p_n, p_context); + r = t->get_plural_message(p_message, p_message_plural, p_n, p_context); + } + if (!r) { + continue; } + res = r; best_score = score; if (score == 10) { break; // Exact match, skip the rest. @@ -911,7 +903,7 @@ String TranslationServer::add_padding(String &p_message, int p_length) const { } const char32_t *TranslationServer::get_accented_version(char32_t p_character) const { - if (!((p_character >= 'a' && p_character <= 'z') || (p_character >= 'A' && p_character <= 'Z'))) { + if (!is_ascii_char(p_character)) { return nullptr; } @@ -933,6 +925,7 @@ bool TranslationServer::is_placeholder(String &p_message, int p_index) const { void TranslationServer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale); ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale); + ClassDB::bind_method(D_METHOD("get_tool_locale"), &TranslationServer::get_tool_locale); ClassDB::bind_method(D_METHOD("compare_locales", "locale_a", "locale_b"), &TranslationServer::compare_locales); ClassDB::bind_method(D_METHOD("standardize_locale", "locale"), &TranslationServer::standardize_locale); diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 658dbc98cf..c4edc8c086 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -54,34 +54,14 @@ static const int MAX_DECIMALS = 32; -static _FORCE_INLINE_ bool is_digit(char32_t c) { - return (c >= '0' && c <= '9'); -} - -static _FORCE_INLINE_ bool is_hex_digit(char32_t c) { - return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); -} - -static _FORCE_INLINE_ bool is_upper_case(char32_t c) { - return (c >= 'A' && c <= 'Z'); -} - -static _FORCE_INLINE_ bool is_lower_case(char32_t c) { - return (c >= 'a' && c <= 'z'); -} - static _FORCE_INLINE_ char32_t lower_case(char32_t c) { - return (is_upper_case(c) ? (c + ('a' - 'A')) : c); + return (is_ascii_upper_case(c) ? (c + ('a' - 'A')) : c); } const char CharString::_null = 0; const char16_t Char16String::_null = 0; const char32_t String::_null = 0; -bool is_symbol(char32_t c) { - return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' '); -} - bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end) { const String &s = p_s; int beg = CLAMP(p_col, 0, s.length()); @@ -974,21 +954,21 @@ String String::camelcase_to_underscore(bool lowercase) const { int start_index = 0; for (int i = 1; i < this->size(); i++) { - bool is_upper = is_upper_case(cstr[i]); + bool is_upper = is_ascii_upper_case(cstr[i]); bool is_number = is_digit(cstr[i]); bool are_next_2_lower = false; bool is_next_lower = false; bool is_next_number = false; - bool was_precedent_upper = is_upper_case(cstr[i - 1]); + bool was_precedent_upper = is_ascii_upper_case(cstr[i - 1]); bool was_precedent_number = is_digit(cstr[i - 1]); if (i + 2 < this->size()) { - are_next_2_lower = is_lower_case(cstr[i + 1]) && is_lower_case(cstr[i + 2]); + are_next_2_lower = is_ascii_lower_case(cstr[i + 1]) && is_ascii_lower_case(cstr[i + 2]); } if (i + 1 < this->size()) { - is_next_lower = is_lower_case(cstr[i + 1]); + is_next_lower = is_ascii_lower_case(cstr[i + 1]); is_next_number = is_digit(cstr[i + 1]); } @@ -1632,7 +1612,7 @@ String String::utf8(const char *p_utf8, int p_len) { } bool String::parse_utf8(const char *p_utf8, int p_len) { -#define _UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-8?"); +#define UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-8?"); if (!p_utf8) { return true; @@ -1673,12 +1653,12 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } else if ((c & 0xf8) == 0xf0) { skip = 3; } else { - _UNICERROR("invalid skip at " + num_int64(cstr_size)); + UNICERROR("invalid skip at " + num_int64(cstr_size)); return true; //invalid utf8 } if (skip == 1 && (c & 0x1e) == 0) { - _UNICERROR("overlong rejected at " + num_int64(cstr_size)); + UNICERROR("overlong rejected at " + num_int64(cstr_size)); return true; //reject overlong } @@ -1693,7 +1673,7 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } if (skip) { - _UNICERROR("no space left"); + UNICERROR("no space left"); return true; //not enough space } } @@ -1720,17 +1700,17 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } else if ((*p_utf8 & 0xf8) == 0xf0) { len = 4; } else { - _UNICERROR("invalid len"); + UNICERROR("invalid len"); return true; //invalid UTF8 } if (len > cstr_size) { - _UNICERROR("no space left"); + UNICERROR("no space left"); return true; //not enough space } if (len == 2 && (*p_utf8 & 0x1E) == 0) { - _UNICERROR("no space left"); + UNICERROR("no space left"); return true; //reject overlong } @@ -1745,18 +1725,18 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { for (int i = 1; i < len; i++) { if ((p_utf8[i] & 0xc0) != 0x80) { - _UNICERROR("invalid utf8"); + UNICERROR("invalid utf8"); return true; //invalid utf8 } if (unichar == 0 && i == 2 && ((p_utf8[i] & 0x7f) >> (7 - len)) == 0) { - _UNICERROR("invalid utf8 overlong"); + UNICERROR("invalid utf8 overlong"); return true; //no overlong } unichar = (unichar << 6) | (p_utf8[i] & 0x3f); } } if (unichar >= 0xd800 && unichar <= 0xdfff) { - _UNICERROR("invalid code point"); + UNICERROR("invalid code point"); return CharString(); } @@ -1766,7 +1746,7 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } return false; -#undef _UNICERROR +#undef UNICERROR } CharString String::utf8() const { @@ -1840,7 +1820,7 @@ String String::utf16(const char16_t *p_utf16, int p_len) { } bool String::parse_utf16(const char16_t *p_utf16, int p_len) { -#define _UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-16?"); +#define UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-16?"); if (!p_utf16) { return true; @@ -1880,7 +1860,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) { if ((c & 0xfffffc00) == 0xd800) { skip = 1; // lead surrogate } else if ((c & 0xfffffc00) == 0xdc00) { - _UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size)); + UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size)); return true; // invalid UTF16 } else { skip = 0; @@ -1890,7 +1870,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) { if ((c & 0xfffffc00) == 0xdc00) { // trail surrogate --skip; } else { - _UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size)); + UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size)); return true; // invalid UTF16 } } @@ -1900,7 +1880,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) { } if (skip) { - _UNICERROR("no space left"); + UNICERROR("no space left"); return true; // not enough space } } @@ -1925,7 +1905,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) { } if (len > cstr_size) { - _UNICERROR("no space left"); + UNICERROR("no space left"); return true; //not enough space } @@ -1943,7 +1923,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) { } return false; -#undef _UNICERROR +#undef UNICERROR } Char16String String::utf16() const { @@ -2212,7 +2192,7 @@ bool String::is_numeric() const { return false; } dot = true; - } else if (c < '0' || c > '9') { + } else if (!is_digit(c)) { return false; } } @@ -3080,7 +3060,7 @@ bool String::is_subsequence_of(const String &p_string) const { return _base_is_subsequence_of(p_string, false); } -bool String::is_subsequence_ofi(const String &p_string) const { +bool String::is_subsequence_ofn(const String &p_string) const { return _base_is_subsequence_of(p_string, true); } @@ -3558,6 +3538,10 @@ String String::rstrip(const String &p_chars) const { return substr(0, end + 1); } +bool String::is_network_share_path() const { + return begins_with("//") || begins_with("\\\\"); +} + String String::simplify_path() const { String s = *this; String drive; @@ -3570,6 +3554,9 @@ String String::simplify_path() const { } else if (s.begins_with("user://")) { drive = "user://"; s = s.substr(7, s.length()); + } else if (is_network_share_path()) { + drive = s.substr(0, 2); + s = s.substr(2, s.length() - 2); } else if (s.begins_with("/") || s.begins_with("\\")) { drive = s.substr(0, 1); s = s.substr(1, s.length() - 1); @@ -3684,7 +3671,7 @@ bool String::is_valid_identifier() const { } } - bool valid_char = is_digit(str[i]) || is_lower_case(str[i]) || is_upper_case(str[i]) || str[i] == '_'; + bool valid_char = is_ascii_identifier_char(str[i]); if (!valid_char) { return false; @@ -3709,7 +3696,7 @@ String String::uri_encode() const { String res; for (int i = 0; i < temp.length(); ++i) { char ord = temp[i]; - if (ord == '.' || ord == '-' || ord == '_' || ord == '~' || is_lower_case(ord) || is_upper_case(ord) || is_digit(ord)) { + if (ord == '.' || ord == '-' || ord == '~' || is_ascii_identifier_char(ord)) { res += ord; } else { char h_Val[3]; @@ -3731,9 +3718,9 @@ String String::uri_decode() const { for (int i = 0; i < src.length(); ++i) { if (src[i] == '%' && i + 2 < src.length()) { char ord1 = src[i + 1]; - if (is_digit(ord1) || is_upper_case(ord1)) { + if (is_digit(ord1) || is_ascii_upper_case(ord1)) { char ord2 = src[i + 2]; - if (is_digit(ord2) || is_upper_case(ord2)) { + if (is_digit(ord2) || is_ascii_upper_case(ord2)) { char bytes[3] = { (char)ord1, (char)ord2, 0 }; res += (char)strtol(bytes, nullptr, 16); i += 2; @@ -3860,7 +3847,7 @@ static _FORCE_INLINE_ int _xml_unescape(const char32_t *p_src, int p_src_len, ch for (int i = 2; i < p_src_len; i++) { eat = i + 1; char32_t ct = p_src[i]; - if (ct == ';' || ct < '0' || ct > '9') { + if (ct == ';' || !is_digit(ct)) { break; } } @@ -3990,7 +3977,7 @@ String String::pad_zeros(int p_digits) const { int begin = 0; - while (begin < end && (s[begin] < '0' || s[begin] > '9')) { + while (begin < end && !is_digit(s[begin])) { begin++; } @@ -4035,7 +4022,7 @@ bool String::is_valid_int() const { } for (int i = from; i < len; i++) { - if (operator[](i) < '0' || operator[](i) > '9') { + if (!is_digit(operator[](i))) { return false; // no start with number plz } } @@ -4271,13 +4258,13 @@ bool String::is_relative_path() const { String String::get_base_dir() const { int end = 0; - // url scheme style base + // URL scheme style base. int basepos = find("://"); if (basepos != -1) { end = basepos + 3; } - // windows top level directory base + // Windows top level directory base. if (end == 0) { basepos = find(":/"); if (basepos == -1) { @@ -4288,7 +4275,24 @@ String String::get_base_dir() const { } } - // unix root directory base + // Windows UNC network share path. + if (end == 0) { + if (is_network_share_path()) { + basepos = find("/", 2); + if (basepos == -1) { + basepos = find("\\", 2); + } + int servpos = find("/", basepos + 1); + if (servpos == -1) { + servpos = find("\\", basepos + 1); + } + if (servpos != -1) { + end = servpos + 1; + } + } + } + + // Unix root directory base. if (end == 0) { if (begins_with("/")) { end = 1; diff --git a/core/string/ustring.h b/core/string/ustring.h index 4840c236c0..1d302b65a7 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -32,6 +32,7 @@ #define USTRING_GODOT_H // Note: Renamed to avoid conflict with ICU header with the same name. +#include "core/string/char_utils.h" #include "core/templates/cowdata.h" #include "core/templates/vector.h" #include "core/typedefs.h" @@ -285,7 +286,7 @@ public: bool ends_with(const String &p_string) const; bool is_enclosed_in(const String &p_string) const; bool is_subsequence_of(const String &p_string) const; - bool is_subsequence_ofi(const String &p_string) const; + bool is_subsequence_ofn(const String &p_string) const; bool is_quoted() const; Vector<String> bigrams() const; float similarity(const String &p_string) const; @@ -394,6 +395,8 @@ public: Vector<uint8_t> sha256_buffer() const; _FORCE_INLINE_ bool is_empty() const { return length() == 0; } + _FORCE_INLINE_ bool contains(const char *p_str) const { return find(p_str) != -1; } + _FORCE_INLINE_ bool contains(const String &p_str) const { return find(p_str) != -1; } // path functions bool is_absolute_path() const; @@ -405,6 +408,7 @@ public: String get_file() const; static String humanize_size(uint64_t p_size); String simplify_path() const; + bool is_network_share_path() const; String xml_escape(bool p_escape_quotes = false) const; String xml_unescape() const; @@ -530,7 +534,6 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St String RTR(const String &p_text, const String &p_context = ""); String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = ""); -bool is_symbol(char32_t c); bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end); _FORCE_INLINE_ void sarray_add_str(Vector<String> &arr) { diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h index 3ed81e76fd..95632cdec2 100644 --- a/core/templates/rid_owner.h +++ b/core/templates/rid_owner.h @@ -292,43 +292,32 @@ public: _FORCE_INLINE_ uint32_t get_rid_count() const { return alloc_count; } - - _FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) { - ERR_FAIL_UNSIGNED_INDEX_V(p_index, alloc_count, nullptr); + void get_owned_list(List<RID> *p_owned) { if (THREAD_SAFE) { spin_lock.lock(); } - uint64_t idx = free_list_chunks[p_index / elements_in_chunk][p_index % elements_in_chunk]; - T *ptr = &chunks[idx / elements_in_chunk][idx % elements_in_chunk]; - if (THREAD_SAFE) { - spin_lock.unlock(); - } - return ptr; - } - - _FORCE_INLINE_ RID get_rid_by_index(uint32_t p_index) { - ERR_FAIL_INDEX_V(p_index, alloc_count, RID()); - if (THREAD_SAFE) { - spin_lock.lock(); + for (size_t i = 0; i < max_alloc; i++) { + uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk]; + if (validator != 0xFFFFFFFF) { + p_owned->push_back(_make_from_id((validator << 32) | i)); + } } - uint64_t idx = free_list_chunks[p_index / elements_in_chunk][p_index % elements_in_chunk]; - uint64_t validator = validator_chunks[idx / elements_in_chunk][idx % elements_in_chunk]; - - RID rid = _make_from_id((validator << 32) | idx); if (THREAD_SAFE) { spin_lock.unlock(); } - return rid; } - void get_owned_list(List<RID> *p_owned) { + //used for fast iteration in the elements or RIDs + void fill_owned_buffer(RID *p_rid_buffer) { if (THREAD_SAFE) { spin_lock.lock(); } + uint32_t idx = 0; for (size_t i = 0; i < max_alloc; i++) { uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk]; if (validator != 0xFFFFFFFF) { - p_owned->push_back(_make_from_id((validator << 32) | i)); + p_rid_buffer[idx] = _make_from_id((validator << 32) | i); + idx++; } } if (THREAD_SAFE) { @@ -425,18 +414,14 @@ public: return alloc.get_rid_count(); } - _FORCE_INLINE_ RID get_rid_by_index(uint32_t p_index) { - return alloc.get_rid_by_index(p_index); - } - - _FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) { - return *alloc.get_ptr_by_index(p_index); - } - _FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) { return alloc.get_owned_list(p_owned); } + void fill_owned_buffer(RID *p_rid_buffer) { + alloc.fill_owned_buffer(p_rid_buffer); + } + void set_description(const char *p_descrption) { alloc.set_description(p_descrption); } @@ -485,17 +470,12 @@ public: return alloc.get_rid_count(); } - _FORCE_INLINE_ RID get_rid_by_index(uint32_t p_index) { - return alloc.get_rid_by_index(p_index); - } - - _FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) { - return alloc.get_ptr_by_index(p_index); - } - _FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) { return alloc.get_owned_list(p_owned); } + void fill_owned_buffer(RID *p_rid_buffer) { + alloc.fill_owned_buffer(p_rid_buffer); + } void set_description(const char *p_descrption) { alloc.set_description(p_descrption); diff --git a/core/templates/vector.h b/core/templates/vector.h index bd4c6ade86..0877e04e01 100644 --- a/core/templates/vector.h +++ b/core/templates/vector.h @@ -95,9 +95,7 @@ public: void append_array(Vector<T> p_other); - bool has(const T &p_val) const { - return find(p_val, 0) != -1; - } + _FORCE_INLINE_ bool has(const T &p_val) const { return find(p_val) != -1; } template <class C> void sort_custom() { diff --git a/core/variant/array.cpp b/core/variant/array.cpp index 3d2f337442..1b39558dff 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -631,7 +631,7 @@ Variant Array::max() const { } const void *Array::id() const { - return _p->array.ptr(); + return _p; } Array::Array(const Array &p_from, uint32_t p_type, const StringName &p_class_name, const Variant &p_script) { diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index 14f49d530c..b6fdb4d902 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -44,24 +44,42 @@ #include <stdio.h> +// Variant cannot define an implicit cast operator for every Object subclass, so the +// casting is done here, to allow binding methods with parameters more specific than Object * + template <class T> struct VariantCaster { static _FORCE_INLINE_ T cast(const Variant &p_variant) { - return p_variant; + using TStripped = std::remove_pointer_t<T>; + if constexpr (std::is_base_of<Object, TStripped>::value) { + return Object::cast_to<TStripped>(p_variant); + } else { + return p_variant; + } } }; template <class T> struct VariantCaster<T &> { static _FORCE_INLINE_ T cast(const Variant &p_variant) { - return p_variant; + using TStripped = std::remove_pointer_t<T>; + if constexpr (std::is_base_of<Object, TStripped>::value) { + return Object::cast_to<TStripped>(p_variant); + } else { + return p_variant; + } } }; template <class T> struct VariantCaster<const T &> { static _FORCE_INLINE_ T cast(const Variant &p_variant) { - return p_variant; + using TStripped = std::remove_pointer_t<T>; + if constexpr (std::is_base_of<Object, TStripped>::value) { + return Object::cast_to<TStripped>(p_variant); + } else { + return p_variant; + } } }; @@ -135,7 +153,13 @@ struct PtrToArg<char32_t> { template <typename T> struct VariantObjectClassChecker { static _FORCE_INLINE_ bool check(const Variant &p_variant) { - return true; + using TStripped = std::remove_pointer_t<T>; + if constexpr (std::is_base_of<Object, TStripped>::value) { + Object *obj = p_variant; + return Object::cast_to<TStripped>(p_variant) || !obj; + } else { + return true; + } } }; @@ -151,24 +175,6 @@ struct VariantObjectClassChecker<const Ref<T> &> { } }; -template <> -struct VariantObjectClassChecker<Node *> { - static _FORCE_INLINE_ bool check(const Variant &p_variant) { - Object *obj = p_variant; - Node *node = p_variant; - return node || !obj; - } -}; - -template <> -struct VariantObjectClassChecker<Control *> { - static _FORCE_INLINE_ bool check(const Variant &p_variant) { - Object *obj = p_variant; - Control *control = p_variant; - return control || !obj; - } -}; - #ifdef DEBUG_METHODS_ENABLED template <class T> diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp index c6a67f01d5..27792ce111 100644 --- a/core/variant/callable.cpp +++ b/core/variant/callable.cpp @@ -114,8 +114,9 @@ ObjectID Callable::get_object_id() const { } StringName Callable::get_method() const { - ERR_FAIL_COND_V_MSG(is_custom(), StringName(), - vformat("Can't get method on CallableCustom \"%s\".", operator String())); + if (is_custom()) { + return get_custom()->get_method(); + } return method; } @@ -310,6 +311,10 @@ Callable::~Callable() { } } +StringName CallableCustom::get_method() const { + ERR_FAIL_V_MSG(StringName(), vformat("Can't get method on CallableCustom \"%s\".", get_as_text())); +} + void CallableCustom::rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const { r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; r_call_error.argument = 0; diff --git a/core/variant/callable.h b/core/variant/callable.h index 855ffa9129..c61870f194 100644 --- a/core/variant/callable.h +++ b/core/variant/callable.h @@ -125,6 +125,7 @@ public: virtual String get_as_text() const = 0; virtual CompareEqualFunc get_compare_equal_func() const = 0; virtual CompareLessFunc get_compare_less_func() const = 0; + virtual StringName get_method() const; virtual ObjectID get_object() const = 0; //must always be able to provide an object virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const = 0; virtual void rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const; diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp index 4579621760..797e8afede 100644 --- a/core/variant/callable_bind.cpp +++ b/core/variant/callable_bind.cpp @@ -70,12 +70,19 @@ bool CallableCustomBind::_less_func(const CallableCustom *p_a, const CallableCus CallableCustom::CompareEqualFunc CallableCustomBind::get_compare_equal_func() const { return _equal_func; } + CallableCustom::CompareLessFunc CallableCustomBind::get_compare_less_func() const { return _less_func; } + +StringName CallableCustomBind::get_method() const { + return callable.get_method(); +} + ObjectID CallableCustomBind::get_object() const { return callable.get_object_id(); } + const Callable *CallableCustomBind::get_base_comparator() const { return &callable; } @@ -140,12 +147,19 @@ bool CallableCustomUnbind::_less_func(const CallableCustom *p_a, const CallableC CallableCustom::CompareEqualFunc CallableCustomUnbind::get_compare_equal_func() const { return _equal_func; } + CallableCustom::CompareLessFunc CallableCustomUnbind::get_compare_less_func() const { return _less_func; } + +StringName CallableCustomUnbind::get_method() const { + return callable.get_method(); +} + ObjectID CallableCustomUnbind::get_object() const { return callable.get_object_id(); } + const Callable *CallableCustomUnbind::get_base_comparator() const { return &callable; } diff --git a/core/variant/callable_bind.h b/core/variant/callable_bind.h index ac5797e05f..4f79a29629 100644 --- a/core/variant/callable_bind.h +++ b/core/variant/callable_bind.h @@ -47,6 +47,7 @@ public: virtual String get_as_text() const; virtual CompareEqualFunc get_compare_equal_func() const; virtual CompareLessFunc get_compare_less_func() const; + virtual StringName get_method() const; virtual ObjectID get_object() const; //must always be able to provide an object virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const; virtual const Callable *get_base_comparator() const; @@ -71,6 +72,7 @@ public: virtual String get_as_text() const; virtual CompareEqualFunc get_compare_equal_func() const; virtual CompareLessFunc get_compare_less_func() const; + virtual StringName get_method() const; virtual ObjectID get_object() const; //must always be able to provide an object virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const; virtual const Callable *get_base_comparator() const; diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp index cc04ae712b..0f2f8fc8ed 100644 --- a/core/variant/dictionary.cpp +++ b/core/variant/dictionary.cpp @@ -350,7 +350,7 @@ void Dictionary::operator=(const Dictionary &p_dictionary) { } const void *Dictionary::id() const { - return _p->variant_map.id(); + return _p; } Dictionary::Dictionary(const Dictionary &p_from) { diff --git a/core/variant/method_ptrcall.h b/core/variant/method_ptrcall.h index 75a93ac4c8..d0acf60c22 100644 --- a/core/variant/method_ptrcall.h +++ b/core/variant/method_ptrcall.h @@ -31,7 +31,6 @@ #ifndef METHOD_PTRCALL_H #define METHOD_PTRCALL_H -#include "core/math/transform_2d.h" #include "core/object/object_id.h" #include "core/typedefs.h" #include "core/variant/variant.h" diff --git a/core/variant/native_ptr.h b/core/variant/native_ptr.h index fe541c8d4b..8e9fbbc0a4 100644 --- a/core/variant/native_ptr.h +++ b/core/variant/native_ptr.h @@ -53,22 +53,36 @@ struct GDNativePtr { operator Variant() const { return uint64_t(data); } }; -#define GDVIRTUAL_NATIVE_PTR(m_type) \ - template <> \ - struct GDNativeConstPtr<const m_type> { \ - const m_type *data = nullptr; \ - GDNativeConstPtr(const m_type *p_assign) { data = p_assign; } \ - static const char *get_name() { return "const " #m_type; } \ - operator const m_type *() const { return data; } \ - operator Variant() const { return uint64_t(data); } \ - }; \ - template <> \ - struct GDNativePtr<m_type> { \ - m_type *data = nullptr; \ - GDNativePtr(m_type *p_assign) { data = p_assign; } \ - static const char *get_name() { return #m_type; } \ - operator m_type *() const { return data; } \ - operator Variant() const { return uint64_t(data); } \ +#define GDVIRTUAL_NATIVE_PTR(m_type) \ + template <> \ + struct GDNativeConstPtr<const m_type> { \ + const m_type *data = nullptr; \ + GDNativeConstPtr() {} \ + GDNativeConstPtr(const m_type *p_assign) { data = p_assign; } \ + static const char *get_name() { return "const " #m_type; } \ + operator const m_type *() const { return data; } \ + operator Variant() const { return uint64_t(data); } \ + }; \ + template <> \ + struct VariantCaster<GDNativeConstPtr<const m_type>> { \ + static _FORCE_INLINE_ GDNativeConstPtr<const m_type> cast(const Variant &p_variant) { \ + return GDNativeConstPtr<const m_type>((const m_type *)p_variant.operator uint64_t()); \ + } \ + }; \ + template <> \ + struct GDNativePtr<m_type> { \ + m_type *data = nullptr; \ + GDNativePtr() {} \ + GDNativePtr(m_type *p_assign) { data = p_assign; } \ + static const char *get_name() { return #m_type; } \ + operator m_type *() const { return data; } \ + operator Variant() const { return uint64_t(data); } \ + }; \ + template <> \ + struct VariantCaster<GDNativePtr<m_type>> { \ + static _FORCE_INLINE_ GDNativePtr<m_type> cast(const Variant &p_variant) { \ + return GDNativePtr<m_type>((m_type *)p_variant.operator uint64_t()); \ + } \ }; template <class T> diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index db198da54a..3d11ed6303 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -38,8 +38,6 @@ #include "core/math/math_funcs.h" #include "core/string/print_string.h" #include "core/variant/variant_parser.h" -#include "scene/gui/control.h" -#include "scene/main/node.h" String Variant::get_type_name(Variant::Type p_type) { switch (p_type) { @@ -1025,6 +1023,13 @@ bool Variant::is_null() const { } } +bool Variant::initialize_ref(Object *p_object) { + RefCounted *ref_counted = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_object)); + if (!ref_counted->init_ref()) { + return false; + } + return true; +} void Variant::reference(const Variant &p_variant) { switch (type) { case NIL: @@ -1692,8 +1697,6 @@ String Variant::stringify(int recursion_count) const { pairs.push_back(sp); } - pairs.sort(); - for (int i = 0; i < pairs.size(); i++) { if (i > 0) { str += ", "; @@ -2006,22 +2009,6 @@ Object *Variant::get_validated_object() const { } } -Variant::operator Node *() const { - if (type == OBJECT) { - return Object::cast_to<Node>(_get_obj().obj); - } else { - return nullptr; - } -} - -Variant::operator Control *() const { - if (type == OBJECT) { - return Object::cast_to<Control>(_get_obj().obj); - } else { - return nullptr; - } -} - Variant::operator Dictionary() const { if (type == DICTIONARY) { return *reinterpret_cast<const Dictionary *>(_data._mem); @@ -3256,7 +3243,7 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const return false; } -bool Variant::is_ref() const { +bool Variant::is_ref_counted() const { return type == OBJECT && _get_obj().id.is_ref_counted(); } @@ -3416,7 +3403,7 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method, } String class_name = p_base->get_class(); - Ref<Script> script = p_base->get_script(); + Ref<Resource> script = p_base->get_script(); if (script.is_valid() && script->get_path().is_resource_file()) { class_name += "(" + script->get_path().get_file() + ")"; } diff --git a/core/variant/variant.h b/core/variant/variant.h index 0860e7fdc3..836a67d942 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -39,8 +39,12 @@ #include "core/math/face3.h" #include "core/math/plane.h" #include "core/math/quaternion.h" +#include "core/math/rect2.h" +#include "core/math/rect2i.h" #include "core/math/transform_2d.h" #include "core/math/transform_3d.h" +#include "core/math/vector2.h" +#include "core/math/vector2i.h" #include "core/math/vector3.h" #include "core/math/vector3i.h" #include "core/object/object_id.h" @@ -53,8 +57,6 @@ #include "core/variant/dictionary.h" class Object; -class Node; // helper -class Control; // helper struct PropertyInfo; struct MethodInfo; @@ -214,6 +216,7 @@ private: } _data alignas(8); void reference(const Variant &p_variant); + static bool initialize_ref(Object *p_object); void _clear_internal(); @@ -287,7 +290,7 @@ public: static bool can_convert(Type p_type_from, Type p_type_to); static bool can_convert_strict(Type p_type_from, Type p_type_to); - bool is_ref() const; + bool is_ref_counted() const; _FORCE_INLINE_ bool is_num() const { return type == INT || type == FLOAT; } @@ -339,8 +342,6 @@ public: operator ::RID() const; operator Object *() const; - operator Node *() const; - operator Control *() const; operator Callable() const; operator Signal() const; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 8dd48a4c28..a5e89eec80 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1371,6 +1371,8 @@ static void _register_variant_builtin_methods() { bind_method(String, length, sarray(), varray()); bind_method(String, substr, sarray("from", "len"), varray(-1)); bind_method(String, get_slice, sarray("delimiter", "slice"), varray()); + bind_method(String, get_slicec, sarray("delimiter", "slice"), varray()); + bind_method(String, get_slice_count, sarray("delimiter"), varray()); bind_methodv(String, find, static_cast<int (String::*)(const String &, int) const>(&String::find), sarray("what", "from"), varray(0)); bind_method(String, count, sarray("what", "from", "to"), varray(0, 0)); bind_method(String, countn, sarray("what", "from", "to"), varray(0, 0)); @@ -1382,7 +1384,7 @@ static void _register_variant_builtin_methods() { bind_methodv(String, begins_with, static_cast<bool (String::*)(const String &) const>(&String::begins_with), sarray("text"), varray()); bind_method(String, ends_with, sarray("text"), varray()); bind_method(String, is_subsequence_of, sarray("text"), varray()); - bind_method(String, is_subsequence_ofi, sarray("text"), varray()); + bind_method(String, is_subsequence_ofn, sarray("text"), varray()); bind_method(String, bigrams, sarray(), varray()); bind_method(String, similarity, sarray("text"), varray()); @@ -1421,6 +1423,7 @@ static void _register_variant_builtin_methods() { bind_method(String, sha1_buffer, sarray(), varray()); bind_method(String, sha256_buffer, sarray(), varray()); bind_method(String, is_empty, sarray(), varray()); + bind_methodv(String, contains, static_cast<bool (String::*)(const String &) const>(&String::contains), sarray("what"), varray()); bind_method(String, is_absolute_path, sarray(), varray()); bind_method(String, is_relative_path, sarray(), varray()); @@ -1666,6 +1669,7 @@ static void _register_variant_builtin_methods() { /* RID */ + bind_method(RID, is_valid, sarray(), varray()); bind_method(RID, get_id, sarray(), varray()); /* NodePath */ @@ -1789,6 +1793,7 @@ static void _register_variant_builtin_methods() { bind_method(Transform3D, scaled, sarray("scale"), varray()); bind_method(Transform3D, translated, sarray("offset"), varray()); bind_method(Transform3D, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0))); + bind_method(Transform3D, sphere_interpolate_with, sarray("xform", "weight"), varray()); bind_method(Transform3D, interpolate_with, sarray("xform", "weight"), varray()); bind_method(Transform3D, is_equal_approx, sarray("xform"), varray()); diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index aaafa2f6b6..3696ffae60 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -111,6 +111,10 @@ public: } } + _FORCE_INLINE_ static bool initialize_ref(Object *object) { + return Variant::initialize_ref(object); + } + // Atomic types. _FORCE_INLINE_ static bool *get_bool(Variant *v) { return &v->_data._bool; } _FORCE_INLINE_ static const bool *get_bool(const Variant *v) { return &v->_data._bool; } @@ -1430,10 +1434,15 @@ struct VariantTypeConstructor<Object *> { _FORCE_INLINE_ static void variant_from_type(void *p_variant, void *p_value) { Variant *variant = reinterpret_cast<Variant *>(p_variant); VariantInitializer<Object *>::init(variant); - Object *value = *(reinterpret_cast<Object **>(p_value)); - if (value) { - VariantInternalAccessor<Object *>::set(variant, value); - VariantInternalAccessor<ObjectID>::set(variant, value->get_instance_id()); + Object *object = *(reinterpret_cast<Object **>(p_value)); + if (object) { + if (object->is_ref_counted()) { + if (!VariantInternal::initialize_ref(object)) { + return; + } + } + VariantInternalAccessor<Object *>::set(variant, object); + VariantInternalAccessor<ObjectID>::set(variant, object->get_instance_id()); } } diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp index e0ffcc9d11..cd1ae9f41f 100644 --- a/core/variant/variant_op.cpp +++ b/core/variant/variant_op.cpp @@ -45,6 +45,126 @@ void register_op(Variant::Operator p_op, Variant::Type p_type_a, Variant::Type p ptr_operator_evaluator_table[p_op][p_type_a][p_type_b] = T::ptr_evaluate; } +// Special cases that can't be done otherwise because of the forced casting to float. + +template <> +class OperatorEvaluatorMul<Vector2, Vector2i, double> { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Vector2i &a = *VariantGetInternalPtr<Vector2i>::get_ptr(&p_left); + const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_right); + *r_ret = Vector2(a.x, a.y) * b; + r_valid = true; + } + static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + *VariantGetInternalPtr<Vector2>::get_ptr(r_ret) = Vector2(VariantGetInternalPtr<Vector2i>::get_ptr(left)->x, VariantGetInternalPtr<Vector2i>::get_ptr(left)->y) * *VariantGetInternalPtr<double>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<Vector2>::encode(Vector2(PtrToArg<Vector2i>::convert(left).x, PtrToArg<Vector2i>::convert(left).y) * PtrToArg<double>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<Vector2>::VARIANT_TYPE; } +}; + +template <> +class OperatorEvaluatorMul<Vector2, double, Vector2i> { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Vector2i &a = *VariantGetInternalPtr<Vector2i>::get_ptr(&p_right); + const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_left); + *r_ret = Vector2(a.x, a.y) * b; + r_valid = true; + } + static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + *VariantGetInternalPtr<Vector2>::get_ptr(r_ret) = Vector2(VariantGetInternalPtr<Vector2i>::get_ptr(right)->x, VariantGetInternalPtr<Vector2i>::get_ptr(right)->y) * *VariantGetInternalPtr<double>::get_ptr(left); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<Vector2>::encode(Vector2(PtrToArg<Vector2i>::convert(right).x, PtrToArg<Vector2i>::convert(right).y) * PtrToArg<double>::convert(left), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<Vector2>::VARIANT_TYPE; } +}; + +template <> +class OperatorEvaluatorDivNZ<Vector2, Vector2i, double> { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Vector2i &a = *VariantGetInternalPtr<Vector2i>::get_ptr(&p_left); + const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_right); + if (unlikely(b == 0)) { + r_valid = false; + *r_ret = "Division by zero error"; + return; + } + *r_ret = Vector2(a.x, a.y) / b; + r_valid = true; + } + static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + *VariantGetInternalPtr<Vector2>::get_ptr(r_ret) = Vector2(VariantGetInternalPtr<Vector2i>::get_ptr(left)->x, VariantGetInternalPtr<Vector2i>::get_ptr(left)->y) / *VariantGetInternalPtr<double>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<Vector2>::encode(Vector2(PtrToArg<Vector2i>::convert(left).x, PtrToArg<Vector2i>::convert(left).y) / PtrToArg<double>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<Vector2>::VARIANT_TYPE; } +}; + +template <> +class OperatorEvaluatorMul<Vector3, Vector3i, double> { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Vector3i &a = *VariantGetInternalPtr<Vector3i>::get_ptr(&p_left); + const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_right); + *r_ret = Vector3(a.x, a.y, a.z) * b; + r_valid = true; + } + static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + *VariantGetInternalPtr<Vector3>::get_ptr(r_ret) = Vector3(VariantGetInternalPtr<Vector3i>::get_ptr(left)->x, VariantGetInternalPtr<Vector3i>::get_ptr(left)->y, VariantGetInternalPtr<Vector3i>::get_ptr(left)->z) * *VariantGetInternalPtr<double>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<Vector3>::encode(Vector3(PtrToArg<Vector3i>::convert(left).x, PtrToArg<Vector3i>::convert(left).y, PtrToArg<Vector3i>::convert(left).z) * PtrToArg<double>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<Vector3>::VARIANT_TYPE; } +}; + +template <> +class OperatorEvaluatorMul<Vector3, double, Vector3i> { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Vector3i &a = *VariantGetInternalPtr<Vector3i>::get_ptr(&p_right); + const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_left); + *r_ret = Vector3(a.x, a.y, a.z) * b; + r_valid = true; + } + static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + *VariantGetInternalPtr<Vector3>::get_ptr(r_ret) = Vector3(VariantGetInternalPtr<Vector3i>::get_ptr(right)->x, VariantGetInternalPtr<Vector3i>::get_ptr(right)->y, VariantGetInternalPtr<Vector3i>::get_ptr(right)->z) * *VariantGetInternalPtr<double>::get_ptr(left); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<Vector3>::encode(Vector3(PtrToArg<Vector3i>::convert(right).x, PtrToArg<Vector3i>::convert(right).y, PtrToArg<Vector3i>::convert(right).z) * PtrToArg<double>::convert(left), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<Vector3>::VARIANT_TYPE; } +}; + +template <> +class OperatorEvaluatorDivNZ<Vector3, Vector3i, double> { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const Vector3i &a = *VariantGetInternalPtr<Vector3i>::get_ptr(&p_left); + const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_right); + if (unlikely(b == 0)) { + r_valid = false; + *r_ret = "Division by zero error"; + return; + } + *r_ret = Vector3(a.x, a.y, a.z) / b; + r_valid = true; + } + static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + *VariantGetInternalPtr<Vector3>::get_ptr(r_ret) = Vector3(VariantGetInternalPtr<Vector3i>::get_ptr(left)->x, VariantGetInternalPtr<Vector3i>::get_ptr(left)->y, VariantGetInternalPtr<Vector3i>::get_ptr(left)->z) / *VariantGetInternalPtr<double>::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg<Vector3>::encode(Vector3(PtrToArg<Vector3i>::convert(left).x, PtrToArg<Vector3i>::convert(left).y, PtrToArg<Vector3i>::convert(left).z) / PtrToArg<double>::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo<Vector3>::VARIANT_TYPE; } +}; + void Variant::_register_variant_operators() { memset(operator_return_type_table, 0, sizeof(operator_return_type_table)); memset(operator_evaluator_table, 0, sizeof(operator_evaluator_table)); @@ -56,6 +176,7 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorAdd<double, double, int64_t>>(Variant::OP_ADD, Variant::FLOAT, Variant::INT); register_op<OperatorEvaluatorAdd<double, double, double>>(Variant::OP_ADD, Variant::FLOAT, Variant::FLOAT); register_op<OperatorEvaluatorAdd<String, String, String>>(Variant::OP_ADD, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorAdd<String, char32_t, String>>(Variant::OP_ADD, Variant::INT, Variant::STRING); register_op<OperatorEvaluatorAdd<Vector2, Vector2, Vector2>>(Variant::OP_ADD, Variant::VECTOR2, Variant::VECTOR2); register_op<OperatorEvaluatorAdd<Vector2i, Vector2i, Vector2i>>(Variant::OP_ADD, Variant::VECTOR2I, Variant::VECTOR2I); register_op<OperatorEvaluatorAdd<Vector3, Vector3, Vector3>>(Variant::OP_ADD, Variant::VECTOR3, Variant::VECTOR3); @@ -94,9 +215,9 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorMul<double, double, double>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::FLOAT); register_op<OperatorEvaluatorMul<double, double, int64_t>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::INT); register_op<OperatorEvaluatorMul<Vector2, double, Vector2>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR2); - register_op<OperatorEvaluatorMul<Vector2i, double, Vector2i>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR2I); + register_op<OperatorEvaluatorMul<Vector2, double, Vector2i>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR2I); register_op<OperatorEvaluatorMul<Vector3, double, Vector3>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR3); - register_op<OperatorEvaluatorMul<Vector3i, double, Vector3i>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR3I); + register_op<OperatorEvaluatorMul<Vector3, double, Vector3i>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR3I); register_op<OperatorEvaluatorMul<Vector2, Vector2, Vector2>>(Variant::OP_MULTIPLY, Variant::VECTOR2, Variant::VECTOR2); register_op<OperatorEvaluatorMul<Vector2, Vector2, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR2, Variant::INT); @@ -104,7 +225,7 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorMul<Vector2i, Vector2i, Vector2i>>(Variant::OP_MULTIPLY, Variant::VECTOR2I, Variant::VECTOR2I); register_op<OperatorEvaluatorMul<Vector2i, Vector2i, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR2I, Variant::INT); - register_op<OperatorEvaluatorMul<Vector2i, Vector2i, double>>(Variant::OP_MULTIPLY, Variant::VECTOR2I, Variant::FLOAT); + register_op<OperatorEvaluatorMul<Vector2, Vector2i, double>>(Variant::OP_MULTIPLY, Variant::VECTOR2I, Variant::FLOAT); register_op<OperatorEvaluatorMul<Vector3, Vector3, Vector3>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::VECTOR3); register_op<OperatorEvaluatorMul<Vector3, Vector3, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::INT); @@ -112,7 +233,7 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorMul<Vector3i, Vector3i, Vector3i>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::VECTOR3I); register_op<OperatorEvaluatorMul<Vector3i, Vector3i, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::INT); - register_op<OperatorEvaluatorMul<Vector3i, Vector3i, double>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::FLOAT); + register_op<OperatorEvaluatorMul<Vector3, Vector3i, double>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::FLOAT); register_op<OperatorEvaluatorMul<Quaternion, Quaternion, Quaternion>>(Variant::OP_MULTIPLY, Variant::QUATERNION, Variant::QUATERNION); register_op<OperatorEvaluatorMul<Quaternion, Quaternion, int64_t>>(Variant::OP_MULTIPLY, Variant::QUATERNION, Variant::INT); @@ -172,7 +293,7 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorDiv<Vector2, Vector2, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::INT); register_op<OperatorEvaluatorDivNZ<Vector2i, Vector2i, Vector2i>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::VECTOR2I); - register_op<OperatorEvaluatorDivNZ<Vector2i, Vector2i, double>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::FLOAT); + register_op<OperatorEvaluatorDivNZ<Vector2, Vector2i, double>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::FLOAT); register_op<OperatorEvaluatorDivNZ<Vector2i, Vector2i, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::INT); register_op<OperatorEvaluatorDiv<Vector2, Vector2, Vector2>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::VECTOR2); @@ -184,7 +305,7 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorDiv<Vector3, Vector3, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR3, Variant::INT); register_op<OperatorEvaluatorDivNZ<Vector3i, Vector3i, Vector3i>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::VECTOR3I); - register_op<OperatorEvaluatorDivNZ<Vector3i, Vector3i, double>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::FLOAT); + register_op<OperatorEvaluatorDivNZ<Vector3, Vector3i, double>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::FLOAT); register_op<OperatorEvaluatorDivNZ<Vector3i, Vector3i, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::INT); register_op<OperatorEvaluatorDiv<Quaternion, Quaternion, double>>(Variant::OP_DIVIDE, Variant::QUATERNION, Variant::FLOAT); @@ -502,6 +623,7 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorLess<double, int64_t>>(Variant::OP_LESS, Variant::FLOAT, Variant::INT); register_op<OperatorEvaluatorLess<double, double>>(Variant::OP_LESS, Variant::FLOAT, Variant::FLOAT); register_op<OperatorEvaluatorLess<String, String>>(Variant::OP_LESS, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorLess<StringName, StringName>>(Variant::OP_LESS, Variant::STRING_NAME, Variant::STRING_NAME); register_op<OperatorEvaluatorLess<Vector2, Vector2>>(Variant::OP_LESS, Variant::VECTOR2, Variant::VECTOR2); register_op<OperatorEvaluatorLess<Vector2i, Vector2i>>(Variant::OP_LESS, Variant::VECTOR2I, Variant::VECTOR2I); register_op<OperatorEvaluatorLess<Vector3, Vector3>>(Variant::OP_LESS, Variant::VECTOR3, Variant::VECTOR3); @@ -514,6 +636,7 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorLessEqual<double, int64_t>>(Variant::OP_LESS_EQUAL, Variant::FLOAT, Variant::INT); register_op<OperatorEvaluatorLessEqual<double, double>>(Variant::OP_LESS_EQUAL, Variant::FLOAT, Variant::FLOAT); register_op<OperatorEvaluatorLessEqual<String, String>>(Variant::OP_LESS_EQUAL, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorLessEqual<StringName, StringName>>(Variant::OP_LESS_EQUAL, Variant::STRING_NAME, Variant::STRING_NAME); register_op<OperatorEvaluatorLessEqual<Vector2, Vector2>>(Variant::OP_LESS_EQUAL, Variant::VECTOR2, Variant::VECTOR2); register_op<OperatorEvaluatorLessEqual<Vector2i, Vector2i>>(Variant::OP_LESS_EQUAL, Variant::VECTOR2I, Variant::VECTOR2I); register_op<OperatorEvaluatorLessEqual<Vector3, Vector3>>(Variant::OP_LESS_EQUAL, Variant::VECTOR3, Variant::VECTOR3); @@ -527,6 +650,7 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorGreater<double, int64_t>>(Variant::OP_GREATER, Variant::FLOAT, Variant::INT); register_op<OperatorEvaluatorGreater<double, double>>(Variant::OP_GREATER, Variant::FLOAT, Variant::FLOAT); register_op<OperatorEvaluatorGreater<String, String>>(Variant::OP_GREATER, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorGreater<StringName, StringName>>(Variant::OP_GREATER, Variant::STRING_NAME, Variant::STRING_NAME); register_op<OperatorEvaluatorGreater<Vector2, Vector2>>(Variant::OP_GREATER, Variant::VECTOR2, Variant::VECTOR2); register_op<OperatorEvaluatorGreater<Vector2i, Vector2i>>(Variant::OP_GREATER, Variant::VECTOR2I, Variant::VECTOR2I); register_op<OperatorEvaluatorGreater<Vector3, Vector3>>(Variant::OP_GREATER, Variant::VECTOR3, Variant::VECTOR3); @@ -539,6 +663,7 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorGreaterEqual<double, int64_t>>(Variant::OP_GREATER_EQUAL, Variant::FLOAT, Variant::INT); register_op<OperatorEvaluatorGreaterEqual<double, double>>(Variant::OP_GREATER_EQUAL, Variant::FLOAT, Variant::FLOAT); register_op<OperatorEvaluatorGreaterEqual<String, String>>(Variant::OP_GREATER_EQUAL, Variant::STRING, Variant::STRING); + register_op<OperatorEvaluatorGreaterEqual<StringName, StringName>>(Variant::OP_GREATER_EQUAL, Variant::STRING_NAME, Variant::STRING_NAME); register_op<OperatorEvaluatorGreaterEqual<Vector2, Vector2>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR2, Variant::VECTOR2); register_op<OperatorEvaluatorGreaterEqual<Vector2i, Vector2i>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR2I, Variant::VECTOR2I); register_op<OperatorEvaluatorGreaterEqual<Vector3, Vector3>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR3, Variant::VECTOR3); diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index 57875bf50f..e889a1bb40 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -188,7 +188,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri if (p_stream->is_eof()) { r_token.type = TK_EOF; return OK; - } else if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) { + } else if (is_hex_digit(ch)) { color_str += ch; } else { @@ -217,6 +217,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } case '"': { String str; + char32_t prev = 0; while (true) { char32_t ch = p_stream->get_char(); @@ -252,22 +253,25 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri case 'r': res = 13; break; + case 'U': case 'u': { - //hex number - for (int j = 0; j < 4; j++) { + // Hexadecimal sequence. + int hex_len = (next == 'U') ? 6 : 4; + for (int j = 0; j < hex_len; j++) { char32_t c = p_stream->get_char(); + if (c == 0) { r_err_str = "Unterminated String"; r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + if (!is_hex_digit(c)) { r_err_str = "Malformed hex constant in string"; r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } char32_t v; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a'; @@ -290,15 +294,49 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } break; } + // Parse UTF-16 pair. + if ((res & 0xfffffc00) == 0xd800) { + if (prev == 0) { + prev = res; + continue; + } else { + r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate"; + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + } else if ((res & 0xfffffc00) == 0xdc00) { + if (prev == 0) { + r_err_str = "Invalid UTF-16 sequence in string, unpaired trail surrogate"; + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } else { + res = (prev << 10UL) + res - ((0xd800 << 10UL) + 0xdc00 - 0x10000); + prev = 0; + } + } + if (prev != 0) { + r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate"; + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } str += res; - } else { + if (prev != 0) { + r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate"; + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } if (ch == '\n') { line++; } str += ch; } } + if (prev != 0) { + r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate"; + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } if (p_stream->is_utf8()) { str.parse_utf8(str.ascii(true).get_data()); @@ -343,7 +381,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri while (true) { switch (reading) { case READING_INT: { - if (c >= '0' && c <= '9') { + if (is_digit(c)) { //pass } else if (c == '.') { reading = READING_DEC; @@ -357,7 +395,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } break; case READING_DEC: { - if (c >= '0' && c <= '9') { + if (is_digit(c)) { } else if (c == 'e') { reading = READING_EXP; } else { @@ -366,7 +404,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri } break; case READING_EXP: { - if (c >= '0' && c <= '9') { + if (is_digit(c)) { exp_beg = true; } else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) { @@ -395,11 +433,11 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri r_token.value = num.as_int(); } return OK; - } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') { + } else if (is_ascii_char(cchar) || is_underscore(cchar)) { StringBuffer<> id; bool first = true; - while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && cchar >= '0' && cchar <= '9')) { + while (is_ascii_char(cchar) || is_underscore(cchar) || (!first && is_digit(cchar))) { id += cchar; cchar = p_stream->get_char(); first = false; @@ -1457,7 +1495,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str case Variant::FLOAT: { String s = rtos_fix(p_variant.operator double()); if (s != "inf" && s != "inf_neg" && s != "nan") { - if (s.find(".") == -1 && s.find("e") == -1) { + if (!s.contains(".") && !s.contains("e")) { s += ".0"; } } diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 3fd8eb5474..60950099d2 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -433,7 +433,7 @@ struct VariantUtilityFunctions { static inline Variant weakref(const Variant &obj, Callable::CallError &r_error) { if (obj.get_type() == Variant::OBJECT) { r_error.error = Callable::CallError::CALL_OK; - if (obj.is_ref()) { + if (obj.is_ref_counted()) { Ref<WeakRef> wref = memnew(WeakRef); REF r = obj; if (r.is_valid()) { diff --git a/core/version.h b/core/version.h index c718d0f4d7..e22922fa66 100644 --- a/core/version.h +++ b/core/version.h @@ -68,4 +68,7 @@ // Example: "Godot v3.1.4.stable.official.mono" #define VERSION_FULL_NAME "" VERSION_NAME " v" VERSION_FULL_BUILD +// Git commit hash, generated at build time in `core/version_hash.gen.cpp`. +extern const char *const VERSION_HASH; + #endif // VERSION_H |