Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/multiplayer/multiplayer_synchronizer.cpp
10277 views
1
/**************************************************************************/
2
/* multiplayer_synchronizer.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_synchronizer.h"
32
33
#include "core/config/engine.h"
34
#include "scene/main/multiplayer_api.h"
35
36
Object *MultiplayerSynchronizer::_get_prop_target(Object *p_obj, const NodePath &p_path) {
37
if (p_path.get_name_count() == 0) {
38
return p_obj;
39
}
40
Node *node = Object::cast_to<Node>(p_obj);
41
ERR_FAIL_COND_V_MSG(!node || !node->has_node(p_path), nullptr, vformat("Node '%s' not found.", p_path));
42
return node->get_node(p_path);
43
}
44
45
void MultiplayerSynchronizer::_stop() {
46
#ifdef TOOLS_ENABLED
47
if (Engine::get_singleton()->is_editor_hint()) {
48
return;
49
}
50
#endif
51
root_node_cache = ObjectID();
52
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
53
if (node) {
54
get_multiplayer()->object_configuration_remove(node, this);
55
}
56
reset();
57
}
58
59
void MultiplayerSynchronizer::_start() {
60
#ifdef TOOLS_ENABLED
61
if (Engine::get_singleton()->is_editor_hint()) {
62
return;
63
}
64
#endif
65
root_node_cache = ObjectID();
66
reset();
67
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
68
if (node) {
69
root_node_cache = node->get_instance_id();
70
get_multiplayer()->object_configuration_add(node, this);
71
_update_process();
72
}
73
}
74
75
void MultiplayerSynchronizer::_update_process() {
76
#ifdef TOOLS_ENABLED
77
if (Engine::get_singleton()->is_editor_hint()) {
78
return;
79
}
80
#endif
81
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
82
if (!node) {
83
return;
84
}
85
set_process_internal(false);
86
set_physics_process_internal(false);
87
if (!visibility_filters.size()) {
88
return;
89
}
90
switch (visibility_update_mode) {
91
case VISIBILITY_PROCESS_IDLE:
92
set_process_internal(true);
93
break;
94
case VISIBILITY_PROCESS_PHYSICS:
95
set_physics_process_internal(true);
96
break;
97
case VISIBILITY_PROCESS_NONE:
98
break;
99
}
100
}
101
102
Node *MultiplayerSynchronizer::get_root_node() {
103
return root_node_cache.is_valid() ? ObjectDB::get_instance<Node>(root_node_cache) : nullptr;
104
}
105
106
void MultiplayerSynchronizer::reset() {
107
net_id = 0;
108
last_sync_usec = 0;
109
last_inbound_sync = 0;
110
last_watch_usec = 0;
111
sync_started = false;
112
watchers.clear();
113
}
114
115
uint32_t MultiplayerSynchronizer::get_net_id() const {
116
return net_id;
117
}
118
119
void MultiplayerSynchronizer::set_net_id(uint32_t p_net_id) {
120
net_id = p_net_id;
121
}
122
123
bool MultiplayerSynchronizer::update_outbound_sync_time(uint64_t p_usec) {
124
if (last_sync_usec == p_usec) {
125
// last_sync_usec has been updated in this frame.
126
return true;
127
}
128
if (p_usec < last_sync_usec + sync_interval_usec) {
129
// Too soon, should skip this synchronization frame.
130
return false;
131
}
132
last_sync_usec = p_usec;
133
return true;
134
}
135
136
bool MultiplayerSynchronizer::update_inbound_sync_time(uint16_t p_network_time) {
137
if (!sync_started) {
138
sync_started = true;
139
} else if (p_network_time <= last_inbound_sync && last_inbound_sync - p_network_time < 32767) {
140
return false;
141
}
142
last_inbound_sync = p_network_time;
143
return true;
144
}
145
146
PackedStringArray MultiplayerSynchronizer::get_configuration_warnings() const {
147
PackedStringArray warnings = Node::get_configuration_warnings();
148
149
if (root_path.is_empty() || !has_node(root_path)) {
150
warnings.push_back(RTR("A valid NodePath must be set in the \"Root Path\" property in order for MultiplayerSynchronizer to be able to synchronize properties."));
151
}
152
153
return warnings;
154
}
155
156
Error MultiplayerSynchronizer::get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs) {
157
ERR_FAIL_NULL_V(p_obj, ERR_INVALID_PARAMETER);
158
r_variant.resize(p_properties.size());
159
r_variant_ptrs.resize(r_variant.size());
160
int i = 0;
161
for (const NodePath &prop : p_properties) {
162
bool valid = false;
163
const Object *obj = _get_prop_target(p_obj, prop);
164
ERR_FAIL_NULL_V(obj, FAILED);
165
r_variant.write[i] = obj->get_indexed(prop.get_subnames(), &valid);
166
r_variant_ptrs.write[i] = &r_variant[i];
167
ERR_FAIL_COND_V_MSG(!valid, ERR_INVALID_DATA, vformat("Property '%s' not found.", prop));
168
i++;
169
}
170
return OK;
171
}
172
173
Error MultiplayerSynchronizer::set_state(const List<NodePath> &p_properties, Object *p_obj, const Vector<Variant> &p_state) {
174
ERR_FAIL_NULL_V(p_obj, ERR_INVALID_PARAMETER);
175
int i = 0;
176
for (const NodePath &prop : p_properties) {
177
Object *obj = _get_prop_target(p_obj, prop);
178
ERR_FAIL_NULL_V(obj, FAILED);
179
obj->set_indexed(prop.get_subnames(), p_state[i]);
180
i += 1;
181
}
182
return OK;
183
}
184
185
bool MultiplayerSynchronizer::is_visibility_public() const {
186
return peer_visibility.has(0);
187
}
188
189
void MultiplayerSynchronizer::set_visibility_public(bool p_visible) {
190
set_visibility_for(0, p_visible);
191
}
192
193
bool MultiplayerSynchronizer::is_visible_to(int p_peer) {
194
if (visibility_filters.size()) {
195
Variant arg = p_peer;
196
const Variant *argv[1] = { &arg };
197
for (Callable filter : visibility_filters) {
198
Variant ret;
199
Callable::CallError err;
200
filter.callp(argv, 1, ret, err);
201
ERR_FAIL_COND_V(err.error != Callable::CallError::CALL_OK || ret.get_type() != Variant::BOOL, false);
202
if (!ret.operator bool()) {
203
return false;
204
}
205
}
206
}
207
return peer_visibility.has(0) || peer_visibility.has(p_peer);
208
}
209
210
void MultiplayerSynchronizer::add_visibility_filter(Callable p_callback) {
211
visibility_filters.insert(p_callback);
212
_update_process();
213
}
214
215
void MultiplayerSynchronizer::remove_visibility_filter(Callable p_callback) {
216
visibility_filters.erase(p_callback);
217
_update_process();
218
}
219
220
void MultiplayerSynchronizer::set_visibility_for(int p_peer, bool p_visible) {
221
if (peer_visibility.has(p_peer) == p_visible) {
222
return;
223
}
224
if (p_visible) {
225
peer_visibility.insert(p_peer);
226
} else {
227
peer_visibility.erase(p_peer);
228
}
229
update_visibility(p_peer);
230
}
231
232
bool MultiplayerSynchronizer::get_visibility_for(int p_peer) const {
233
return peer_visibility.has(p_peer);
234
}
235
236
void MultiplayerSynchronizer::set_visibility_update_mode(VisibilityUpdateMode p_mode) {
237
visibility_update_mode = p_mode;
238
_update_process();
239
}
240
241
MultiplayerSynchronizer::VisibilityUpdateMode MultiplayerSynchronizer::get_visibility_update_mode() const {
242
return visibility_update_mode;
243
}
244
245
void MultiplayerSynchronizer::_bind_methods() {
246
ClassDB::bind_method(D_METHOD("set_root_path", "path"), &MultiplayerSynchronizer::set_root_path);
247
ClassDB::bind_method(D_METHOD("get_root_path"), &MultiplayerSynchronizer::get_root_path);
248
249
ClassDB::bind_method(D_METHOD("set_replication_interval", "milliseconds"), &MultiplayerSynchronizer::set_replication_interval);
250
ClassDB::bind_method(D_METHOD("get_replication_interval"), &MultiplayerSynchronizer::get_replication_interval);
251
252
ClassDB::bind_method(D_METHOD("set_delta_interval", "milliseconds"), &MultiplayerSynchronizer::set_delta_interval);
253
ClassDB::bind_method(D_METHOD("get_delta_interval"), &MultiplayerSynchronizer::get_delta_interval);
254
255
ClassDB::bind_method(D_METHOD("set_replication_config", "config"), &MultiplayerSynchronizer::set_replication_config);
256
ClassDB::bind_method(D_METHOD("get_replication_config"), &MultiplayerSynchronizer::get_replication_config);
257
258
ClassDB::bind_method(D_METHOD("set_visibility_update_mode", "mode"), &MultiplayerSynchronizer::set_visibility_update_mode);
259
ClassDB::bind_method(D_METHOD("get_visibility_update_mode"), &MultiplayerSynchronizer::get_visibility_update_mode);
260
ClassDB::bind_method(D_METHOD("update_visibility", "for_peer"), &MultiplayerSynchronizer::update_visibility, DEFVAL(0));
261
262
ClassDB::bind_method(D_METHOD("set_visibility_public", "visible"), &MultiplayerSynchronizer::set_visibility_public);
263
ClassDB::bind_method(D_METHOD("is_visibility_public"), &MultiplayerSynchronizer::is_visibility_public);
264
265
ClassDB::bind_method(D_METHOD("add_visibility_filter", "filter"), &MultiplayerSynchronizer::add_visibility_filter);
266
ClassDB::bind_method(D_METHOD("remove_visibility_filter", "filter"), &MultiplayerSynchronizer::remove_visibility_filter);
267
ClassDB::bind_method(D_METHOD("set_visibility_for", "peer", "visible"), &MultiplayerSynchronizer::set_visibility_for);
268
ClassDB::bind_method(D_METHOD("get_visibility_for", "peer"), &MultiplayerSynchronizer::get_visibility_for);
269
270
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path");
271
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "replication_interval", PROPERTY_HINT_RANGE, "0,5,0.001,suffix:s"), "set_replication_interval", "get_replication_interval");
272
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "delta_interval", PROPERTY_HINT_RANGE, "0,5,0.001,suffix:s"), "set_delta_interval", "get_delta_interval");
273
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replication_config", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_replication_config", "get_replication_config");
274
ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,None"), "set_visibility_update_mode", "get_visibility_update_mode");
275
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "public_visibility"), "set_visibility_public", "is_visibility_public");
276
277
BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_IDLE);
278
BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_PHYSICS);
279
BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_NONE);
280
281
ADD_SIGNAL(MethodInfo("synchronized"));
282
ADD_SIGNAL(MethodInfo("delta_synchronized"));
283
ADD_SIGNAL(MethodInfo("visibility_changed", PropertyInfo(Variant::INT, "for_peer")));
284
}
285
286
void MultiplayerSynchronizer::_notification(int p_what) {
287
#ifdef TOOLS_ENABLED
288
if (Engine::get_singleton()->is_editor_hint()) {
289
return;
290
}
291
#endif
292
if (root_path.is_empty()) {
293
return;
294
}
295
296
switch (p_what) {
297
case NOTIFICATION_ENTER_TREE: {
298
_start();
299
} break;
300
301
case NOTIFICATION_EXIT_TREE: {
302
_stop();
303
} break;
304
305
case NOTIFICATION_INTERNAL_PROCESS:
306
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
307
update_visibility(0);
308
} break;
309
}
310
}
311
312
void MultiplayerSynchronizer::set_replication_interval(double p_interval) {
313
ERR_FAIL_COND_MSG(p_interval < 0, "Interval must be greater or equal to 0 (where 0 means default)");
314
sync_interval_usec = uint64_t(p_interval * 1000 * 1000);
315
}
316
317
double MultiplayerSynchronizer::get_replication_interval() const {
318
return double(sync_interval_usec) / 1000.0 / 1000.0;
319
}
320
321
void MultiplayerSynchronizer::set_delta_interval(double p_interval) {
322
ERR_FAIL_COND_MSG(p_interval < 0, "Interval must be greater or equal to 0 (where 0 means default)");
323
delta_interval_usec = uint64_t(p_interval * 1000 * 1000);
324
}
325
326
double MultiplayerSynchronizer::get_delta_interval() const {
327
return double(delta_interval_usec) / 1000.0 / 1000.0;
328
}
329
330
void MultiplayerSynchronizer::set_replication_config(Ref<SceneReplicationConfig> p_config) {
331
replication_config = p_config;
332
}
333
334
Ref<SceneReplicationConfig> MultiplayerSynchronizer::get_replication_config() {
335
return replication_config;
336
}
337
338
void MultiplayerSynchronizer::update_visibility(int p_for_peer) {
339
#ifdef TOOLS_ENABLED
340
if (Engine::get_singleton()->is_editor_hint()) {
341
return;
342
}
343
#endif
344
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
345
if (node && get_multiplayer()->has_multiplayer_peer() && is_multiplayer_authority()) {
346
emit_signal(SceneStringName(visibility_changed), p_for_peer);
347
}
348
}
349
350
void MultiplayerSynchronizer::set_root_path(const NodePath &p_path) {
351
if (p_path == root_path) {
352
return;
353
}
354
_stop();
355
root_path = p_path;
356
_start();
357
update_configuration_warnings();
358
}
359
360
NodePath MultiplayerSynchronizer::get_root_path() const {
361
return root_path;
362
}
363
364
void MultiplayerSynchronizer::set_multiplayer_authority(int p_peer_id, bool p_recursive) {
365
if (get_multiplayer_authority() == p_peer_id) {
366
return;
367
}
368
_stop();
369
Node::set_multiplayer_authority(p_peer_id, p_recursive);
370
_start();
371
}
372
373
Error MultiplayerSynchronizer::_watch_changes(uint64_t p_usec) {
374
ERR_FAIL_COND_V(replication_config.is_null(), FAILED);
375
const List<NodePath> props = replication_config->get_watch_properties();
376
if (props.size() != watchers.size()) {
377
watchers.resize(props.size());
378
}
379
if (props.is_empty()) {
380
return OK;
381
}
382
Node *node = get_root_node();
383
ERR_FAIL_NULL_V(node, FAILED);
384
int idx = -1;
385
Watcher *ptr = watchers.ptrw();
386
for (const NodePath &prop : props) {
387
idx++;
388
bool valid = false;
389
const Object *obj = _get_prop_target(node, prop);
390
ERR_CONTINUE_MSG(!obj, vformat("Node not found for property '%s'.", prop));
391
Variant v = obj->get_indexed(prop.get_subnames(), &valid);
392
ERR_CONTINUE_MSG(!valid, vformat("Property '%s' not found.", prop));
393
Watcher &w = ptr[idx];
394
if (w.prop != prop) {
395
w.prop = prop;
396
w.value = v.duplicate(true);
397
w.last_change_usec = p_usec;
398
} else if (!w.value.hash_compare(v)) {
399
w.value = v.duplicate(true);
400
w.last_change_usec = p_usec;
401
}
402
}
403
return OK;
404
}
405
406
List<Variant> MultiplayerSynchronizer::get_delta_state(uint64_t p_cur_usec, uint64_t p_last_usec, uint64_t &r_indexes) {
407
r_indexes = 0;
408
List<Variant> out;
409
410
if (last_watch_usec == p_cur_usec) {
411
// We already watched for changes in this frame.
412
413
} else if (p_cur_usec < p_last_usec + delta_interval_usec) {
414
// Too soon skip delta synchronization.
415
return out;
416
417
} else {
418
// Watch for changes.
419
Error err = _watch_changes(p_cur_usec);
420
ERR_FAIL_COND_V(err != OK, out);
421
last_watch_usec = p_cur_usec;
422
}
423
424
const Watcher *ptr = watchers.size() ? watchers.ptr() : nullptr;
425
for (int i = 0; i < watchers.size(); i++) {
426
const Watcher &w = ptr[i];
427
if (w.last_change_usec <= p_last_usec) {
428
continue;
429
}
430
out.push_back(w.value);
431
r_indexes |= 1ULL << i;
432
}
433
return out;
434
}
435
436
List<NodePath> MultiplayerSynchronizer::get_delta_properties(uint64_t p_indexes) {
437
List<NodePath> out;
438
ERR_FAIL_COND_V(replication_config.is_null(), out);
439
const List<NodePath> watch_props = replication_config->get_watch_properties();
440
int idx = 0;
441
for (const NodePath &prop : watch_props) {
442
if ((p_indexes & (1ULL << idx++)) == 0) {
443
continue;
444
}
445
out.push_back(prop);
446
}
447
return out;
448
}
449
450
SceneReplicationConfig *MultiplayerSynchronizer::get_replication_config_ptr() const {
451
return replication_config.ptr();
452
}
453
454
MultiplayerSynchronizer::MultiplayerSynchronizer() {
455
// Publicly visible by default.
456
peer_visibility.insert(0);
457
}
458
459