multiplayer_debugger.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /**************************************************************************/
  2. /* multiplayer_debugger.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  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 "multiplayer_synchronizer.h"
  32. #include "scene_replication_config.h"
  33. #include "core/debugger/engine_debugger.h"
  34. #include "scene/main/node.h"
  35. List<Ref<EngineProfiler>> multiplayer_profilers;
  36. void MultiplayerDebugger::initialize() {
  37. Ref<BandwidthProfiler> bandwidth;
  38. bandwidth.instantiate();
  39. bandwidth->bind("multiplayer:bandwidth");
  40. multiplayer_profilers.push_back(bandwidth);
  41. Ref<RPCProfiler> rpc_profiler;
  42. rpc_profiler.instantiate();
  43. rpc_profiler->bind("multiplayer:rpc");
  44. multiplayer_profilers.push_back(rpc_profiler);
  45. Ref<ReplicationProfiler> replication_profiler;
  46. replication_profiler.instantiate();
  47. replication_profiler->bind("multiplayer:replication");
  48. multiplayer_profilers.push_back(replication_profiler);
  49. EngineDebugger::register_message_capture("multiplayer", EngineDebugger::Capture(nullptr, &_capture));
  50. }
  51. void MultiplayerDebugger::deinitialize() {
  52. multiplayer_profilers.clear();
  53. }
  54. Error MultiplayerDebugger::_capture(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured) {
  55. if (p_msg == "cache") {
  56. Array out;
  57. for (int i = 0; i < p_args.size(); i++) {
  58. ObjectID id = p_args[i].operator ObjectID();
  59. Object *obj = ObjectDB::get_instance(id);
  60. ERR_CONTINUE(!obj);
  61. if (Object::cast_to<SceneReplicationConfig>(obj)) {
  62. out.push_back(id);
  63. out.push_back(obj->get_class());
  64. out.push_back(((SceneReplicationConfig *)obj)->get_path());
  65. } else if (Object::cast_to<Node>(obj)) {
  66. out.push_back(id);
  67. out.push_back(obj->get_class());
  68. out.push_back(String(((Node *)obj)->get_path()));
  69. } else {
  70. ERR_FAIL_V(FAILED);
  71. }
  72. }
  73. EngineDebugger::get_singleton()->send_message("multiplayer:cache", out);
  74. return OK;
  75. }
  76. ERR_FAIL_V(FAILED);
  77. }
  78. // BandwidthProfiler
  79. int MultiplayerDebugger::BandwidthProfiler::bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) {
  80. ERR_FAIL_COND_V(p_buffer.is_empty(), 0);
  81. int total_bandwidth = 0;
  82. uint64_t timestamp = OS::get_singleton()->get_ticks_msec();
  83. uint64_t final_timestamp = timestamp - 1000;
  84. int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size();
  85. while (i != p_pointer && p_buffer[i].packet_size > 0) {
  86. if (p_buffer[i].timestamp < final_timestamp) {
  87. return total_bandwidth;
  88. }
  89. total_bandwidth += p_buffer[i].packet_size;
  90. i = (i + p_buffer.size() - 1) % p_buffer.size();
  91. }
  92. ERR_FAIL_COND_V_MSG(i == p_pointer, total_bandwidth, "Reached the end of the bandwidth profiler buffer, values might be inaccurate.");
  93. return total_bandwidth;
  94. }
  95. void MultiplayerDebugger::BandwidthProfiler::toggle(bool p_enable, const Array &p_opts) {
  96. if (!p_enable) {
  97. bandwidth_in.clear();
  98. bandwidth_out.clear();
  99. } else {
  100. bandwidth_in_ptr = 0;
  101. bandwidth_in.resize(16384); // ~128kB
  102. for (int i = 0; i < bandwidth_in.size(); ++i) {
  103. bandwidth_in.write[i].packet_size = -1;
  104. }
  105. bandwidth_out_ptr = 0;
  106. bandwidth_out.resize(16384); // ~128kB
  107. for (int i = 0; i < bandwidth_out.size(); ++i) {
  108. bandwidth_out.write[i].packet_size = -1;
  109. }
  110. }
  111. }
  112. void MultiplayerDebugger::BandwidthProfiler::add(const Array &p_data) {
  113. ERR_FAIL_COND(p_data.size() < 3);
  114. const String inout = p_data[0];
  115. int time = p_data[1];
  116. int size = p_data[2];
  117. if (inout == "in") {
  118. bandwidth_in.write[bandwidth_in_ptr].timestamp = time;
  119. bandwidth_in.write[bandwidth_in_ptr].packet_size = size;
  120. bandwidth_in_ptr = (bandwidth_in_ptr + 1) % bandwidth_in.size();
  121. } else if (inout == "out") {
  122. bandwidth_out.write[bandwidth_out_ptr].timestamp = time;
  123. bandwidth_out.write[bandwidth_out_ptr].packet_size = size;
  124. bandwidth_out_ptr = (bandwidth_out_ptr + 1) % bandwidth_out.size();
  125. }
  126. }
  127. void MultiplayerDebugger::BandwidthProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {
  128. uint64_t pt = OS::get_singleton()->get_ticks_msec();
  129. if (pt - last_bandwidth_time > 200) {
  130. last_bandwidth_time = pt;
  131. int incoming_bandwidth = bandwidth_usage(bandwidth_in, bandwidth_in_ptr);
  132. int outgoing_bandwidth = bandwidth_usage(bandwidth_out, bandwidth_out_ptr);
  133. Array arr;
  134. arr.push_back(incoming_bandwidth);
  135. arr.push_back(outgoing_bandwidth);
  136. EngineDebugger::get_singleton()->send_message("multiplayer:bandwidth", arr);
  137. }
  138. }
  139. // RPCProfiler
  140. Array MultiplayerDebugger::RPCFrame::serialize() {
  141. Array arr;
  142. arr.push_back(infos.size() * 6);
  143. for (int i = 0; i < infos.size(); ++i) {
  144. arr.push_back(uint64_t(infos[i].node));
  145. arr.push_back(infos[i].node_path);
  146. arr.push_back(infos[i].incoming_rpc);
  147. arr.push_back(infos[i].incoming_size);
  148. arr.push_back(infos[i].outgoing_rpc);
  149. arr.push_back(infos[i].outgoing_size);
  150. }
  151. return arr;
  152. }
  153. bool MultiplayerDebugger::RPCFrame::deserialize(const Array &p_arr) {
  154. ERR_FAIL_COND_V(p_arr.is_empty(), false);
  155. uint32_t size = p_arr[0];
  156. ERR_FAIL_COND_V(size % 6, false);
  157. ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false);
  158. infos.resize(size / 6);
  159. int idx = 1;
  160. for (uint32_t i = 0; i < size / 6; i++) {
  161. infos.write[i].node = uint64_t(p_arr[idx]);
  162. infos.write[i].node_path = p_arr[idx + 1];
  163. infos.write[i].incoming_rpc = p_arr[idx + 2];
  164. infos.write[i].incoming_size = p_arr[idx + 3];
  165. infos.write[i].outgoing_rpc = p_arr[idx + 4];
  166. infos.write[i].outgoing_size = p_arr[idx + 5];
  167. idx += 6;
  168. }
  169. return true;
  170. }
  171. void MultiplayerDebugger::RPCProfiler::init_node(const ObjectID p_node) {
  172. if (rpc_node_data.has(p_node)) {
  173. return;
  174. }
  175. rpc_node_data.insert(p_node, RPCNodeInfo());
  176. rpc_node_data[p_node].node = p_node;
  177. rpc_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
  178. }
  179. void MultiplayerDebugger::RPCProfiler::toggle(bool p_enable, const Array &p_opts) {
  180. rpc_node_data.clear();
  181. }
  182. void MultiplayerDebugger::RPCProfiler::add(const Array &p_data) {
  183. ERR_FAIL_COND(p_data.size() != 3);
  184. const String what = p_data[0];
  185. const ObjectID id = p_data[1];
  186. const int size = p_data[2];
  187. init_node(id);
  188. RPCNodeInfo &info = rpc_node_data[id];
  189. if (what == "rpc_in") {
  190. info.incoming_rpc++;
  191. info.incoming_size += size;
  192. } else if (what == "rpc_out") {
  193. info.outgoing_rpc++;
  194. info.outgoing_size += size;
  195. }
  196. }
  197. void MultiplayerDebugger::RPCProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {
  198. uint64_t pt = OS::get_singleton()->get_ticks_msec();
  199. if (pt - last_profile_time > 100) {
  200. last_profile_time = pt;
  201. RPCFrame frame;
  202. for (const KeyValue<ObjectID, RPCNodeInfo> &E : rpc_node_data) {
  203. frame.infos.push_back(E.value);
  204. }
  205. rpc_node_data.clear();
  206. EngineDebugger::get_singleton()->send_message("multiplayer:rpc", frame.serialize());
  207. }
  208. }
  209. // ReplicationProfiler
  210. MultiplayerDebugger::SyncInfo::SyncInfo(MultiplayerSynchronizer *p_sync) {
  211. ERR_FAIL_NULL(p_sync);
  212. synchronizer = p_sync->get_instance_id();
  213. if (p_sync->get_replication_config_ptr()) {
  214. config = p_sync->get_replication_config_ptr()->get_instance_id();
  215. }
  216. if (p_sync->get_root_node()) {
  217. root_node = p_sync->get_root_node()->get_instance_id();
  218. }
  219. }
  220. void MultiplayerDebugger::SyncInfo::write_to_array(Array &r_arr) const {
  221. r_arr.push_back(synchronizer);
  222. r_arr.push_back(config);
  223. r_arr.push_back(root_node);
  224. r_arr.push_back(incoming_syncs);
  225. r_arr.push_back(incoming_size);
  226. r_arr.push_back(outgoing_syncs);
  227. r_arr.push_back(outgoing_size);
  228. }
  229. bool MultiplayerDebugger::SyncInfo::read_from_array(const Array &p_arr, int p_offset) {
  230. ERR_FAIL_COND_V(p_arr.size() - p_offset < 7, false);
  231. synchronizer = int64_t(p_arr[p_offset]);
  232. config = int64_t(p_arr[p_offset + 1]);
  233. root_node = int64_t(p_arr[p_offset + 2]);
  234. incoming_syncs = p_arr[p_offset + 3];
  235. incoming_size = p_arr[p_offset + 4];
  236. outgoing_syncs = p_arr[p_offset + 5];
  237. outgoing_size = p_arr[p_offset + 6];
  238. return true;
  239. }
  240. Array MultiplayerDebugger::ReplicationFrame::serialize() {
  241. Array arr;
  242. arr.push_back(infos.size() * 7);
  243. for (const KeyValue<ObjectID, SyncInfo> &E : infos) {
  244. E.value.write_to_array(arr);
  245. }
  246. return arr;
  247. }
  248. bool MultiplayerDebugger::ReplicationFrame::deserialize(const Array &p_arr) {
  249. ERR_FAIL_COND_V(p_arr.is_empty(), false);
  250. uint32_t size = p_arr[0];
  251. ERR_FAIL_COND_V(size % 7, false);
  252. ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false);
  253. int idx = 1;
  254. for (uint32_t i = 0; i < size / 7; i++) {
  255. SyncInfo info;
  256. if (!info.read_from_array(p_arr, idx)) {
  257. return false;
  258. }
  259. infos[info.synchronizer] = info;
  260. idx += 7;
  261. }
  262. return true;
  263. }
  264. void MultiplayerDebugger::ReplicationProfiler::toggle(bool p_enable, const Array &p_opts) {
  265. sync_data.clear();
  266. }
  267. void MultiplayerDebugger::ReplicationProfiler::add(const Array &p_data) {
  268. ERR_FAIL_COND(p_data.size() != 3);
  269. const String what = p_data[0];
  270. const ObjectID id = p_data[1];
  271. const uint64_t size = p_data[2];
  272. MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(ObjectDB::get_instance(id));
  273. ERR_FAIL_NULL(sync);
  274. if (!sync_data.has(id)) {
  275. sync_data[id] = SyncInfo(sync);
  276. }
  277. SyncInfo &info = sync_data[id];
  278. if (what == "sync_in") {
  279. info.incoming_syncs++;
  280. info.incoming_size += size;
  281. } else if (what == "sync_out") {
  282. info.outgoing_syncs++;
  283. info.outgoing_size += size;
  284. }
  285. }
  286. void MultiplayerDebugger::ReplicationProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {
  287. uint64_t pt = OS::get_singleton()->get_ticks_msec();
  288. if (pt - last_profile_time > 100) {
  289. last_profile_time = pt;
  290. ReplicationFrame frame;
  291. for (const KeyValue<ObjectID, SyncInfo> &E : sync_data) {
  292. frame.infos[E.key] = E.value;
  293. }
  294. sync_data.clear();
  295. EngineDebugger::get_singleton()->send_message("multiplayer:syncs", frame.serialize());
  296. }
  297. }