From 67265d14f7aac8fdd3f7394495a9bf2eef972810 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Wed, 5 Oct 2022 17:50:19 +0200 Subject: [PATCH] [MP] Move engine and editor profilers to a plugin. Also refactor the editor plugin out of the ReplicationEditor. --- core/debugger/remote_debugger.cpp | 87 -------- core/debugger/remote_debugger.h | 2 - editor/debugger/script_editor_debugger.cpp | 24 +-- editor/debugger/script_editor_debugger.h | 3 - .../editor}/editor_network_profiler.cpp | 4 +- .../editor}/editor_network_profiler.h | 8 +- .../editor/multiplayer_editor_plugin.cpp | 140 +++++++++++++ .../editor/multiplayer_editor_plugin.h | 81 ++++++++ ...itor_plugin.cpp => replication_editor.cpp} | 68 +----- ...n_editor_plugin.h => replication_editor.h} | 31 +-- modules/multiplayer/multiplayer_debugger.cpp | 194 ++++++++++++++++++ modules/multiplayer/multiplayer_debugger.h | 95 +++++++++ modules/multiplayer/register_types.cpp | 9 +- scene/debugger/scene_debugger.cpp | 77 ------- scene/debugger/scene_debugger.h | 19 -- 15 files changed, 534 insertions(+), 308 deletions(-) rename {editor/debugger => modules/multiplayer/editor}/editor_network_profiler.cpp (98%) rename {editor/debugger => modules/multiplayer/editor}/editor_network_profiler.h (94%) create mode 100644 modules/multiplayer/editor/multiplayer_editor_plugin.cpp create mode 100644 modules/multiplayer/editor/multiplayer_editor_plugin.h rename modules/multiplayer/editor/{replication_editor_plugin.cpp => replication_editor.cpp} (89%) rename modules/multiplayer/editor/{replication_editor_plugin.h => replication_editor.h} (86%) create mode 100644 modules/multiplayer/multiplayer_debugger.cpp create mode 100644 modules/multiplayer/multiplayer_debugger.h diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index 23ee977df46..a2e3fab82c2 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -39,89 +39,6 @@ #include "core/object/script_language.h" #include "core/os/os.h" -class RemoteDebugger::MultiplayerProfiler : public EngineProfiler { - struct BandwidthFrame { - uint32_t timestamp; - int packet_size; - }; - - int bandwidth_in_ptr = 0; - Vector bandwidth_in; - int bandwidth_out_ptr = 0; - Vector bandwidth_out; - uint64_t last_bandwidth_time = 0; - - int bandwidth_usage(const Vector &p_buffer, int p_pointer) { - ERR_FAIL_COND_V(p_buffer.size() == 0, 0); - int total_bandwidth = 0; - - uint64_t timestamp = OS::get_singleton()->get_ticks_msec(); - uint64_t final_timestamp = timestamp - 1000; - - int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size(); - - while (i != p_pointer && p_buffer[i].packet_size > 0) { - if (p_buffer[i].timestamp < final_timestamp) { - return total_bandwidth; - } - total_bandwidth += p_buffer[i].packet_size; - i = (i + p_buffer.size() - 1) % p_buffer.size(); - } - - ERR_FAIL_COND_V_MSG(i == p_pointer, total_bandwidth, "Reached the end of the bandwidth profiler buffer, values might be inaccurate."); - return total_bandwidth; - } - -public: - void toggle(bool p_enable, const Array &p_opts) { - if (!p_enable) { - bandwidth_in.clear(); - bandwidth_out.clear(); - } else { - bandwidth_in_ptr = 0; - bandwidth_in.resize(16384); // ~128kB - for (int i = 0; i < bandwidth_in.size(); ++i) { - bandwidth_in.write[i].packet_size = -1; - } - bandwidth_out_ptr = 0; - bandwidth_out.resize(16384); // ~128kB - for (int i = 0; i < bandwidth_out.size(); ++i) { - bandwidth_out.write[i].packet_size = -1; - } - } - } - - void add(const Array &p_data) { - 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(); - } - } - - void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) { - uint64_t pt = OS::get_singleton()->get_ticks_msec(); - if (pt - last_bandwidth_time > 200) { - last_bandwidth_time = pt; - int incoming_bandwidth = bandwidth_usage(bandwidth_in, bandwidth_in_ptr); - int outgoing_bandwidth = bandwidth_usage(bandwidth_out, bandwidth_out_ptr); - - Array arr; - arr.push_back(incoming_bandwidth); - arr.push_back(outgoing_bandwidth); - EngineDebugger::get_singleton()->send_message("multiplayer:bandwidth", arr); - } - } -}; - class RemoteDebugger::PerformanceProfiler : public EngineProfiler { Object *performance = nullptr; int last_perf_time = 0; @@ -659,10 +576,6 @@ RemoteDebugger::RemoteDebugger(Ref 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"); - // Multiplayer Profiler - multiplayer_profiler.instantiate(); - multiplayer_profiler->bind("multiplayer"); - // Performance Profiler Object *perf = Engine::get_singleton()->get_singleton_object("Performance"); if (perf) { diff --git a/core/debugger/remote_debugger.h b/core/debugger/remote_debugger.h index fe4bbe86ea8..944229d361e 100644 --- a/core/debugger/remote_debugger.h +++ b/core/debugger/remote_debugger.h @@ -50,10 +50,8 @@ public: private: typedef DebuggerMarshalls::OutputError ErrorMessage; - class MultiplayerProfiler; class PerformanceProfiler; - Ref multiplayer_profiler; Ref performance_profiler; Ref peer; diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index fe646cdc8d5..fd77ad784af 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -37,7 +37,6 @@ #include "core/string/ustring.h" #include "core/version.h" #include "editor/debugger/debug_adapter/debug_adapter_protocol.h" -#include "editor/debugger/editor_network_profiler.h" #include "editor/debugger/editor_performance_profiler.h" #include "editor/debugger/editor_profiler.h" #include "editor/debugger/editor_visual_profiler.h" @@ -52,6 +51,7 @@ #include "editor/plugins/node_3d_editor_plugin.h" #include "main/performance.h" #include "scene/3d/camera_3d.h" +#include "scene/debugger/scene_debugger.h" #include "scene/gui/dialogs.h" #include "scene/gui/label.h" #include "scene/gui/line_edit.h" @@ -713,17 +713,6 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da profiler->add_frame_metric(metric, true); } - } else if (p_msg == "multiplayer:rpc") { - SceneDebugger::RPCProfilerFrame frame; - frame.deserialize(p_data); - for (int i = 0; i < frame.infos.size(); i++) { - network_profiler->add_node_frame_data(frame.infos[i]); - } - - } else if (p_msg == "multiplayer:bandwidth") { - ERR_FAIL_COND(p_data.size() < 2); - network_profiler->set_bandwidth(p_data[0], p_data[1]); - } else if (p_msg == "request_quit") { emit_signal(SNAME("stop_requested")); _stop_and_notify(); @@ -967,10 +956,6 @@ void ScriptEditorDebugger::_profiler_activate(bool p_enable, int p_type) { Array msg_data; msg_data.push_back(p_enable); switch (p_type) { - case PROFILER_NETWORK: - _put_msg("profiler:multiplayer", msg_data); - _put_msg("profiler:rpc", msg_data); - break; case PROFILER_VISUAL: _put_msg("profiler:visual", msg_data); break; @@ -1873,13 +1858,6 @@ ScriptEditorDebugger::ScriptEditorDebugger() { visual_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate).bind(PROFILER_VISUAL)); } - { //network profiler - network_profiler = memnew(EditorNetworkProfiler); - network_profiler->set_name(TTR("Network Profiler")); - tabs->add_child(network_profiler); - network_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate).bind(PROFILER_NETWORK)); - } - { //monitors performance_profiler = memnew(EditorPerformanceProfiler); tabs->add_child(performance_profiler); diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h index 82ef46f60d8..22d6e48294d 100644 --- a/editor/debugger/script_editor_debugger.h +++ b/editor/debugger/script_editor_debugger.h @@ -50,7 +50,6 @@ class ItemList; class EditorProfiler; class EditorFileDialog; class EditorVisualProfiler; -class EditorNetworkProfiler; class EditorPerformanceProfiler; class SceneDebuggerTree; class EditorDebuggerPlugin; @@ -72,7 +71,6 @@ private: }; enum ProfilerType { - PROFILER_NETWORK, PROFILER_VISUAL, PROFILER_SCRIPTS_SERVERS }; @@ -151,7 +149,6 @@ private: EditorProfiler *profiler = nullptr; EditorVisualProfiler *visual_profiler = nullptr; - EditorNetworkProfiler *network_profiler = nullptr; EditorPerformanceProfiler *performance_profiler = nullptr; OS::ProcessID remote_pid = 0; diff --git a/editor/debugger/editor_network_profiler.cpp b/modules/multiplayer/editor/editor_network_profiler.cpp similarity index 98% rename from editor/debugger/editor_network_profiler.cpp rename to modules/multiplayer/editor/editor_network_profiler.cpp index 8c18eba71d6..a7e5b80b662 100644 --- a/editor/debugger/editor_network_profiler.cpp +++ b/modules/multiplayer/editor/editor_network_profiler.cpp @@ -59,7 +59,7 @@ void EditorNetworkProfiler::_update_frame() { TreeItem *root = counters_display->create_item(); - for (const KeyValue &E : nodes_data) { + for (const KeyValue &E : nodes_data) { TreeItem *node = counters_display->create_item(root); for (int j = 0; j < counters_display->get_columns(); ++j) { @@ -92,7 +92,7 @@ void EditorNetworkProfiler::_clear_pressed() { } } -void EditorNetworkProfiler::add_node_frame_data(const SceneDebugger::RPCNodeInfo p_frame) { +void EditorNetworkProfiler::add_node_frame_data(const RPCNodeInfo p_frame) { if (!nodes_data.has(p_frame.node)) { nodes_data.insert(p_frame.node, p_frame); } else { diff --git a/editor/debugger/editor_network_profiler.h b/modules/multiplayer/editor/editor_network_profiler.h similarity index 94% rename from editor/debugger/editor_network_profiler.h rename to modules/multiplayer/editor/editor_network_profiler.h index aea7ce3eecd..98d12e3c0aa 100644 --- a/editor/debugger/editor_network_profiler.h +++ b/modules/multiplayer/editor/editor_network_profiler.h @@ -38,10 +38,14 @@ #include "scene/gui/split_container.h" #include "scene/gui/tree.h" +#include "../multiplayer_debugger.h" + class EditorNetworkProfiler : public VBoxContainer { GDCLASS(EditorNetworkProfiler, VBoxContainer) private: + using RPCNodeInfo = MultiplayerDebugger::RPCNodeInfo; + Button *activate = nullptr; Button *clear_button = nullptr; Tree *counters_display = nullptr; @@ -50,7 +54,7 @@ private: Timer *frame_delay = nullptr; - HashMap nodes_data; + HashMap nodes_data; void _update_frame(); @@ -62,7 +66,7 @@ protected: static void _bind_methods(); public: - void add_node_frame_data(const SceneDebugger::RPCNodeInfo p_frame); + void add_node_frame_data(const RPCNodeInfo p_frame); void set_bandwidth(int p_incoming, int p_outgoing); bool is_profiling(); diff --git a/modules/multiplayer/editor/multiplayer_editor_plugin.cpp b/modules/multiplayer/editor/multiplayer_editor_plugin.cpp new file mode 100644 index 00000000000..00b1537827d --- /dev/null +++ b/modules/multiplayer/editor/multiplayer_editor_plugin.cpp @@ -0,0 +1,140 @@ +/*************************************************************************/ +/* multiplayer_editor_plugin.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 "multiplayer_editor_plugin.h" + +#include "../multiplayer_synchronizer.h" +#include "editor_network_profiler.h" +#include "replication_editor.h" + +#include "editor/editor_node.h" + +bool MultiplayerEditorDebugger::has_capture(const String &p_capture) const { + return p_capture == "multiplayer"; +} + +bool MultiplayerEditorDebugger::capture(const String &p_message, const Array &p_data, int p_session) { + ERR_FAIL_COND_V(!profilers.has(p_session), false); + EditorNetworkProfiler *profiler = profilers[p_session]; + if (p_message == "multiplayer:rpc") { + MultiplayerDebugger::RPCFrame frame; + frame.deserialize(p_data); + for (int i = 0; i < frame.infos.size(); i++) { + profiler->add_node_frame_data(frame.infos[i]); + } + return true; + + } else if (p_message == "multiplayer:bandwidth") { + ERR_FAIL_COND_V(p_data.size() < 2, false); + profiler->set_bandwidth(p_data[0], p_data[1]); + return true; + } + return false; +} + +void MultiplayerEditorDebugger::_profiler_activate(bool p_enable, int p_session_id) { + Ref session = get_session(p_session_id); + ERR_FAIL_COND(session.is_null()); + session->toggle_profiler("multiplayer", p_enable); + session->toggle_profiler("rpc", p_enable); +} + +void MultiplayerEditorDebugger::setup_session(int p_session_id) { + Ref session = get_session(p_session_id); + ERR_FAIL_COND(session.is_null()); + EditorNetworkProfiler *profiler = memnew(EditorNetworkProfiler); + profiler->connect("enable_profiling", callable_mp(this, &MultiplayerEditorDebugger::_profiler_activate).bind(p_session_id)); + profiler->set_name(TTR("Network Profiler")); + session->add_session_tab(profiler); + profilers[p_session_id] = profiler; +} + +MultiplayerEditorPlugin::MultiplayerEditorPlugin() { + repl_editor = memnew(ReplicationEditor); + button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Replication"), repl_editor); + button->hide(); + repl_editor->get_pin()->connect("pressed", callable_mp(this, &MultiplayerEditorPlugin::_pinned)); + debugger.instantiate(); +} + +MultiplayerEditorPlugin::~MultiplayerEditorPlugin() { +} + +void MultiplayerEditorPlugin::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + get_tree()->connect("node_removed", callable_mp(this, &MultiplayerEditorPlugin::_node_removed)); + add_debugger_plugin(debugger); + } break; + case NOTIFICATION_EXIT_TREE: { + remove_debugger_plugin(debugger); + } + } +} + +void MultiplayerEditorPlugin::_node_removed(Node *p_node) { + if (p_node && p_node == repl_editor->get_current()) { + repl_editor->edit(nullptr); + if (repl_editor->is_visible_in_tree()) { + EditorNode::get_singleton()->hide_bottom_panel(); + } + button->hide(); + repl_editor->get_pin()->set_pressed(false); + } +} + +void MultiplayerEditorPlugin::_pinned() { + if (!repl_editor->get_pin()->is_pressed()) { + if (repl_editor->is_visible_in_tree()) { + EditorNode::get_singleton()->hide_bottom_panel(); + } + button->hide(); + } +} + +void MultiplayerEditorPlugin::edit(Object *p_object) { + repl_editor->edit(Object::cast_to(p_object)); +} + +bool MultiplayerEditorPlugin::handles(Object *p_object) const { + return p_object->is_class("MultiplayerSynchronizer"); +} + +void MultiplayerEditorPlugin::make_visible(bool p_visible) { + if (p_visible) { + button->show(); + EditorNode::get_singleton()->make_bottom_panel_item_visible(repl_editor); + } else if (!repl_editor->get_pin()->is_pressed()) { + if (repl_editor->is_visible_in_tree()) { + EditorNode::get_singleton()->hide_bottom_panel(); + } + button->hide(); + } +} diff --git a/modules/multiplayer/editor/multiplayer_editor_plugin.h b/modules/multiplayer/editor/multiplayer_editor_plugin.h new file mode 100644 index 00000000000..6d1514cdb1d --- /dev/null +++ b/modules/multiplayer/editor/multiplayer_editor_plugin.h @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* multiplayer_editor_plugin.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_EDITOR_PLUGIN_H +#define MULTIPLAYER_EDITOR_PLUGIN_H + +#include "editor/editor_plugin.h" + +#include "editor/plugins/editor_debugger_plugin.h" + +class EditorNetworkProfiler; +class MultiplayerEditorDebugger : public EditorDebuggerPlugin { + GDCLASS(MultiplayerEditorDebugger, EditorDebuggerPlugin); + +private: + HashMap profilers; + + void _profiler_activate(bool p_enable, int p_session_id); + +public: + virtual bool has_capture(const String &p_capture) const override; + virtual bool capture(const String &p_message, const Array &p_data, int p_index) override; + virtual void setup_session(int p_session_id) override; + + MultiplayerEditorDebugger() {} +}; + +class ReplicationEditor; + +class MultiplayerEditorPlugin : public EditorPlugin { + GDCLASS(MultiplayerEditorPlugin, EditorPlugin); + +private: + Button *button = nullptr; + ReplicationEditor *repl_editor = nullptr; + Ref debugger; + + void _node_removed(Node *p_node); + + void _pinned(); + +protected: + void _notification(int p_what); + +public: + virtual void edit(Object *p_object) override; + virtual bool handles(Object *p_object) const override; + virtual void make_visible(bool p_visible) override; + + MultiplayerEditorPlugin(); + ~MultiplayerEditorPlugin(); +}; + +#endif // MULTIPLAYER_EDITOR_PLUGIN_H diff --git a/modules/multiplayer/editor/replication_editor_plugin.cpp b/modules/multiplayer/editor/replication_editor.cpp similarity index 89% rename from modules/multiplayer/editor/replication_editor_plugin.cpp rename to modules/multiplayer/editor/replication_editor.cpp index de10420652b..4ba9cd14f0d 100644 --- a/modules/multiplayer/editor/replication_editor_plugin.cpp +++ b/modules/multiplayer/editor/replication_editor.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* replication_editor_plugin.cpp */ +/* replication_editor.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "replication_editor_plugin.h" +#include "replication_editor.h" + +#include "../multiplayer_synchronizer.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" @@ -37,7 +39,6 @@ #include "editor/inspector_dock.h" #include "editor/property_selector.h" #include "editor/scene_tree_editor.h" -#include "modules/multiplayer/multiplayer_synchronizer.h" #include "scene/gui/dialogs.h" #include "scene/gui/separator.h" #include "scene/gui/tree.h" @@ -141,7 +142,7 @@ void ReplicationEditor::_add_sync_property(String p_path) { return; } - Ref undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref &undo_redo = EditorNode::get_singleton()->get_undo_redo(); undo_redo->create_action(TTR("Add property to synchronizer")); if (config.is_null()) { @@ -493,62 +494,3 @@ void ReplicationEditor::_add_property(const NodePath &p_property, bool p_spawn, item->set_checked(2, p_sync); item->set_editable(2, true); } - -/// ReplicationEditorPlugin -ReplicationEditorPlugin::ReplicationEditorPlugin() { - repl_editor = memnew(ReplicationEditor); - button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Replication"), repl_editor); - button->hide(); - repl_editor->get_pin()->connect("pressed", callable_mp(this, &ReplicationEditorPlugin::_pinned)); -} - -ReplicationEditorPlugin::~ReplicationEditorPlugin() { -} - -void ReplicationEditorPlugin::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - get_tree()->connect("node_removed", callable_mp(this, &ReplicationEditorPlugin::_node_removed)); - } break; - } -} - -void ReplicationEditorPlugin::_node_removed(Node *p_node) { - if (p_node && p_node == repl_editor->get_current()) { - repl_editor->edit(nullptr); - if (repl_editor->is_visible_in_tree()) { - EditorNode::get_singleton()->hide_bottom_panel(); - } - button->hide(); - repl_editor->get_pin()->set_pressed(false); - } -} - -void ReplicationEditorPlugin::_pinned() { - if (!repl_editor->get_pin()->is_pressed()) { - if (repl_editor->is_visible_in_tree()) { - EditorNode::get_singleton()->hide_bottom_panel(); - } - button->hide(); - } -} - -void ReplicationEditorPlugin::edit(Object *p_object) { - repl_editor->edit(Object::cast_to(p_object)); -} - -bool ReplicationEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("MultiplayerSynchronizer"); -} - -void ReplicationEditorPlugin::make_visible(bool p_visible) { - if (p_visible) { - button->show(); - EditorNode::get_singleton()->make_bottom_panel_item_visible(repl_editor); - } else if (!repl_editor->get_pin()->is_pressed()) { - if (repl_editor->is_visible_in_tree()) { - EditorNode::get_singleton()->hide_bottom_panel(); - } - button->hide(); - } -} diff --git a/modules/multiplayer/editor/replication_editor_plugin.h b/modules/multiplayer/editor/replication_editor.h similarity index 86% rename from modules/multiplayer/editor/replication_editor_plugin.h rename to modules/multiplayer/editor/replication_editor.h index 6c40a992932..8a48e8dbe7d 100644 --- a/modules/multiplayer/editor/replication_editor_plugin.h +++ b/modules/multiplayer/editor/replication_editor.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* replication_editor_plugin.h */ +/* replication_editor.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef REPLICATION_EDITOR_PLUGIN_H -#define REPLICATION_EDITOR_PLUGIN_H +#ifndef REPLICATION_EDITOR_H +#define REPLICATION_EDITOR_H #include "editor/editor_plugin.h" #include "modules/multiplayer/scene_replication_config.h" @@ -105,27 +105,4 @@ public: ~ReplicationEditor() {} }; -class ReplicationEditorPlugin : public EditorPlugin { - GDCLASS(ReplicationEditorPlugin, EditorPlugin); - -private: - Button *button = nullptr; - ReplicationEditor *repl_editor = nullptr; - - void _node_removed(Node *p_node); - - void _pinned(); - -protected: - void _notification(int p_what); - -public: - virtual void edit(Object *p_object) override; - virtual bool handles(Object *p_object) const override; - virtual void make_visible(bool p_visible) override; - - ReplicationEditorPlugin(); - ~ReplicationEditorPlugin(); -}; - -#endif // REPLICATION_EDITOR_PLUGIN_H +#endif // REPLICATION_EDITOR_H diff --git a/modules/multiplayer/multiplayer_debugger.cpp b/modules/multiplayer/multiplayer_debugger.cpp new file mode 100644 index 00000000000..3d22af04dc6 --- /dev/null +++ b/modules/multiplayer/multiplayer_debugger.cpp @@ -0,0 +1,194 @@ +/*************************************************************************/ +/* multiplayer_debugger.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 "multiplayer_debugger.h" + +#include "core/debugger/engine_debugger.h" +#include "scene/main/node.h" + +List> multiplayer_profilers; + +void MultiplayerDebugger::initialize() { + Ref bandwidth; + bandwidth.instantiate(); + bandwidth->bind("multiplayer"); + multiplayer_profilers.push_back(bandwidth); + + Ref rpc_profiler; + rpc_profiler.instantiate(); + rpc_profiler->bind("rpc"); + multiplayer_profilers.push_back(rpc_profiler); +} + +void MultiplayerDebugger::deinitialize() { + multiplayer_profilers.clear(); +} + +// BandwidthProfiler + +int MultiplayerDebugger::BandwidthProfiler::bandwidth_usage(const Vector &p_buffer, int p_pointer) { + ERR_FAIL_COND_V(p_buffer.size() == 0, 0); + int total_bandwidth = 0; + + uint64_t timestamp = OS::get_singleton()->get_ticks_msec(); + uint64_t final_timestamp = timestamp - 1000; + + int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size(); + + while (i != p_pointer && p_buffer[i].packet_size > 0) { + if (p_buffer[i].timestamp < final_timestamp) { + return total_bandwidth; + } + total_bandwidth += p_buffer[i].packet_size; + i = (i + p_buffer.size() - 1) % p_buffer.size(); + } + + ERR_FAIL_COND_V_MSG(i == p_pointer, total_bandwidth, "Reached the end of the bandwidth profiler buffer, values might be inaccurate."); + return total_bandwidth; +} + +void MultiplayerDebugger::BandwidthProfiler::toggle(bool p_enable, const Array &p_opts) { + if (!p_enable) { + bandwidth_in.clear(); + bandwidth_out.clear(); + } else { + bandwidth_in_ptr = 0; + bandwidth_in.resize(16384); // ~128kB + for (int i = 0; i < bandwidth_in.size(); ++i) { + bandwidth_in.write[i].packet_size = -1; + } + bandwidth_out_ptr = 0; + bandwidth_out.resize(16384); // ~128kB + for (int i = 0; i < bandwidth_out.size(); ++i) { + bandwidth_out.write[i].packet_size = -1; + } + } +} + +void MultiplayerDebugger::BandwidthProfiler::add(const Array &p_data) { + 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(); + } +} + +void MultiplayerDebugger::BandwidthProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) { + uint64_t pt = OS::get_singleton()->get_ticks_msec(); + if (pt - last_bandwidth_time > 200) { + last_bandwidth_time = pt; + int incoming_bandwidth = bandwidth_usage(bandwidth_in, bandwidth_in_ptr); + int outgoing_bandwidth = bandwidth_usage(bandwidth_out, bandwidth_out_ptr); + + Array arr; + arr.push_back(incoming_bandwidth); + arr.push_back(outgoing_bandwidth); + EngineDebugger::get_singleton()->send_message("multiplayer:bandwidth", arr); + } +} + +// RPCProfiler + +Array MultiplayerDebugger::RPCFrame::serialize() { + Array arr; + arr.push_back(infos.size() * 4); + 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].outgoing_rpc); + } + return arr; +} + +bool MultiplayerDebugger::RPCFrame::deserialize(const Array &p_arr) { + ERR_FAIL_COND_V(p_arr.size() < 1, false); + uint32_t size = p_arr[0]; + ERR_FAIL_COND_V(size % 4, false); + ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false); + infos.resize(size / 4); + int idx = 1; + for (uint32_t i = 0; i < size / 4; ++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].outgoing_rpc = p_arr[idx + 3]; + } + return true; +} + +void MultiplayerDebugger::RPCProfiler::init_node(const ObjectID p_node) { + if (rpc_node_data.has(p_node)) { + return; + } + rpc_node_data.insert(p_node, RPCNodeInfo()); + rpc_node_data[p_node].node = p_node; + rpc_node_data[p_node].node_path = Object::cast_to(ObjectDB::get_instance(p_node))->get_path(); + rpc_node_data[p_node].incoming_rpc = 0; + rpc_node_data[p_node].outgoing_rpc = 0; +} + +void MultiplayerDebugger::RPCProfiler::toggle(bool p_enable, const Array &p_opts) { + rpc_node_data.clear(); +} + +void MultiplayerDebugger::RPCProfiler::add(const Array &p_data) { + ERR_FAIL_COND(p_data.size() < 2); + const ObjectID id = p_data[0]; + const String what = p_data[1]; + init_node(id); + RPCNodeInfo &info = rpc_node_data[id]; + if (what == "rpc_in") { + info.incoming_rpc++; + } else if (what == "rpc_out") { + info.outgoing_rpc++; + } +} + +void MultiplayerDebugger::RPCProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) { + uint64_t pt = OS::get_singleton()->get_ticks_msec(); + if (pt - last_profile_time > 100) { + last_profile_time = pt; + RPCFrame frame; + for (const KeyValue &E : rpc_node_data) { + frame.infos.push_back(E.value); + } + rpc_node_data.clear(); + EngineDebugger::get_singleton()->send_message("multiplayer:rpc", frame.serialize()); + } +} diff --git a/modules/multiplayer/multiplayer_debugger.h b/modules/multiplayer/multiplayer_debugger.h new file mode 100644 index 00000000000..4efd1da016a --- /dev/null +++ b/modules/multiplayer/multiplayer_debugger.h @@ -0,0 +1,95 @@ +/*************************************************************************/ +/* multiplayer_debugger.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_DEBUGGER_H +#define MULTIPLAYER_DEBUGGER_H + +#include "core/debugger/engine_profiler.h" + +#include "core/os/os.h" + +class MultiplayerDebugger { +public: + struct RPCNodeInfo { + ObjectID node; + String node_path; + int incoming_rpc = 0; + int outgoing_rpc = 0; + }; + + struct RPCFrame { + Vector infos; + + Array serialize(); + bool deserialize(const Array &p_arr); + }; + +private: + class BandwidthProfiler : public EngineProfiler { + protected: + struct BandwidthFrame { + uint32_t timestamp; + int packet_size; + }; + + int bandwidth_in_ptr = 0; + Vector bandwidth_in; + int bandwidth_out_ptr = 0; + Vector bandwidth_out; + uint64_t last_bandwidth_time = 0; + + int bandwidth_usage(const Vector &p_buffer, int p_pointer); + + public: + void toggle(bool p_enable, const Array &p_opts); + void add(const Array &p_data); + void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time); + }; + + class RPCProfiler : public EngineProfiler { + public: + private: + HashMap rpc_node_data; + uint64_t last_profile_time = 0; + + void init_node(const ObjectID p_node); + + public: + void toggle(bool p_enable, const Array &p_opts); + void add(const Array &p_data); + void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time); + }; + +public: + static void initialize(); + static void deinitialize(); +}; + +#endif // MULTIPLAYER_DEBUGGER_H diff --git a/modules/multiplayer/register_types.cpp b/modules/multiplayer/register_types.cpp index a2c524da803..2bf10410292 100644 --- a/modules/multiplayer/register_types.cpp +++ b/modules/multiplayer/register_types.cpp @@ -36,9 +36,10 @@ #include "scene_replication_interface.h" #include "scene_rpc_interface.h" +#include "multiplayer_debugger.h" + #ifdef TOOLS_ENABLED -#include "editor/editor_plugin.h" -#include "editor/replication_editor_plugin.h" +#include "editor/multiplayer_editor_plugin.h" #endif void initialize_multiplayer_module(ModuleInitializationLevel p_level) { @@ -48,13 +49,15 @@ void initialize_multiplayer_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(MultiplayerSynchronizer); GDREGISTER_CLASS(SceneMultiplayer); MultiplayerAPI::set_default_interface("SceneMultiplayer"); + MultiplayerDebugger::initialize(); } #ifdef TOOLS_ENABLED if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { - EditorPlugins::add_by_type(); + EditorPlugins::add_by_type(); } #endif } void uninitialize_multiplayer_module(ModuleInitializationLevel p_level) { + MultiplayerDebugger::deinitialize(); } diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index b4bdda9ecb3..df27eecce7d 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -39,87 +39,10 @@ #include "scene/main/window.h" #include "scene/resources/packed_scene.h" -Array SceneDebugger::RPCProfilerFrame::serialize() { - Array arr; - arr.push_back(infos.size() * 4); - 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].outgoing_rpc); - } - return arr; -} - -bool SceneDebugger::RPCProfilerFrame::deserialize(const Array &p_arr) { - ERR_FAIL_COND_V(p_arr.size() < 1, false); - uint32_t size = p_arr[0]; - ERR_FAIL_COND_V(size % 4, false); - ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false); - infos.resize(size / 4); - int idx = 1; - for (uint32_t i = 0; i < size / 4; ++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].outgoing_rpc = p_arr[idx + 3]; - } - return true; -} - -class SceneDebugger::RPCProfiler : public EngineProfiler { - HashMap rpc_node_data; - uint64_t last_profile_time = 0; - - void init_node(const ObjectID p_node) { - if (rpc_node_data.has(p_node)) { - return; - } - rpc_node_data.insert(p_node, RPCNodeInfo()); - rpc_node_data[p_node].node = p_node; - rpc_node_data[p_node].node_path = Object::cast_to(ObjectDB::get_instance(p_node))->get_path(); - rpc_node_data[p_node].incoming_rpc = 0; - rpc_node_data[p_node].outgoing_rpc = 0; - } - -public: - void toggle(bool p_enable, const Array &p_opts) { - rpc_node_data.clear(); - } - - void add(const Array &p_data) { - ERR_FAIL_COND(p_data.size() < 2); - const ObjectID id = p_data[0]; - const String what = p_data[1]; - init_node(id); - RPCNodeInfo &info = rpc_node_data[id]; - if (what == "rpc_in") { - info.incoming_rpc++; - } else if (what == "rpc_out") { - info.outgoing_rpc++; - } - } - - void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) { - uint64_t pt = OS::get_singleton()->get_ticks_msec(); - if (pt - last_profile_time > 100) { - last_profile_time = pt; - RPCProfilerFrame frame; - for (const KeyValue &E : rpc_node_data) { - frame.infos.push_back(E.value); - } - rpc_node_data.clear(); - EngineDebugger::get_singleton()->send_message("multiplayer:rpc", frame.serialize()); - } - } -}; - SceneDebugger *SceneDebugger::singleton = nullptr; SceneDebugger::SceneDebugger() { singleton = this; - rpc_profiler.instantiate(); - rpc_profiler->bind("rpc"); #ifdef DEBUG_ENABLED LiveEditor::singleton = memnew(LiveEditor); EngineDebugger::register_message_capture("scene", EngineDebugger::Capture(nullptr, SceneDebugger::parse_message)); diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h index fe35446aae1..0428bfcc2e5 100644 --- a/scene/debugger/scene_debugger.h +++ b/scene/debugger/scene_debugger.h @@ -42,28 +42,9 @@ class Node; class SceneDebugger { public: - // RPC profiler - struct RPCNodeInfo { - ObjectID node; - String node_path; - int incoming_rpc = 0; - int outgoing_rpc = 0; - }; - - struct RPCProfilerFrame { - Vector infos; - - Array serialize(); - bool deserialize(const Array &p_arr); - }; - private: - class RPCProfiler; - static SceneDebugger *singleton; - Ref rpc_profiler; - SceneDebugger(); public: