multiplayer_debugger.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /*************************************************************************/
  2. /* multiplayer_debugger.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "multiplayer_debugger.h"
  31. #include "core/debugger/engine_debugger.h"
  32. #include "scene/main/node.h"
  33. List<Ref<EngineProfiler>> multiplayer_profilers;
  34. void MultiplayerDebugger::initialize() {
  35. Ref<BandwidthProfiler> bandwidth;
  36. bandwidth.instantiate();
  37. bandwidth->bind("multiplayer");
  38. multiplayer_profilers.push_back(bandwidth);
  39. Ref<RPCProfiler> rpc_profiler;
  40. rpc_profiler.instantiate();
  41. rpc_profiler->bind("rpc");
  42. multiplayer_profilers.push_back(rpc_profiler);
  43. }
  44. void MultiplayerDebugger::deinitialize() {
  45. multiplayer_profilers.clear();
  46. }
  47. // BandwidthProfiler
  48. int MultiplayerDebugger::BandwidthProfiler::bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) {
  49. ERR_FAIL_COND_V(p_buffer.size() == 0, 0);
  50. int total_bandwidth = 0;
  51. uint64_t timestamp = OS::get_singleton()->get_ticks_msec();
  52. uint64_t final_timestamp = timestamp - 1000;
  53. int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size();
  54. while (i != p_pointer && p_buffer[i].packet_size > 0) {
  55. if (p_buffer[i].timestamp < final_timestamp) {
  56. return total_bandwidth;
  57. }
  58. total_bandwidth += p_buffer[i].packet_size;
  59. i = (i + p_buffer.size() - 1) % p_buffer.size();
  60. }
  61. ERR_FAIL_COND_V_MSG(i == p_pointer, total_bandwidth, "Reached the end of the bandwidth profiler buffer, values might be inaccurate.");
  62. return total_bandwidth;
  63. }
  64. void MultiplayerDebugger::BandwidthProfiler::toggle(bool p_enable, const Array &p_opts) {
  65. if (!p_enable) {
  66. bandwidth_in.clear();
  67. bandwidth_out.clear();
  68. } else {
  69. bandwidth_in_ptr = 0;
  70. bandwidth_in.resize(16384); // ~128kB
  71. for (int i = 0; i < bandwidth_in.size(); ++i) {
  72. bandwidth_in.write[i].packet_size = -1;
  73. }
  74. bandwidth_out_ptr = 0;
  75. bandwidth_out.resize(16384); // ~128kB
  76. for (int i = 0; i < bandwidth_out.size(); ++i) {
  77. bandwidth_out.write[i].packet_size = -1;
  78. }
  79. }
  80. }
  81. void MultiplayerDebugger::BandwidthProfiler::add(const Array &p_data) {
  82. ERR_FAIL_COND(p_data.size() < 3);
  83. const String inout = p_data[0];
  84. int time = p_data[1];
  85. int size = p_data[2];
  86. if (inout == "in") {
  87. bandwidth_in.write[bandwidth_in_ptr].timestamp = time;
  88. bandwidth_in.write[bandwidth_in_ptr].packet_size = size;
  89. bandwidth_in_ptr = (bandwidth_in_ptr + 1) % bandwidth_in.size();
  90. } else if (inout == "out") {
  91. bandwidth_out.write[bandwidth_out_ptr].timestamp = time;
  92. bandwidth_out.write[bandwidth_out_ptr].packet_size = size;
  93. bandwidth_out_ptr = (bandwidth_out_ptr + 1) % bandwidth_out.size();
  94. }
  95. }
  96. void MultiplayerDebugger::BandwidthProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {
  97. uint64_t pt = OS::get_singleton()->get_ticks_msec();
  98. if (pt - last_bandwidth_time > 200) {
  99. last_bandwidth_time = pt;
  100. int incoming_bandwidth = bandwidth_usage(bandwidth_in, bandwidth_in_ptr);
  101. int outgoing_bandwidth = bandwidth_usage(bandwidth_out, bandwidth_out_ptr);
  102. Array arr;
  103. arr.push_back(incoming_bandwidth);
  104. arr.push_back(outgoing_bandwidth);
  105. EngineDebugger::get_singleton()->send_message("multiplayer:bandwidth", arr);
  106. }
  107. }
  108. // RPCProfiler
  109. Array MultiplayerDebugger::RPCFrame::serialize() {
  110. Array arr;
  111. arr.push_back(infos.size() * 6);
  112. for (int i = 0; i < infos.size(); ++i) {
  113. arr.push_back(uint64_t(infos[i].node));
  114. arr.push_back(infos[i].node_path);
  115. arr.push_back(infos[i].incoming_rpc);
  116. arr.push_back(infos[i].incoming_size);
  117. arr.push_back(infos[i].outgoing_rpc);
  118. arr.push_back(infos[i].outgoing_size);
  119. }
  120. return arr;
  121. }
  122. bool MultiplayerDebugger::RPCFrame::deserialize(const Array &p_arr) {
  123. ERR_FAIL_COND_V(p_arr.size() < 1, false);
  124. uint32_t size = p_arr[0];
  125. ERR_FAIL_COND_V(size % 6, false);
  126. ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false);
  127. infos.resize(size / 6);
  128. int idx = 1;
  129. for (uint32_t i = 0; i < size / 6; i++) {
  130. infos.write[i].node = uint64_t(p_arr[idx]);
  131. infos.write[i].node_path = p_arr[idx + 1];
  132. infos.write[i].incoming_rpc = p_arr[idx + 2];
  133. infos.write[i].incoming_size = p_arr[idx + 3];
  134. infos.write[i].outgoing_rpc = p_arr[idx + 4];
  135. infos.write[i].outgoing_size = p_arr[idx + 5];
  136. idx += 6;
  137. }
  138. return true;
  139. }
  140. void MultiplayerDebugger::RPCProfiler::init_node(const ObjectID p_node) {
  141. if (rpc_node_data.has(p_node)) {
  142. return;
  143. }
  144. rpc_node_data.insert(p_node, RPCNodeInfo());
  145. rpc_node_data[p_node].node = p_node;
  146. rpc_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
  147. }
  148. void MultiplayerDebugger::RPCProfiler::toggle(bool p_enable, const Array &p_opts) {
  149. rpc_node_data.clear();
  150. }
  151. void MultiplayerDebugger::RPCProfiler::add(const Array &p_data) {
  152. ERR_FAIL_COND(p_data.size() != 3);
  153. const String what = p_data[0];
  154. const ObjectID id = p_data[1];
  155. const int size = p_data[2];
  156. init_node(id);
  157. RPCNodeInfo &info = rpc_node_data[id];
  158. if (what == "rpc_in") {
  159. info.incoming_rpc++;
  160. info.incoming_size += size;
  161. } else if (what == "rpc_out") {
  162. info.outgoing_rpc++;
  163. info.outgoing_size += size;
  164. }
  165. }
  166. void MultiplayerDebugger::RPCProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {
  167. uint64_t pt = OS::get_singleton()->get_ticks_msec();
  168. if (pt - last_profile_time > 100) {
  169. last_profile_time = pt;
  170. RPCFrame frame;
  171. for (const KeyValue<ObjectID, RPCNodeInfo> &E : rpc_node_data) {
  172. frame.infos.push_back(E.value);
  173. }
  174. rpc_node_data.clear();
  175. EngineDebugger::get_singleton()->send_message("multiplayer:rpc", frame.serialize());
  176. }
  177. }