[MP] Move engine and editor profilers to a plugin.
Also refactor the editor plugin out of the ReplicationEditor.
This commit is contained in:
parent
d568b25e36
commit
67265d14f7
@ -39,89 +39,6 @@
|
|||||||
#include "core/object/script_language.h"
|
#include "core/object/script_language.h"
|
||||||
#include "core/os/os.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<BandwidthFrame> bandwidth_in;
|
|
||||||
int bandwidth_out_ptr = 0;
|
|
||||||
Vector<BandwidthFrame> bandwidth_out;
|
|
||||||
uint64_t last_bandwidth_time = 0;
|
|
||||||
|
|
||||||
int bandwidth_usage(const Vector<BandwidthFrame> &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 {
|
class RemoteDebugger::PerformanceProfiler : public EngineProfiler {
|
||||||
Object *performance = nullptr;
|
Object *performance = nullptr;
|
||||||
int last_perf_time = 0;
|
int last_perf_time = 0;
|
||||||
@ -659,10 +576,6 @@ RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) {
|
|||||||
max_errors_per_second = GLOBAL_GET("network/limits/debugger/max_errors_per_second");
|
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");
|
max_warnings_per_second = GLOBAL_GET("network/limits/debugger/max_warnings_per_second");
|
||||||
|
|
||||||
// Multiplayer Profiler
|
|
||||||
multiplayer_profiler.instantiate();
|
|
||||||
multiplayer_profiler->bind("multiplayer");
|
|
||||||
|
|
||||||
// Performance Profiler
|
// Performance Profiler
|
||||||
Object *perf = Engine::get_singleton()->get_singleton_object("Performance");
|
Object *perf = Engine::get_singleton()->get_singleton_object("Performance");
|
||||||
if (perf) {
|
if (perf) {
|
||||||
|
@ -50,10 +50,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
typedef DebuggerMarshalls::OutputError ErrorMessage;
|
typedef DebuggerMarshalls::OutputError ErrorMessage;
|
||||||
|
|
||||||
class MultiplayerProfiler;
|
|
||||||
class PerformanceProfiler;
|
class PerformanceProfiler;
|
||||||
|
|
||||||
Ref<MultiplayerProfiler> multiplayer_profiler;
|
|
||||||
Ref<PerformanceProfiler> performance_profiler;
|
Ref<PerformanceProfiler> performance_profiler;
|
||||||
|
|
||||||
Ref<RemoteDebuggerPeer> peer;
|
Ref<RemoteDebuggerPeer> peer;
|
||||||
|
@ -37,7 +37,6 @@
|
|||||||
#include "core/string/ustring.h"
|
#include "core/string/ustring.h"
|
||||||
#include "core/version.h"
|
#include "core/version.h"
|
||||||
#include "editor/debugger/debug_adapter/debug_adapter_protocol.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_performance_profiler.h"
|
||||||
#include "editor/debugger/editor_profiler.h"
|
#include "editor/debugger/editor_profiler.h"
|
||||||
#include "editor/debugger/editor_visual_profiler.h"
|
#include "editor/debugger/editor_visual_profiler.h"
|
||||||
@ -52,6 +51,7 @@
|
|||||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||||
#include "main/performance.h"
|
#include "main/performance.h"
|
||||||
#include "scene/3d/camera_3d.h"
|
#include "scene/3d/camera_3d.h"
|
||||||
|
#include "scene/debugger/scene_debugger.h"
|
||||||
#include "scene/gui/dialogs.h"
|
#include "scene/gui/dialogs.h"
|
||||||
#include "scene/gui/label.h"
|
#include "scene/gui/label.h"
|
||||||
#include "scene/gui/line_edit.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);
|
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") {
|
} else if (p_msg == "request_quit") {
|
||||||
emit_signal(SNAME("stop_requested"));
|
emit_signal(SNAME("stop_requested"));
|
||||||
_stop_and_notify();
|
_stop_and_notify();
|
||||||
@ -967,10 +956,6 @@ void ScriptEditorDebugger::_profiler_activate(bool p_enable, int p_type) {
|
|||||||
Array msg_data;
|
Array msg_data;
|
||||||
msg_data.push_back(p_enable);
|
msg_data.push_back(p_enable);
|
||||||
switch (p_type) {
|
switch (p_type) {
|
||||||
case PROFILER_NETWORK:
|
|
||||||
_put_msg("profiler:multiplayer", msg_data);
|
|
||||||
_put_msg("profiler:rpc", msg_data);
|
|
||||||
break;
|
|
||||||
case PROFILER_VISUAL:
|
case PROFILER_VISUAL:
|
||||||
_put_msg("profiler:visual", msg_data);
|
_put_msg("profiler:visual", msg_data);
|
||||||
break;
|
break;
|
||||||
@ -1873,13 +1858,6 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
|
|||||||
visual_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate).bind(PROFILER_VISUAL));
|
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
|
{ //monitors
|
||||||
performance_profiler = memnew(EditorPerformanceProfiler);
|
performance_profiler = memnew(EditorPerformanceProfiler);
|
||||||
tabs->add_child(performance_profiler);
|
tabs->add_child(performance_profiler);
|
||||||
|
@ -50,7 +50,6 @@ class ItemList;
|
|||||||
class EditorProfiler;
|
class EditorProfiler;
|
||||||
class EditorFileDialog;
|
class EditorFileDialog;
|
||||||
class EditorVisualProfiler;
|
class EditorVisualProfiler;
|
||||||
class EditorNetworkProfiler;
|
|
||||||
class EditorPerformanceProfiler;
|
class EditorPerformanceProfiler;
|
||||||
class SceneDebuggerTree;
|
class SceneDebuggerTree;
|
||||||
class EditorDebuggerPlugin;
|
class EditorDebuggerPlugin;
|
||||||
@ -72,7 +71,6 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum ProfilerType {
|
enum ProfilerType {
|
||||||
PROFILER_NETWORK,
|
|
||||||
PROFILER_VISUAL,
|
PROFILER_VISUAL,
|
||||||
PROFILER_SCRIPTS_SERVERS
|
PROFILER_SCRIPTS_SERVERS
|
||||||
};
|
};
|
||||||
@ -151,7 +149,6 @@ private:
|
|||||||
|
|
||||||
EditorProfiler *profiler = nullptr;
|
EditorProfiler *profiler = nullptr;
|
||||||
EditorVisualProfiler *visual_profiler = nullptr;
|
EditorVisualProfiler *visual_profiler = nullptr;
|
||||||
EditorNetworkProfiler *network_profiler = nullptr;
|
|
||||||
EditorPerformanceProfiler *performance_profiler = nullptr;
|
EditorPerformanceProfiler *performance_profiler = nullptr;
|
||||||
|
|
||||||
OS::ProcessID remote_pid = 0;
|
OS::ProcessID remote_pid = 0;
|
||||||
|
@ -59,7 +59,7 @@ void EditorNetworkProfiler::_update_frame() {
|
|||||||
|
|
||||||
TreeItem *root = counters_display->create_item();
|
TreeItem *root = counters_display->create_item();
|
||||||
|
|
||||||
for (const KeyValue<ObjectID, SceneDebugger::RPCNodeInfo> &E : nodes_data) {
|
for (const KeyValue<ObjectID, RPCNodeInfo> &E : nodes_data) {
|
||||||
TreeItem *node = counters_display->create_item(root);
|
TreeItem *node = counters_display->create_item(root);
|
||||||
|
|
||||||
for (int j = 0; j < counters_display->get_columns(); ++j) {
|
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)) {
|
if (!nodes_data.has(p_frame.node)) {
|
||||||
nodes_data.insert(p_frame.node, p_frame);
|
nodes_data.insert(p_frame.node, p_frame);
|
||||||
} else {
|
} else {
|
@ -38,10 +38,14 @@
|
|||||||
#include "scene/gui/split_container.h"
|
#include "scene/gui/split_container.h"
|
||||||
#include "scene/gui/tree.h"
|
#include "scene/gui/tree.h"
|
||||||
|
|
||||||
|
#include "../multiplayer_debugger.h"
|
||||||
|
|
||||||
class EditorNetworkProfiler : public VBoxContainer {
|
class EditorNetworkProfiler : public VBoxContainer {
|
||||||
GDCLASS(EditorNetworkProfiler, VBoxContainer)
|
GDCLASS(EditorNetworkProfiler, VBoxContainer)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
using RPCNodeInfo = MultiplayerDebugger::RPCNodeInfo;
|
||||||
|
|
||||||
Button *activate = nullptr;
|
Button *activate = nullptr;
|
||||||
Button *clear_button = nullptr;
|
Button *clear_button = nullptr;
|
||||||
Tree *counters_display = nullptr;
|
Tree *counters_display = nullptr;
|
||||||
@ -50,7 +54,7 @@ private:
|
|||||||
|
|
||||||
Timer *frame_delay = nullptr;
|
Timer *frame_delay = nullptr;
|
||||||
|
|
||||||
HashMap<ObjectID, SceneDebugger::RPCNodeInfo> nodes_data;
|
HashMap<ObjectID, RPCNodeInfo> nodes_data;
|
||||||
|
|
||||||
void _update_frame();
|
void _update_frame();
|
||||||
|
|
||||||
@ -62,7 +66,7 @@ protected:
|
|||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
public:
|
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);
|
void set_bandwidth(int p_incoming, int p_outgoing);
|
||||||
bool is_profiling();
|
bool is_profiling();
|
||||||
|
|
140
modules/multiplayer/editor/multiplayer_editor_plugin.cpp
Normal file
140
modules/multiplayer/editor/multiplayer_editor_plugin.cpp
Normal file
@ -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<EditorDebuggerSession> 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<EditorDebuggerSession> 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<MultiplayerSynchronizer>(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();
|
||||||
|
}
|
||||||
|
}
|
81
modules/multiplayer/editor/multiplayer_editor_plugin.h
Normal file
81
modules/multiplayer/editor/multiplayer_editor_plugin.h
Normal file
@ -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<int, EditorNetworkProfiler *> 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<MultiplayerEditorDebugger> 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
|
@ -1,5 +1,5 @@
|
|||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
/* replication_editor_plugin.cpp */
|
/* replication_editor.cpp */
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
/* This file is part of: */
|
/* This file is part of: */
|
||||||
/* GODOT ENGINE */
|
/* GODOT ENGINE */
|
||||||
@ -28,7 +28,9 @@
|
|||||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
/* 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_node.h"
|
||||||
#include "editor/editor_scale.h"
|
#include "editor/editor_scale.h"
|
||||||
@ -37,7 +39,6 @@
|
|||||||
#include "editor/inspector_dock.h"
|
#include "editor/inspector_dock.h"
|
||||||
#include "editor/property_selector.h"
|
#include "editor/property_selector.h"
|
||||||
#include "editor/scene_tree_editor.h"
|
#include "editor/scene_tree_editor.h"
|
||||||
#include "modules/multiplayer/multiplayer_synchronizer.h"
|
|
||||||
#include "scene/gui/dialogs.h"
|
#include "scene/gui/dialogs.h"
|
||||||
#include "scene/gui/separator.h"
|
#include "scene/gui/separator.h"
|
||||||
#include "scene/gui/tree.h"
|
#include "scene/gui/tree.h"
|
||||||
@ -141,7 +142,7 @@ void ReplicationEditor::_add_sync_property(String p_path) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo();
|
Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
|
||||||
undo_redo->create_action(TTR("Add property to synchronizer"));
|
undo_redo->create_action(TTR("Add property to synchronizer"));
|
||||||
|
|
||||||
if (config.is_null()) {
|
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_checked(2, p_sync);
|
||||||
item->set_editable(2, true);
|
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<MultiplayerSynchronizer>(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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
/* replication_editor_plugin.h */
|
/* replication_editor.h */
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
/* This file is part of: */
|
/* This file is part of: */
|
||||||
/* GODOT ENGINE */
|
/* GODOT ENGINE */
|
||||||
@ -28,8 +28,8 @@
|
|||||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
||||||
#ifndef REPLICATION_EDITOR_PLUGIN_H
|
#ifndef REPLICATION_EDITOR_H
|
||||||
#define REPLICATION_EDITOR_PLUGIN_H
|
#define REPLICATION_EDITOR_H
|
||||||
|
|
||||||
#include "editor/editor_plugin.h"
|
#include "editor/editor_plugin.h"
|
||||||
#include "modules/multiplayer/scene_replication_config.h"
|
#include "modules/multiplayer/scene_replication_config.h"
|
||||||
@ -105,27 +105,4 @@ public:
|
|||||||
~ReplicationEditor() {}
|
~ReplicationEditor() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ReplicationEditorPlugin : public EditorPlugin {
|
#endif // REPLICATION_EDITOR_H
|
||||||
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
|
|
194
modules/multiplayer/multiplayer_debugger.cpp
Normal file
194
modules/multiplayer/multiplayer_debugger.cpp
Normal file
@ -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<Ref<EngineProfiler>> multiplayer_profilers;
|
||||||
|
|
||||||
|
void MultiplayerDebugger::initialize() {
|
||||||
|
Ref<BandwidthProfiler> bandwidth;
|
||||||
|
bandwidth.instantiate();
|
||||||
|
bandwidth->bind("multiplayer");
|
||||||
|
multiplayer_profilers.push_back(bandwidth);
|
||||||
|
|
||||||
|
Ref<RPCProfiler> 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<BandwidthFrame> &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<Node>(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<ObjectID, RPCNodeInfo> &E : rpc_node_data) {
|
||||||
|
frame.infos.push_back(E.value);
|
||||||
|
}
|
||||||
|
rpc_node_data.clear();
|
||||||
|
EngineDebugger::get_singleton()->send_message("multiplayer:rpc", frame.serialize());
|
||||||
|
}
|
||||||
|
}
|
95
modules/multiplayer/multiplayer_debugger.h
Normal file
95
modules/multiplayer/multiplayer_debugger.h
Normal file
@ -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<RPCNodeInfo> 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<BandwidthFrame> bandwidth_in;
|
||||||
|
int bandwidth_out_ptr = 0;
|
||||||
|
Vector<BandwidthFrame> bandwidth_out;
|
||||||
|
uint64_t last_bandwidth_time = 0;
|
||||||
|
|
||||||
|
int bandwidth_usage(const Vector<BandwidthFrame> &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<ObjectID, RPCNodeInfo> 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
|
@ -36,9 +36,10 @@
|
|||||||
#include "scene_replication_interface.h"
|
#include "scene_replication_interface.h"
|
||||||
#include "scene_rpc_interface.h"
|
#include "scene_rpc_interface.h"
|
||||||
|
|
||||||
|
#include "multiplayer_debugger.h"
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
#include "editor/editor_plugin.h"
|
#include "editor/multiplayer_editor_plugin.h"
|
||||||
#include "editor/replication_editor_plugin.h"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void initialize_multiplayer_module(ModuleInitializationLevel p_level) {
|
void initialize_multiplayer_module(ModuleInitializationLevel p_level) {
|
||||||
@ -48,13 +49,15 @@ void initialize_multiplayer_module(ModuleInitializationLevel p_level) {
|
|||||||
GDREGISTER_CLASS(MultiplayerSynchronizer);
|
GDREGISTER_CLASS(MultiplayerSynchronizer);
|
||||||
GDREGISTER_CLASS(SceneMultiplayer);
|
GDREGISTER_CLASS(SceneMultiplayer);
|
||||||
MultiplayerAPI::set_default_interface("SceneMultiplayer");
|
MultiplayerAPI::set_default_interface("SceneMultiplayer");
|
||||||
|
MultiplayerDebugger::initialize();
|
||||||
}
|
}
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
|
if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
|
||||||
EditorPlugins::add_by_type<ReplicationEditorPlugin>();
|
EditorPlugins::add_by_type<MultiplayerEditorPlugin>();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void uninitialize_multiplayer_module(ModuleInitializationLevel p_level) {
|
void uninitialize_multiplayer_module(ModuleInitializationLevel p_level) {
|
||||||
|
MultiplayerDebugger::deinitialize();
|
||||||
}
|
}
|
||||||
|
@ -39,87 +39,10 @@
|
|||||||
#include "scene/main/window.h"
|
#include "scene/main/window.h"
|
||||||
#include "scene/resources/packed_scene.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<ObjectID, RPCNodeInfo> 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<Node>(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<ObjectID, RPCNodeInfo> &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 = nullptr;
|
||||||
|
|
||||||
SceneDebugger::SceneDebugger() {
|
SceneDebugger::SceneDebugger() {
|
||||||
singleton = this;
|
singleton = this;
|
||||||
rpc_profiler.instantiate();
|
|
||||||
rpc_profiler->bind("rpc");
|
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
LiveEditor::singleton = memnew(LiveEditor);
|
LiveEditor::singleton = memnew(LiveEditor);
|
||||||
EngineDebugger::register_message_capture("scene", EngineDebugger::Capture(nullptr, SceneDebugger::parse_message));
|
EngineDebugger::register_message_capture("scene", EngineDebugger::Capture(nullptr, SceneDebugger::parse_message));
|
||||||
|
@ -42,28 +42,9 @@ class Node;
|
|||||||
|
|
||||||
class SceneDebugger {
|
class SceneDebugger {
|
||||||
public:
|
public:
|
||||||
// RPC profiler
|
|
||||||
struct RPCNodeInfo {
|
|
||||||
ObjectID node;
|
|
||||||
String node_path;
|
|
||||||
int incoming_rpc = 0;
|
|
||||||
int outgoing_rpc = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RPCProfilerFrame {
|
|
||||||
Vector<RPCNodeInfo> infos;
|
|
||||||
|
|
||||||
Array serialize();
|
|
||||||
bool deserialize(const Array &p_arr);
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class RPCProfiler;
|
|
||||||
|
|
||||||
static SceneDebugger *singleton;
|
static SceneDebugger *singleton;
|
||||||
|
|
||||||
Ref<RPCProfiler> rpc_profiler;
|
|
||||||
|
|
||||||
SceneDebugger();
|
SceneDebugger();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
Loading…
Reference in New Issue
Block a user