Path: blob/master/modules/multiplayer/multiplayer_debugger.cpp
10277 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 "scene/main/node.h"3738List<Ref<EngineProfiler>> multiplayer_profilers;3940void MultiplayerDebugger::initialize() {41Ref<BandwidthProfiler> bandwidth;42bandwidth.instantiate();43bandwidth->bind("multiplayer:bandwidth");44multiplayer_profilers.push_back(bandwidth);4546Ref<RPCProfiler> rpc_profiler;47rpc_profiler.instantiate();48rpc_profiler->bind("multiplayer:rpc");49multiplayer_profilers.push_back(rpc_profiler);5051Ref<ReplicationProfiler> replication_profiler;52replication_profiler.instantiate();53replication_profiler->bind("multiplayer:replication");54multiplayer_profilers.push_back(replication_profiler);5556EngineDebugger::register_message_capture("multiplayer", EngineDebugger::Capture(nullptr, &_capture));57}5859void MultiplayerDebugger::deinitialize() {60multiplayer_profilers.clear();61}6263Error MultiplayerDebugger::_capture(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured) {64if (p_msg == "cache") {65Array out;66for (int i = 0; i < p_args.size(); i++) {67ObjectID id = p_args[i].operator ObjectID();68Object *obj = ObjectDB::get_instance(id);69ERR_CONTINUE(!obj);70if (Object::cast_to<SceneReplicationConfig>(obj)) {71out.push_back(id);72out.push_back(obj->get_class());73out.push_back(((SceneReplicationConfig *)obj)->get_path());74} else if (Object::cast_to<Node>(obj)) {75out.push_back(id);76out.push_back(obj->get_class());77out.push_back(String(((Node *)obj)->get_path()));78} else {79ERR_FAIL_V(FAILED);80}81}82EngineDebugger::get_singleton()->send_message("multiplayer:cache", out);83return OK;84}85ERR_FAIL_V(FAILED);86}8788// BandwidthProfiler8990int MultiplayerDebugger::BandwidthProfiler::bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) {91ERR_FAIL_COND_V(p_buffer.is_empty(), 0);92int total_bandwidth = 0;9394uint64_t timestamp = OS::get_singleton()->get_ticks_msec();95uint64_t final_timestamp = timestamp - 1000;9697int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size();9899while (i != p_pointer && p_buffer[i].packet_size > 0) {100if (p_buffer[i].timestamp < final_timestamp) {101return total_bandwidth;102}103total_bandwidth += p_buffer[i].packet_size;104i = (i + p_buffer.size() - 1) % p_buffer.size();105}106107ERR_FAIL_COND_V_MSG(i == p_pointer, total_bandwidth, "Reached the end of the bandwidth profiler buffer, values might be inaccurate.");108return total_bandwidth;109}110111void MultiplayerDebugger::BandwidthProfiler::toggle(bool p_enable, const Array &p_opts) {112if (!p_enable) {113bandwidth_in.clear();114bandwidth_out.clear();115} else {116bandwidth_in_ptr = 0;117bandwidth_in.resize(16384); // ~128kB118for (int i = 0; i < bandwidth_in.size(); ++i) {119bandwidth_in.write[i].packet_size = -1;120}121bandwidth_out_ptr = 0;122bandwidth_out.resize(16384); // ~128kB123for (int i = 0; i < bandwidth_out.size(); ++i) {124bandwidth_out.write[i].packet_size = -1;125}126}127}128129void MultiplayerDebugger::BandwidthProfiler::add(const Array &p_data) {130ERR_FAIL_COND(p_data.size() < 3);131const String inout = p_data[0];132int time = p_data[1];133int size = p_data[2];134if (inout == "in") {135bandwidth_in.write[bandwidth_in_ptr].timestamp = time;136bandwidth_in.write[bandwidth_in_ptr].packet_size = size;137bandwidth_in_ptr = (bandwidth_in_ptr + 1) % bandwidth_in.size();138} else if (inout == "out") {139bandwidth_out.write[bandwidth_out_ptr].timestamp = time;140bandwidth_out.write[bandwidth_out_ptr].packet_size = size;141bandwidth_out_ptr = (bandwidth_out_ptr + 1) % bandwidth_out.size();142}143}144145void MultiplayerDebugger::BandwidthProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {146uint64_t pt = OS::get_singleton()->get_ticks_msec();147if (pt - last_bandwidth_time > 200) {148last_bandwidth_time = pt;149int incoming_bandwidth = bandwidth_usage(bandwidth_in, bandwidth_in_ptr);150int outgoing_bandwidth = bandwidth_usage(bandwidth_out, bandwidth_out_ptr);151152Array arr = { incoming_bandwidth, outgoing_bandwidth };153EngineDebugger::get_singleton()->send_message("multiplayer:bandwidth", arr);154}155}156157// RPCProfiler158159Array MultiplayerDebugger::RPCFrame::serialize() {160Array arr = { infos.size() * 6 };161for (int i = 0; i < infos.size(); ++i) {162arr.push_back(uint64_t(infos[i].node));163arr.push_back(infos[i].node_path);164arr.push_back(infos[i].incoming_rpc);165arr.push_back(infos[i].incoming_size);166arr.push_back(infos[i].outgoing_rpc);167arr.push_back(infos[i].outgoing_size);168}169return arr;170}171172bool MultiplayerDebugger::RPCFrame::deserialize(const Array &p_arr) {173ERR_FAIL_COND_V(p_arr.is_empty(), false);174uint32_t size = p_arr[0];175ERR_FAIL_COND_V(size % 6, false);176ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false);177infos.resize(size / 6);178int idx = 1;179for (uint32_t i = 0; i < size / 6; i++) {180infos.write[i].node = uint64_t(p_arr[idx]);181infos.write[i].node_path = p_arr[idx + 1];182infos.write[i].incoming_rpc = p_arr[idx + 2];183infos.write[i].incoming_size = p_arr[idx + 3];184infos.write[i].outgoing_rpc = p_arr[idx + 4];185infos.write[i].outgoing_size = p_arr[idx + 5];186idx += 6;187}188return true;189}190191void MultiplayerDebugger::RPCProfiler::init_node(const ObjectID p_node) {192if (rpc_node_data.has(p_node)) {193return;194}195rpc_node_data.insert(p_node, RPCNodeInfo());196rpc_node_data[p_node].node = p_node;197rpc_node_data[p_node].node_path = String(ObjectDB::get_instance<Node>(p_node)->get_path());198}199200void MultiplayerDebugger::RPCProfiler::toggle(bool p_enable, const Array &p_opts) {201rpc_node_data.clear();202}203204void MultiplayerDebugger::RPCProfiler::add(const Array &p_data) {205ERR_FAIL_COND(p_data.size() != 3);206const String what = p_data[0];207const ObjectID id = p_data[1];208const int size = p_data[2];209init_node(id);210RPCNodeInfo &info = rpc_node_data[id];211if (what == "rpc_in") {212info.incoming_rpc++;213info.incoming_size += size;214} else if (what == "rpc_out") {215info.outgoing_rpc++;216info.outgoing_size += size;217}218}219220void MultiplayerDebugger::RPCProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {221uint64_t pt = OS::get_singleton()->get_ticks_msec();222if (pt - last_profile_time > 100) {223last_profile_time = pt;224RPCFrame frame;225for (const KeyValue<ObjectID, RPCNodeInfo> &E : rpc_node_data) {226frame.infos.push_back(E.value);227}228rpc_node_data.clear();229EngineDebugger::get_singleton()->send_message("multiplayer:rpc", frame.serialize());230}231}232233// ReplicationProfiler234235MultiplayerDebugger::SyncInfo::SyncInfo(MultiplayerSynchronizer *p_sync) {236ERR_FAIL_NULL(p_sync);237synchronizer = p_sync->get_instance_id();238if (p_sync->get_replication_config_ptr()) {239config = p_sync->get_replication_config_ptr()->get_instance_id();240}241if (p_sync->get_root_node()) {242root_node = p_sync->get_root_node()->get_instance_id();243}244}245246void MultiplayerDebugger::SyncInfo::write_to_array(Array &r_arr) const {247r_arr.push_back(synchronizer);248r_arr.push_back(config);249r_arr.push_back(root_node);250r_arr.push_back(incoming_syncs);251r_arr.push_back(incoming_size);252r_arr.push_back(outgoing_syncs);253r_arr.push_back(outgoing_size);254}255256bool MultiplayerDebugger::SyncInfo::read_from_array(const Array &p_arr, int p_offset) {257ERR_FAIL_COND_V(p_arr.size() - p_offset < 7, false);258synchronizer = int64_t(p_arr[p_offset]);259config = int64_t(p_arr[p_offset + 1]);260root_node = int64_t(p_arr[p_offset + 2]);261incoming_syncs = p_arr[p_offset + 3];262incoming_size = p_arr[p_offset + 4];263outgoing_syncs = p_arr[p_offset + 5];264outgoing_size = p_arr[p_offset + 6];265return true;266}267268Array MultiplayerDebugger::ReplicationFrame::serialize() {269Array arr = { infos.size() * 7 };270for (const KeyValue<ObjectID, SyncInfo> &E : infos) {271E.value.write_to_array(arr);272}273return arr;274}275276bool MultiplayerDebugger::ReplicationFrame::deserialize(const Array &p_arr) {277ERR_FAIL_COND_V(p_arr.is_empty(), false);278uint32_t size = p_arr[0];279ERR_FAIL_COND_V(size % 7, false);280ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false);281int idx = 1;282for (uint32_t i = 0; i < size / 7; i++) {283SyncInfo info;284if (!info.read_from_array(p_arr, idx)) {285return false;286}287infos[info.synchronizer] = info;288idx += 7;289}290return true;291}292293void MultiplayerDebugger::ReplicationProfiler::toggle(bool p_enable, const Array &p_opts) {294sync_data.clear();295}296297void MultiplayerDebugger::ReplicationProfiler::add(const Array &p_data) {298ERR_FAIL_COND(p_data.size() != 3);299const String what = p_data[0];300const ObjectID id = p_data[1];301const uint64_t size = p_data[2];302MultiplayerSynchronizer *sync = ObjectDB::get_instance<MultiplayerSynchronizer>(id);303ERR_FAIL_NULL(sync);304if (!sync_data.has(id)) {305sync_data[id] = SyncInfo(sync);306}307SyncInfo &info = sync_data[id];308if (what == "sync_in") {309info.incoming_syncs++;310info.incoming_size += size;311} else if (what == "sync_out") {312info.outgoing_syncs++;313info.outgoing_size += size;314}315}316317void MultiplayerDebugger::ReplicationProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {318uint64_t pt = OS::get_singleton()->get_ticks_msec();319if (pt - last_profile_time > 100) {320last_profile_time = pt;321ReplicationFrame frame;322for (const KeyValue<ObjectID, SyncInfo> &E : sync_data) {323frame.infos[E.key] = E.value;324}325sync_data.clear();326EngineDebugger::get_singleton()->send_message("multiplayer:syncs", frame.serialize());327}328}329330331