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