Path: blob/master/modules/multiplayer/multiplayer_debugger.cpp
20843 views
/**************************************************************************/1/* multiplayer_debugger.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "multiplayer_debugger.h"3132#include "multiplayer_synchronizer.h"33#include "scene_replication_config.h"3435#include "core/debugger/engine_debugger.h"36#include "core/os/os.h"37#include "scene/main/node.h"3839List<Ref<EngineProfiler>> multiplayer_profilers;4041void MultiplayerDebugger::initialize() {42Ref<BandwidthProfiler> bandwidth;43bandwidth.instantiate();44bandwidth->bind("multiplayer:bandwidth");45multiplayer_profilers.push_back(bandwidth);4647Ref<RPCProfiler> rpc_profiler;48rpc_profiler.instantiate();49rpc_profiler->bind("multiplayer:rpc");50multiplayer_profilers.push_back(rpc_profiler);5152Ref<ReplicationProfiler> replication_profiler;53replication_profiler.instantiate();54replication_profiler->bind("multiplayer:replication");55multiplayer_profilers.push_back(replication_profiler);5657EngineDebugger::register_message_capture("multiplayer", EngineDebugger::Capture(nullptr, &_capture));58}5960void MultiplayerDebugger::deinitialize() {61multiplayer_profilers.clear();62}6364Error MultiplayerDebugger::_capture(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured) {65if (p_msg == "cache") {66Array out;67for (int i = 0; i < p_args.size(); i++) {68ObjectID id = p_args[i].operator ObjectID();69Object *obj = ObjectDB::get_instance(id);70ERR_CONTINUE(!obj);71if (Object::cast_to<SceneReplicationConfig>(obj)) {72out.push_back(id);73out.push_back(obj->get_class());74out.push_back(((SceneReplicationConfig *)obj)->get_path());75} else if (Object::cast_to<Node>(obj)) {76out.push_back(id);77out.push_back(obj->get_class());78out.push_back(String(((Node *)obj)->get_path()));79} else {80ERR_FAIL_V(FAILED);81}82}83EngineDebugger::get_singleton()->send_message("multiplayer:cache", out);84return OK;85}86ERR_FAIL_V(FAILED);87}8889// BandwidthProfiler9091int MultiplayerDebugger::BandwidthProfiler::bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) {92ERR_FAIL_COND_V(p_buffer.is_empty(), 0);93int total_bandwidth = 0;9495uint64_t timestamp = OS::get_singleton()->get_ticks_msec();96uint64_t final_timestamp = timestamp - 1000;9798int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size();99100while (i != p_pointer && p_buffer[i].packet_size > 0) {101if (p_buffer[i].timestamp < final_timestamp) {102return total_bandwidth;103}104total_bandwidth += p_buffer[i].packet_size;105i = (i + p_buffer.size() - 1) % p_buffer.size();106}107108ERR_FAIL_COND_V_MSG(i == p_pointer, total_bandwidth, "Reached the end of the bandwidth profiler buffer, values might be inaccurate.");109return total_bandwidth;110}111112void MultiplayerDebugger::BandwidthProfiler::toggle(bool p_enable, const Array &p_opts) {113if (!p_enable) {114bandwidth_in.clear();115bandwidth_out.clear();116} else {117bandwidth_in_ptr = 0;118bandwidth_in.resize(16384); // ~128kB119for (int i = 0; i < bandwidth_in.size(); ++i) {120bandwidth_in.write[i].packet_size = -1;121}122bandwidth_out_ptr = 0;123bandwidth_out.resize(16384); // ~128kB124for (int i = 0; i < bandwidth_out.size(); ++i) {125bandwidth_out.write[i].packet_size = -1;126}127}128}129130void MultiplayerDebugger::BandwidthProfiler::add(const Array &p_data) {131ERR_FAIL_COND(p_data.size() < 3);132const String inout = p_data[0];133int time = p_data[1];134int size = p_data[2];135if (inout == "in") {136bandwidth_in.write[bandwidth_in_ptr].timestamp = time;137bandwidth_in.write[bandwidth_in_ptr].packet_size = size;138bandwidth_in_ptr = (bandwidth_in_ptr + 1) % bandwidth_in.size();139} else if (inout == "out") {140bandwidth_out.write[bandwidth_out_ptr].timestamp = time;141bandwidth_out.write[bandwidth_out_ptr].packet_size = size;142bandwidth_out_ptr = (bandwidth_out_ptr + 1) % bandwidth_out.size();143}144}145146void MultiplayerDebugger::BandwidthProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {147uint64_t pt = OS::get_singleton()->get_ticks_msec();148if (pt - last_bandwidth_time > 200) {149last_bandwidth_time = pt;150int incoming_bandwidth = bandwidth_usage(bandwidth_in, bandwidth_in_ptr);151int outgoing_bandwidth = bandwidth_usage(bandwidth_out, bandwidth_out_ptr);152153Array arr = { incoming_bandwidth, outgoing_bandwidth };154EngineDebugger::get_singleton()->send_message("multiplayer:bandwidth", arr);155}156}157158// RPCProfiler159160Array MultiplayerDebugger::RPCFrame::serialize() {161Array arr = { infos.size() * 6 };162for (int i = 0; i < infos.size(); ++i) {163arr.push_back(uint64_t(infos[i].node));164arr.push_back(infos[i].node_path);165arr.push_back(infos[i].incoming_rpc);166arr.push_back(infos[i].incoming_size);167arr.push_back(infos[i].outgoing_rpc);168arr.push_back(infos[i].outgoing_size);169}170return arr;171}172173bool MultiplayerDebugger::RPCFrame::deserialize(const Array &p_arr) {174ERR_FAIL_COND_V(p_arr.is_empty(), false);175uint32_t size = p_arr[0];176ERR_FAIL_COND_V(size % 6, false);177ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false);178infos.resize(size / 6);179int idx = 1;180for (uint32_t i = 0; i < size / 6; i++) {181infos.write[i].node = uint64_t(p_arr[idx]);182infos.write[i].node_path = p_arr[idx + 1];183infos.write[i].incoming_rpc = p_arr[idx + 2];184infos.write[i].incoming_size = p_arr[idx + 3];185infos.write[i].outgoing_rpc = p_arr[idx + 4];186infos.write[i].outgoing_size = p_arr[idx + 5];187idx += 6;188}189return true;190}191192void MultiplayerDebugger::RPCProfiler::init_node(const ObjectID p_node) {193if (rpc_node_data.has(p_node)) {194return;195}196rpc_node_data.insert(p_node, RPCNodeInfo());197rpc_node_data[p_node].node = p_node;198rpc_node_data[p_node].node_path = String(ObjectDB::get_instance<Node>(p_node)->get_path());199}200201void MultiplayerDebugger::RPCProfiler::toggle(bool p_enable, const Array &p_opts) {202rpc_node_data.clear();203}204205void MultiplayerDebugger::RPCProfiler::add(const Array &p_data) {206ERR_FAIL_COND(p_data.size() != 3);207const String what = p_data[0];208const ObjectID id = p_data[1];209const int size = p_data[2];210init_node(id);211RPCNodeInfo &info = rpc_node_data[id];212if (what == "rpc_in") {213info.incoming_rpc++;214info.incoming_size += size;215} else if (what == "rpc_out") {216info.outgoing_rpc++;217info.outgoing_size += size;218}219}220221void MultiplayerDebugger::RPCProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {222uint64_t pt = OS::get_singleton()->get_ticks_msec();223if (pt - last_profile_time > 100) {224last_profile_time = pt;225RPCFrame frame;226for (const KeyValue<ObjectID, RPCNodeInfo> &E : rpc_node_data) {227frame.infos.push_back(E.value);228}229rpc_node_data.clear();230EngineDebugger::get_singleton()->send_message("multiplayer:rpc", frame.serialize());231}232}233234// ReplicationProfiler235236MultiplayerDebugger::SyncInfo::SyncInfo(MultiplayerSynchronizer *p_sync) {237ERR_FAIL_NULL(p_sync);238synchronizer = p_sync->get_instance_id();239if (p_sync->get_replication_config_ptr()) {240config = p_sync->get_replication_config_ptr()->get_instance_id();241}242if (p_sync->get_root_node()) {243root_node = p_sync->get_root_node()->get_instance_id();244}245}246247void MultiplayerDebugger::SyncInfo::write_to_array(Array &r_arr) const {248r_arr.push_back(synchronizer);249r_arr.push_back(config);250r_arr.push_back(root_node);251r_arr.push_back(incoming_syncs);252r_arr.push_back(incoming_size);253r_arr.push_back(outgoing_syncs);254r_arr.push_back(outgoing_size);255}256257bool MultiplayerDebugger::SyncInfo::read_from_array(const Array &p_arr, int p_offset) {258ERR_FAIL_COND_V(p_arr.size() - p_offset < 7, false);259synchronizer = int64_t(p_arr[p_offset]);260config = int64_t(p_arr[p_offset + 1]);261root_node = int64_t(p_arr[p_offset + 2]);262incoming_syncs = p_arr[p_offset + 3];263incoming_size = p_arr[p_offset + 4];264outgoing_syncs = p_arr[p_offset + 5];265outgoing_size = p_arr[p_offset + 6];266return true;267}268269Array MultiplayerDebugger::ReplicationFrame::serialize() {270Array arr = { infos.size() * 7 };271for (const KeyValue<ObjectID, SyncInfo> &E : infos) {272E.value.write_to_array(arr);273}274return arr;275}276277bool MultiplayerDebugger::ReplicationFrame::deserialize(const Array &p_arr) {278ERR_FAIL_COND_V(p_arr.is_empty(), false);279uint32_t size = p_arr[0];280ERR_FAIL_COND_V(size % 7, false);281ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false);282int idx = 1;283for (uint32_t i = 0; i < size / 7; i++) {284SyncInfo info;285if (!info.read_from_array(p_arr, idx)) {286return false;287}288infos[info.synchronizer] = info;289idx += 7;290}291return true;292}293294void MultiplayerDebugger::ReplicationProfiler::toggle(bool p_enable, const Array &p_opts) {295sync_data.clear();296}297298void MultiplayerDebugger::ReplicationProfiler::add(const Array &p_data) {299ERR_FAIL_COND(p_data.size() != 3);300const String what = p_data[0];301const ObjectID id = p_data[1];302const uint64_t size = p_data[2];303MultiplayerSynchronizer *sync = ObjectDB::get_instance<MultiplayerSynchronizer>(id);304ERR_FAIL_NULL(sync);305if (!sync_data.has(id)) {306sync_data[id] = SyncInfo(sync);307}308SyncInfo &info = sync_data[id];309if (what == "sync_in") {310info.incoming_syncs++;311info.incoming_size += size;312} else if (what == "sync_out") {313info.outgoing_syncs++;314info.outgoing_size += size;315}316}317318void MultiplayerDebugger::ReplicationProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {319uint64_t pt = OS::get_singleton()->get_ticks_msec();320if (pt - last_profile_time > 100) {321last_profile_time = pt;322ReplicationFrame frame;323for (const KeyValue<ObjectID, SyncInfo> &E : sync_data) {324frame.infos[E.key] = E.value;325}326sync_data.clear();327EngineDebugger::get_singleton()->send_message("multiplayer:syncs", frame.serialize());328}329}330331332